From e0a01ba1743cd147ee29ad11944f8204768dd5c1 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Tue, 9 Apr 2019 04:47:00 +0200 Subject: [PATCH] css: Redo for new parser This commit is still way too big, but I couldn't make it smaller. It transitions the old CSS parser to the new parser. CSS parsing is now tokenized, everything else is probably still buggy. --- gtk/css/gtkcssparser.c | 44 +- gtk/css/gtkcssparserprivate.h | 2 + gtk/gtkcsscalcvalue.c | 58 +- gtk/gtkcsscolorvalue.c | 306 +++----- gtk/gtkcssdimensionvalue.c | 113 +++ gtk/gtkcssfontfeaturesvalue.c | 1 - gtk/gtkcssfontvariationsvalue.c | 1 - gtk/gtkcssimage.c | 3 + gtk/gtkcsskeyframes.c | 58 +- gtk/gtkcssnumbervalue.c | 9 +- gtk/gtkcssparser.c | 1244 +------------------------------ gtk/gtkcssparserprivate.h | 92 +-- gtk/gtkcssprovider.c | 257 +++---- gtk/gtkcsssection.c | 33 +- gtk/gtkcssselector.c | 930 +++++++++++++++-------- gtk/gtkcssshadowvalue.c | 3 - 16 files changed, 1073 insertions(+), 2081 deletions(-) diff --git a/gtk/css/gtkcssparser.c b/gtk/css/gtkcssparser.c index e0bb5f8a8a..6f63393931 100644 --- a/gtk/css/gtkcssparser.c +++ b/gtk/css/gtkcssparser.c @@ -352,21 +352,13 @@ gtk_css_parser_end_block (GtkCssParser *self) if (gtk_css_token_is (&self->token, GTK_CSS_TOKEN_EOF)) { - gtk_css_parser_warn (self, - GTK_CSS_PARSER_WARNING_SYNTAX, - gtk_css_parser_get_block_location (self), - gtk_css_parser_get_start_location (self), - "Unterminated block at end of document"); + gtk_css_parser_warn_syntax (self, "Unterminated block at end of document"); g_array_set_size (self->blocks, self->blocks->len - 1); } else if (gtk_css_token_is (&self->token, block->inherited_end_token)) { g_assert (block->end_token == GTK_CSS_TOKEN_SEMICOLON); - gtk_css_parser_warn (self, - GTK_CSS_PARSER_WARNING_SYNTAX, - gtk_css_parser_get_block_location (self), - gtk_css_parser_get_start_location (self), - "Expected ';' at end of block"); + gtk_css_parser_warn_syntax (self, "Expected ';' at end of block"); g_array_set_size (self->blocks, self->blocks->len - 1); } else @@ -434,15 +426,13 @@ gtk_css_parser_skip_until (GtkCssParser *self, } void -gtk_css_parser_emit_error (GtkCssParser *self, - const GError *error) +gtk_css_parser_emit_error (GtkCssParser *self, + const GtkCssLocation *start, + const GtkCssLocation *end, + const GError *error) { if (self->error_func) - self->error_func (self, - &self->location, - &self->location, - error, - self->user_data); + self->error_func (self, start, end, error, self->user_data); } void @@ -457,7 +447,10 @@ gtk_css_parser_error_syntax (GtkCssParser *self, error = g_error_new_valist (GTK_CSS_PARSER_ERROR, GTK_CSS_PARSER_ERROR_SYNTAX, format, args); - gtk_css_parser_emit_error (self, error); + gtk_css_parser_emit_error (self, + &self->location, + gtk_css_tokenizer_get_location (self->tokenizer), + error); g_error_free (error); va_end (args); } @@ -474,7 +467,10 @@ gtk_css_parser_error_value (GtkCssParser *self, error = g_error_new_valist (GTK_CSS_PARSER_ERROR, GTK_CSS_PARSER_ERROR_UNKNOWN_VALUE, format, args); - gtk_css_parser_emit_error (self, error); + gtk_css_parser_emit_error (self, + &self->location, + gtk_css_tokenizer_get_location (self->tokenizer), + error); g_error_free (error); va_end (args); } @@ -491,7 +487,10 @@ gtk_css_parser_warn_syntax (GtkCssParser *self, error = g_error_new_valist (GTK_CSS_PARSER_WARNING, GTK_CSS_PARSER_WARNING_SYNTAX, format, args); - gtk_css_parser_emit_error (self, error); + gtk_css_parser_emit_error (self, + &self->location, + gtk_css_tokenizer_get_location (self->tokenizer), + error); g_error_free (error); va_end (args); } @@ -864,7 +863,10 @@ gtk_css_parser_consume_url (GtkCssParser *self) GError *error = g_error_new (GTK_CSS_PARSER_ERROR, GTK_CSS_PARSER_ERROR_IMPORT, "Could not resolve \"%s\" to a valid URL", url); - gtk_css_parser_emit_error (self, error); + gtk_css_parser_emit_error (self, + &self->location, + gtk_css_tokenizer_get_location (self->tokenizer), + error); g_free (url); g_error_free (error); return NULL; diff --git a/gtk/css/gtkcssparserprivate.h b/gtk/css/gtkcssparserprivate.h index 9ae77e2e7e..f39d0025a3 100644 --- a/gtk/css/gtkcssparserprivate.h +++ b/gtk/css/gtkcssparserprivate.h @@ -82,6 +82,8 @@ void gtk_css_parser_skip_until (GtkCssParser GtkCssTokenType token_type); void gtk_css_parser_emit_error (GtkCssParser *self, + const GtkCssLocation *start, + const GtkCssLocation *end, const GError *error); void gtk_css_parser_error_syntax (GtkCssParser *self, const char *format, diff --git a/gtk/gtkcsscalcvalue.c b/gtk/gtkcsscalcvalue.c index 129dd93c52..aed0326fb6 100644 --- a/gtk/gtkcsscalcvalue.c +++ b/gtk/gtkcsscalcvalue.c @@ -322,19 +322,24 @@ gtk_css_calc_value_parse_value (GtkCssParser *parser, return NULL; } - if (_gtk_css_parser_try (parser, "(", TRUE)) + if (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_OPEN_PARENS)) { - GtkCssValue *result = gtk_css_calc_value_parse_sum (parser, flags); - if (result == NULL) - return NULL; + GtkCssValue *result; - if (!_gtk_css_parser_try (parser, ")", TRUE)) + gtk_css_parser_start_block (parser); + + result = gtk_css_calc_value_parse_sum (parser, flags); + if (result == NULL) { - _gtk_css_parser_error (parser, "Missing closing ')' in calc() subterm"); - _gtk_css_value_unref (result); + gtk_css_parser_end_block (parser); return NULL; } + if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF)) + _gtk_css_parser_error (parser, "Expected closing ')' in calc() subterm"); + + gtk_css_parser_end_block (parser); + return result; } @@ -454,32 +459,45 @@ fail: return NULL; } +typedef struct +{ + GtkCssNumberParseFlags flags; + GtkCssValue *value; +} ParseCalcData; + +static guint +gtk_css_calc_value_parse_arg (GtkCssParser *parser, + guint arg, + gpointer data_) +{ + ParseCalcData *data = data_; + + data->value = gtk_css_calc_value_parse_sum (parser, data->flags); + if (data->value == NULL) + return 0; + + return 1; +} + GtkCssValue * gtk_css_calc_value_parse (GtkCssParser *parser, GtkCssNumberParseFlags flags) { - GtkCssValue *value; + ParseCalcData data; /* This can only be handled at compute time, we allow '-' after all */ - flags &= ~GTK_CSS_POSITIVE_ONLY; + data.flags = flags & ~GTK_CSS_POSITIVE_ONLY; + data.value = NULL; - if (!_gtk_css_parser_try (parser, "calc(", TRUE)) + if (!gtk_css_parser_has_function (parser, "calc")) { _gtk_css_parser_error (parser, "Expected 'calc('"); return NULL; } - value = gtk_css_calc_value_parse_sum (parser, flags); - if (value == NULL) + if (!gtk_css_parser_consume_function (parser, 1, 1, gtk_css_calc_value_parse_arg, &data)) return NULL; - if (!_gtk_css_parser_try (parser, ")", TRUE)) - { - _gtk_css_value_unref (value); - _gtk_css_parser_error (parser, "Expected ')' after calc() statement"); - return NULL; - } - - return value; + return data.value; } diff --git a/gtk/gtkcsscolorvalue.c b/gtk/gtkcsscolorvalue.c index 0735d1e9f4..40b3159666 100644 --- a/gtk/gtkcsscolorvalue.c +++ b/gtk/gtkcsscolorvalue.c @@ -22,9 +22,10 @@ #include "gtkcssrgbavalueprivate.h" #include "gtkcssstylepropertyprivate.h" #include "gtkhslaprivate.h" +#include "gtkprivate.h" #include "gtkstylepropertyprivate.h" -#include "gtkprivate.h" +#include "gdk/gdkrgbaprivate.h" typedef enum { COLOR_TYPE_LITERAL, @@ -510,227 +511,144 @@ _gtk_css_color_value_new_current_color (void) return _gtk_css_value_ref (¤t_color); } -typedef enum { - COLOR_RGBA, - COLOR_RGB, - COLOR_LIGHTER, - COLOR_DARKER, - COLOR_SHADE, - COLOR_ALPHA, - COLOR_MIX -} ColorParseType; - -static GtkCssValue * -_gtk_css_color_value_parse_function (GtkCssParser *parser, - ColorParseType color) +typedef struct { - GtkCssValue *value; - GtkCssValue *child1, *child2; - double d; + GtkCssValue *color; + GtkCssValue *color2; + double value; +} ColorFunctionData; - if (!_gtk_css_parser_try (parser, "(", TRUE)) - { - _gtk_css_parser_error (parser, "Missing opening bracket in color definition"); - return NULL; - } +static guint +parse_color_mix (GtkCssParser *parser, + guint arg, + gpointer data_) +{ + ColorFunctionData *data = data_; - if (color == COLOR_RGB || color == COLOR_RGBA) - { - GdkRGBA rgba; - double tmp; - guint i; + switch (arg) + { + case 0: + data->color = _gtk_css_color_value_parse (parser); + if (data->color == NULL) + return 0; + return 1; - 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; - } + case 1: + data->color2 = _gtk_css_color_value_parse (parser); + if (data->color2 == NULL) + return 0; + return 1; - if (!gtk_css_parser_consume_number (parser, &tmp)) - return NULL; + case 2: + if (!gtk_css_parser_consume_number (parser, &data->value)) + return 0; + return 1; - 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 (); - } + default: + g_return_val_if_reached (0); + } +} - if (color == COLOR_RGBA) - { - if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE)) - { - _gtk_css_parser_error (parser, "Expected ',' in color definition"); - return NULL; - } +static guint +parse_color_number (GtkCssParser *parser, + guint arg, + gpointer data_) +{ + ColorFunctionData *data = data_; - if (!gtk_css_parser_consume_number (parser, &rgba.alpha)) - return NULL; - } - else - rgba.alpha = 1.0; - - value = _gtk_css_color_value_new_literal (&rgba); - } - else - { - child1 = _gtk_css_color_value_parse (parser); - if (child1 == NULL) - return NULL; + switch (arg) + { + case 0: + data->color = _gtk_css_color_value_parse (parser); + if (data->color == NULL) + return 0; + return 1; - if (color == COLOR_MIX) - { - if (!_gtk_css_parser_try (parser, ",", TRUE)) - { - _gtk_css_parser_error (parser, "Expected ',' in color definition"); - _gtk_css_value_unref (child1); - return NULL; - } + case 1: + if (!gtk_css_parser_consume_number (parser, &data->value)) + return 0; + return 1; - child2 = _gtk_css_color_value_parse (parser); - if (child2 == NULL) - { - _gtk_css_value_unref (child1); - return NULL; - } - } - else - child2 = NULL; - - if (color == COLOR_LIGHTER) - d = 1.3; - else if (color == COLOR_DARKER) - d = 0.7; - else - { - if (!_gtk_css_parser_try (parser, ",", TRUE)) - { - _gtk_css_parser_error (parser, "Expected ',' in color definition"); - _gtk_css_value_unref (child1); - if (child2) - _gtk_css_value_unref (child2); - return NULL; - } - - if (!gtk_css_parser_consume_number (parser, &d)) - { - _gtk_css_value_unref (child1); - if (child2) - _gtk_css_value_unref (child2); - return NULL; - } - } - - switch (color) - { - case COLOR_LIGHTER: - case COLOR_DARKER: - case COLOR_SHADE: - value = _gtk_css_color_value_new_shade (child1, d); - break; - case COLOR_ALPHA: - value = _gtk_css_color_value_new_alpha (child1, d); - break; - case COLOR_MIX: - value = _gtk_css_color_value_new_mix (child1, child2, d); - break; - case COLOR_RGB: - case COLOR_RGBA: - default: - g_assert_not_reached (); - value = NULL; - } - - _gtk_css_value_unref (child1); - if (child2) - _gtk_css_value_unref (child2); - } - - if (!_gtk_css_parser_try (parser, ")", TRUE)) - { - _gtk_css_parser_error (parser, "Expected ')' in color definition"); - _gtk_css_value_unref (value); - return NULL; - } - - return value; + default: + g_return_val_if_reached (0); + } } GtkCssValue * _gtk_css_color_value_parse (GtkCssParser *parser) { + ColorFunctionData data = { NULL, }; GtkCssValue *value; GdkRGBA rgba; - guint color; - const char *names[] = {"rgba", "rgb", "lighter", "darker", "shade", "alpha", "mix"}; - char *name; if (gtk_css_parser_try_ident (parser, "currentColor")) - return _gtk_css_color_value_new_current_color (); - - if (gtk_css_parser_try_ident (parser, "transparent")) { - GdkRGBA transparent = { 0, 0, 0, 0 }; - - return _gtk_css_color_value_new_literal (&transparent); + return _gtk_css_color_value_new_current_color (); } - - if (_gtk_css_parser_try (parser, "@", FALSE)) + else if (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_AT_KEYWORD)) { - name = _gtk_css_parser_try_name (parser, TRUE); + const GtkCssToken *token = gtk_css_parser_get_token (parser); - if (name) - { - value = _gtk_css_color_value_new_name (name); - } + value = _gtk_css_color_value_new_name (token->string.string); + gtk_css_parser_consume_token (parser); + + return value; + } + else if (gtk_css_parser_has_function (parser, "lighter")) + { + if (gtk_css_parser_consume_function (parser, 1, 1, parse_color_number, &data)) + value = _gtk_css_color_value_new_shade (data.color, 1.3); else - { - _gtk_css_parser_error (parser, "'%s' is not a valid color color name", name); - value = NULL; - } + value = NULL; - g_free (name); + g_clear_pointer (&data.color, gtk_css_value_unref); + return value; + } + else if (gtk_css_parser_has_function (parser, "darker")) + { + if (gtk_css_parser_consume_function (parser, 1, 1, parse_color_number, &data)) + value = _gtk_css_color_value_new_shade (data.color, 0.7); + else + value = NULL; + + g_clear_pointer (&data.color, gtk_css_value_unref); + return value; + } + else if (gtk_css_parser_has_function (parser, "shade")) + { + if (gtk_css_parser_consume_function (parser, 2, 2, parse_color_number, &data)) + value = _gtk_css_color_value_new_shade (data.color, data.value); + else + value = NULL; + + g_clear_pointer (&data.color, gtk_css_value_unref); + return value; + } + else if (gtk_css_parser_has_function (parser, "alpha")) + { + if (gtk_css_parser_consume_function (parser, 2, 2, parse_color_number, &data)) + value = _gtk_css_color_value_new_alpha (data.color, data.value); + else + value = NULL; + + g_clear_pointer (&data.color, gtk_css_value_unref); + return value; + } + else if (gtk_css_parser_has_function (parser, "mix")) + { + if (gtk_css_parser_consume_function (parser, 3, 3, parse_color_mix, &data)) + value = _gtk_css_color_value_new_mix (data.color, data.color2, data.value); + else + value = NULL; + + g_clear_pointer (&data.color, gtk_css_value_unref); + g_clear_pointer (&data.color2, gtk_css_value_unref); return value; } - 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_color_value_parse_function (parser, color); - - if (_gtk_css_parser_try_hash_color (parser, &rgba)) + if (gdk_rgba_parser_parse (parser, &rgba)) return _gtk_css_color_value_new_literal (&rgba); - - name = _gtk_css_parser_try_name (parser, TRUE); - if (name) - { - if (gdk_rgba_parse (&rgba, name)) - { - value = _gtk_css_color_value_new_literal (&rgba); - } - else - { - _gtk_css_parser_error (parser, "'%s' is not a valid color name", name); - value = NULL; - } - g_free (name); - return value; - } - - _gtk_css_parser_error (parser, "Not a color definition"); - return NULL; + else + return NULL; } diff --git a/gtk/gtkcssdimensionvalue.c b/gtk/gtkcssdimensionvalue.c index c9c378e0cf..feaaf9a991 100644 --- a/gtk/gtkcssdimensionvalue.c +++ b/gtk/gtkcssdimensionvalue.c @@ -325,3 +325,116 @@ gtk_css_dimension_value_new (double value, return result; } +GtkCssValue * +gtk_css_dimension_value_parse (GtkCssParser *parser, + GtkCssNumberParseFlags flags) +{ + static const struct { + const char *name; + GtkCssUnit unit; + GtkCssNumberParseFlags required_flags; + } units[] = { + { "px", GTK_CSS_PX, GTK_CSS_PARSE_LENGTH }, + { "pt", GTK_CSS_PT, GTK_CSS_PARSE_LENGTH }, + { "em", GTK_CSS_EM, GTK_CSS_PARSE_LENGTH }, + { "ex", GTK_CSS_EX, GTK_CSS_PARSE_LENGTH }, + { "rem", GTK_CSS_REM, GTK_CSS_PARSE_LENGTH }, + { "pc", GTK_CSS_PC, GTK_CSS_PARSE_LENGTH }, + { "in", GTK_CSS_IN, GTK_CSS_PARSE_LENGTH }, + { "cm", GTK_CSS_CM, GTK_CSS_PARSE_LENGTH }, + { "mm", GTK_CSS_MM, GTK_CSS_PARSE_LENGTH }, + { "rad", GTK_CSS_RAD, GTK_CSS_PARSE_ANGLE }, + { "deg", GTK_CSS_DEG, GTK_CSS_PARSE_ANGLE }, + { "grad", GTK_CSS_GRAD, GTK_CSS_PARSE_ANGLE }, + { "turn", GTK_CSS_TURN, GTK_CSS_PARSE_ANGLE }, + { "s", GTK_CSS_S, GTK_CSS_PARSE_TIME }, + { "ms", GTK_CSS_MS, GTK_CSS_PARSE_TIME } + }; + const GtkCssToken *token; + GtkCssValue *result; + GtkCssUnit unit; + double number; + + token = gtk_css_parser_get_token (parser); + + /* Handle percentages first */ + if (gtk_css_token_is (token, GTK_CSS_TOKEN_PERCENTAGE)) + { + if (!(flags & GTK_CSS_PARSE_PERCENT)) + { + gtk_css_parser_error_value (parser, "Percentages are not allowed here"); + return NULL; + } + number = token->number.number; + unit = GTK_CSS_PERCENT; + } + else if (gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER) || + gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER) || + gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_NUMBER) || + gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_NUMBER)) + { + number = token->number.number; + if (number == 0.0) + { + if (flags & GTK_CSS_PARSE_NUMBER) + unit = GTK_CSS_NUMBER; + else if (flags & GTK_CSS_PARSE_LENGTH) + unit = GTK_CSS_PX; + else if (flags & GTK_CSS_PARSE_ANGLE) + unit = GTK_CSS_DEG; + else if (flags & GTK_CSS_PARSE_TIME) + unit = GTK_CSS_S; + else + unit = GTK_CSS_PERCENT; + } + else if (flags & GTK_CSS_PARSE_NUMBER) + { + unit = GTK_CSS_NUMBER; + } + else + { + gtk_css_parser_error_syntax (parser, "Unit is missing."); + return NULL; + } + } + else if (gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION) || + gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION) || + gtk_css_token_is (token, GTK_CSS_TOKEN_DIMENSION)) + { + guint i; + + for (i = 0; i < G_N_ELEMENTS (units); i++) + { + if (flags & units[i].required_flags && + g_ascii_strcasecmp (token->dimension.dimension, units[i].name) == 0) + break; + } + + if (i >= G_N_ELEMENTS (units)) + { + gtk_css_parser_error_syntax (parser, "'%s' is not a valid unit.", token->dimension.dimension); + return NULL; + } + + unit = units[i].unit; + number = token->dimension.value; + } + else + { + gtk_css_parser_error_syntax (parser, "Expected a number"); + return NULL; + } + + if (flags & GTK_CSS_POSITIVE_ONLY && + number < 0) + { + gtk_css_parser_error_value (parser, "negative values are not allowed."); + return NULL; + } + + result = gtk_css_dimension_value_new (number, unit); + gtk_css_parser_consume_token (parser); + + return result; +} + diff --git a/gtk/gtkcssfontfeaturesvalue.c b/gtk/gtkcssfontfeaturesvalue.c index 6f7c2894f0..5f7dce764b 100644 --- a/gtk/gtkcssfontfeaturesvalue.c +++ b/gtk/gtkcssfontfeaturesvalue.c @@ -236,7 +236,6 @@ gtk_css_font_features_value_parse (GtkCssParser *parser) result = gtk_css_font_features_value_new_empty (); do { - _gtk_css_parser_skip_whitespace (parser); name = gtk_css_parser_consume_string (parser); if (name == NULL) { diff --git a/gtk/gtkcssfontvariationsvalue.c b/gtk/gtkcssfontvariationsvalue.c index be3ab90e72..537ebb1d6e 100644 --- a/gtk/gtkcssfontvariationsvalue.c +++ b/gtk/gtkcssfontvariationsvalue.c @@ -234,7 +234,6 @@ gtk_css_font_variations_value_parse (GtkCssParser *parser) result = gtk_css_font_variations_value_new_empty (); do { - _gtk_css_parser_skip_whitespace (parser); name = gtk_css_parser_consume_string (parser); if (name == NULL) { diff --git a/gtk/gtkcssimage.c b/gtk/gtkcssimage.c index ac0eab182a..b1e6d3b6e0 100644 --- a/gtk/gtkcssimage.c +++ b/gtk/gtkcssimage.c @@ -513,6 +513,9 @@ gtk_css_image_get_parser_type (GtkCssParser *parser) return image_types[i].type_func (); } + if (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_URL)) + return _gtk_css_image_url_get_type (); + return G_TYPE_INVALID; } diff --git a/gtk/gtkcsskeyframes.c b/gtk/gtkcsskeyframes.c index 891da2ea59..16d0e5efda 100644 --- a/gtk/gtkcsskeyframes.c +++ b/gtk/gtkcsskeyframes.c @@ -215,23 +215,19 @@ keyframes_set_value (GtkCssKeyframes *keyframes, } static gboolean -parse_declaration (GtkCssKeyframes *keyframes, - guint k, - GtkCssParser *parser) +gtk_css_keyframes_parse_declaration (GtkCssKeyframes *keyframes, + guint k, + GtkCssParser *parser) { GtkStyleProperty *property; GtkCssValue *value; char *name; - while (_gtk_css_parser_try (parser, ";", TRUE)) - { - /* SKIP ALL THE THINGS! */ - } - - name = _gtk_css_parser_try_ident (parser, TRUE); + name = gtk_css_parser_consume_ident (parser); if (name == NULL) { - _gtk_css_parser_error (parser, "No property name given"); + if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF)) + gtk_css_parser_error_syntax (parser, "Expected a property name"); return FALSE; } @@ -246,7 +242,7 @@ parse_declaration (GtkCssKeyframes *keyframes, g_free (name); - if (!_gtk_css_parser_try (parser, ":", TRUE)) + if (!gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COLON)) { _gtk_css_parser_error (parser, "Expected a ':'"); return FALSE; @@ -256,8 +252,7 @@ parse_declaration (GtkCssKeyframes *keyframes, if (value == NULL) return FALSE; - if (!_gtk_css_parser_try (parser, ";", TRUE) && - !gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_CLOSE_CURLY)) + if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF)) { _gtk_css_parser_error (parser, "Junk at end of value"); _gtk_css_value_unref (value); @@ -297,28 +292,27 @@ parse_declaration (GtkCssKeyframes *keyframes, } static gboolean -parse_block (GtkCssKeyframes *keyframes, - guint k, - GtkCssParser *parser) +gtk_css_keyframes_parse_block (GtkCssKeyframes *keyframes, + guint k, + GtkCssParser *parser) { - if (!_gtk_css_parser_try (parser, "{", TRUE)) + if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_OPEN_CURLY)) { - _gtk_css_parser_error (parser, "Expected closing bracket after keyframes block"); + gtk_css_parser_error_syntax (parser, "Expected '{'"); return FALSE; } - while (!_gtk_css_parser_try (parser, "}", TRUE)) - { - if (!parse_declaration (keyframes, k, parser)) - _gtk_css_parser_resync (parser, TRUE, '}'); + gtk_css_parser_start_block (parser); - if (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF)) - { - _gtk_css_parser_error (parser, "Expected closing '}' after keyframes block"); - return FALSE; - } + while (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF)) + { + gtk_css_parser_start_semicolon_block (parser, GTK_CSS_TOKEN_EOF); + gtk_css_keyframes_parse_declaration (keyframes, k, parser); + gtk_css_parser_end_block (parser); } + gtk_css_parser_end_block (parser); + return TRUE; } @@ -333,19 +327,18 @@ _gtk_css_keyframes_parse (GtkCssParser *parser) keyframes = gtk_css_keyframes_new (); - while (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_CLOSE_CURLY)) + while (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF)) { if (gtk_css_parser_try_ident (parser, "from")) progress = 0; else if (gtk_css_parser_try_ident (parser, "to")) progress = 1; - else if (gtk_css_parser_consume_number (parser, &progress) && - _gtk_css_parser_try (parser, "%", TRUE)) + else if (gtk_css_parser_consume_percentage (parser, &progress)) { if (progress < 0 || progress > 100) { /* XXX: should we skip over the block here? */ - _gtk_css_parser_error (parser, "percentages must be between 0%% and 100%%"); + gtk_css_parser_error_value (parser, "percentages must be between 0%% and 100%%"); _gtk_css_keyframes_unref (keyframes); return NULL; } @@ -353,14 +346,13 @@ _gtk_css_keyframes_parse (GtkCssParser *parser) } else { - _gtk_css_parser_error (parser, "expected a percentage"); _gtk_css_keyframes_unref (keyframes); return NULL; } k = gtk_css_keyframes_add_keyframe (keyframes, progress); - if (!parse_block (keyframes, k, parser)) + if (!gtk_css_keyframes_parse_block (keyframes, k, parser)) { _gtk_css_keyframes_unref (keyframes); return NULL; diff --git a/gtk/gtkcssnumbervalue.c b/gtk/gtkcssnumbervalue.c index 6b742c42e1..e8543bea28 100644 --- a/gtk/gtkcssnumbervalue.c +++ b/gtk/gtkcssnumbervalue.c @@ -129,7 +129,14 @@ gtk_css_number_value_transition (GtkCssValue *start, gboolean gtk_css_number_value_can_parse (GtkCssParser *parser) { - return _gtk_css_parser_has_number (parser) + return gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNED_NUMBER) + || gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_NUMBER) + || gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNED_INTEGER) + || gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_INTEGER) + || gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_PERCENTAGE) + || gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION) + || gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION) + || gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_DIMENSION) || gtk_css_parser_has_function (parser, "calc"); } diff --git a/gtk/gtkcssparser.c b/gtk/gtkcssparser.c index 502f2ec794..46b8a9786b 100644 --- a/gtk/gtkcssparser.c +++ b/gtk/gtkcssparser.c @@ -19,1159 +19,23 @@ #include "gtkcssparserprivate.h" -#include "gtkcssdimensionvalueprivate.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; - GFile *file; - GtkCssParserErrorFunc error_func; - gpointer user_data; - - const char *line_start; - guint line; - - /* Use this for parsing identifiers, names and strings. */ - GString *ident_str; -}; - -GtkCssParser * -_gtk_css_parser_new (const char *data, - GFile *file, - GtkCssParserErrorFunc error_func, - gpointer user_data) -{ - GtkCssParser *parser; - - g_return_val_if_fail (data != NULL, NULL); - g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL); - - parser = g_slice_new0 (GtkCssParser); - - parser->data = data; - if (file) - parser->file = g_object_ref (file); - parser->error_func = error_func; - parser->user_data = user_data; - - parser->line_start = data; - parser->line = 0; - - parser->ident_str = NULL; - - return parser; -} - -void -_gtk_css_parser_free (GtkCssParser *parser) -{ - g_return_if_fail (GTK_IS_CSS_PARSER (parser)); - - if (parser->file) - g_object_unref (parser->file); - - if (parser->ident_str) - g_string_free (parser->ident_str, TRUE); - - g_slice_free (GtkCssParser, parser); -} - -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; -} - -static GFile * -gtk_css_parser_get_base_file (GtkCssParser *parser) -{ - GFile *base; - - if (parser->file) - { - base = g_file_get_parent (parser->file); - } - else - { - char *dir = g_get_current_dir (); - base = g_file_new_for_path (dir); - g_free (dir); - } - - return base; -} - -GFile * -gtk_css_parser_resolve_url (GtkCssParser *parser, - const char *url) -{ - GFile *base, *file; - char *scheme; - - g_return_val_if_fail (parser != NULL, NULL); - g_return_val_if_fail (url != NULL, NULL); - - scheme = g_uri_parse_scheme (url); - if (scheme != NULL) - { - file = g_file_new_for_uri (url); - g_free (scheme); - return file; - } - g_free (scheme); - - base = gtk_css_parser_get_base_file (parser); - file = g_file_resolve_relative_path (base, url); - g_object_unref (base); - - return file; -} - -GFile * -_gtk_css_parser_get_file (GtkCssParser *parser) -{ - g_return_val_if_fail (parser != NULL, NULL); - - return parser->file; -} - -void -_gtk_css_parser_take_error (GtkCssParser *parser, - GError *error) -{ - parser->error_func (parser, error, parser->user_data); - - g_error_free (error); -} - void _gtk_css_parser_error (GtkCssParser *parser, const char *format, ...) { + GtkCssLocation location; + va_list args; GError *error; - va_list args; - + gtk_css_parser_get_location (parser, &location); va_start (args, format); error = g_error_new_valist (GTK_CSS_PARSER_ERROR, - GTK_CSS_PARSER_ERROR_SYNTAX, + GTK_CSS_PARSER_ERROR_FAILED, format, args); + gtk_css_parser_emit_error (parser, &location, &location, error); + g_error_free (error); va_end (args); - - _gtk_css_parser_take_error (parser, 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 (len > 0 && 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) -{ - gsize string_len; - - g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE); - g_return_val_if_fail (string != NULL, FALSE); - - string_len = strlen (string); - - if (g_ascii_strncasecmp (parser->data, string, string_len) != 0) - return FALSE; - - parser->data += string_len; - - if (skip_whitespace) - _gtk_css_parser_skip_whitespace (parser); - return TRUE; -} - -gboolean -gtk_css_parser_try_at_keyword (GtkCssParser *parser, - const char *keyword) -{ - gsize len; - - g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE); - g_return_val_if_fail (keyword != NULL, FALSE); - - len = strlen (keyword); - - if (parser->data[0] != '@' || - g_ascii_strncasecmp (&parser->data[1], keyword, len) != 0) - return FALSE; - - parser->data += len + 1; - - _gtk_css_parser_skip_whitespace (parser); - - return TRUE; -} - -gboolean -gtk_css_parser_try_ident (GtkCssParser *parser, - const char *ident) -{ - gsize len; - - g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE); - g_return_val_if_fail (ident != NULL, FALSE); - - len = strlen (ident); - - if (g_ascii_strncasecmp (parser->data, ident, len) != 0 || - parser->data[len] == '(') - return FALSE; - - parser->data += len; - - _gtk_css_parser_skip_whitespace (parser); - - return TRUE; -} - -gboolean -gtk_css_parser_try_delim (GtkCssParser *parser, - gunichar delim) -{ - if (*parser->data != delim) - return FALSE; - parser->data += 1; - _gtk_css_parser_skip_whitespace (parser); - return TRUE; -} - -gboolean -gtk_css_parser_try_token (GtkCssParser *parser, - GtkCssTokenType type) -{ - g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE); - - switch (type) - { - case GTK_CSS_TOKEN_OPEN_CURLY: - if (*parser->data != '{') - return FALSE; - parser->data += 1; - _gtk_css_parser_skip_whitespace (parser); - return TRUE; - - case GTK_CSS_TOKEN_CLOSE_CURLY: - if (*parser->data != '}') - return FALSE; - parser->data += 1; - _gtk_css_parser_skip_whitespace (parser); - return TRUE; - - case GTK_CSS_TOKEN_OPEN_PARENS: - if (*parser->data != '(') - return FALSE; - parser->data += 1; - _gtk_css_parser_skip_whitespace (parser); - return TRUE; - - case GTK_CSS_TOKEN_CLOSE_PARENS: - if (*parser->data != ')') - return FALSE; - parser->data += 1; - _gtk_css_parser_skip_whitespace (parser); - return TRUE; - - case GTK_CSS_TOKEN_COMMA: - if (*parser->data != ',') - return FALSE; - parser->data += 1; - _gtk_css_parser_skip_whitespace (parser); - return TRUE; - - case GTK_CSS_TOKEN_COLON: - if (*parser->data != ':') - return FALSE; - parser->data += 1; - _gtk_css_parser_skip_whitespace (parser); - return TRUE; - - case GTK_CSS_TOKEN_SEMICOLON: - if (*parser->data != ';') - return FALSE; - parser->data += 1; - _gtk_css_parser_skip_whitespace (parser); - return TRUE; - - default: - case GTK_CSS_TOKEN_STRING: - case GTK_CSS_TOKEN_AT_KEYWORD: - case GTK_CSS_TOKEN_IDENT: - case GTK_CSS_TOKEN_FUNCTION: - case GTK_CSS_TOKEN_HASH_UNRESTRICTED: - case GTK_CSS_TOKEN_HASH_ID: - case GTK_CSS_TOKEN_URL: - case GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION: - case GTK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION: - case GTK_CSS_TOKEN_DIMENSION: - case GTK_CSS_TOKEN_EOF: - case GTK_CSS_TOKEN_WHITESPACE: - case GTK_CSS_TOKEN_OPEN_SQUARE: - case GTK_CSS_TOKEN_CLOSE_SQUARE: - case GTK_CSS_TOKEN_CDC: - case GTK_CSS_TOKEN_CDO: - case GTK_CSS_TOKEN_DELIM: - case GTK_CSS_TOKEN_SIGNED_INTEGER: - case GTK_CSS_TOKEN_SIGNLESS_INTEGER: - case GTK_CSS_TOKEN_SIGNED_NUMBER: - case GTK_CSS_TOKEN_SIGNLESS_NUMBER: - case GTK_CSS_TOKEN_PERCENTAGE: - case GTK_CSS_TOKEN_INCLUDE_MATCH: - case GTK_CSS_TOKEN_DASH_MATCH: - case GTK_CSS_TOKEN_PREFIX_MATCH: - case GTK_CSS_TOKEN_SUFFIX_MATCH: - case GTK_CSS_TOKEN_SUBSTRING_MATCH: - case GTK_CSS_TOKEN_COLUMN: - case GTK_CSS_TOKEN_BAD_STRING: - case GTK_CSS_TOKEN_BAD_URL: - case GTK_CSS_TOKEN_COMMENT: - g_assert_not_reached (); - return FALSE; - } -} - -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; -} - -static char * -_gtk_css_parser_get_ident (GtkCssParser *parser) -{ - char *result; - gsize len; - - len = parser->ident_str->len; - - result = g_new (char, len + 1); - memcpy (result, parser->ident_str->str, len + 1); - g_string_set_size (parser->ident_str, 0); - - return result; -} - -char * -_gtk_css_parser_try_name (GtkCssParser *parser, - gboolean skip_whitespace) -{ - GString *name; - - g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL); - - if (parser->ident_str == NULL) - parser->ident_str = g_string_new (NULL); - - name = parser->ident_str; - - while (_gtk_css_parser_read_char (parser, name, NMCHAR)) - ; - - if (skip_whitespace) - _gtk_css_parser_skip_whitespace (parser); - - return _gtk_css_parser_get_ident (parser); -} - -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; - - if (parser->ident_str == NULL) - parser->ident_str = g_string_new (NULL); - - ident = parser->ident_str; - - if (*parser->data == '-') - { - g_string_append_c (ident, '-'); - parser->data++; - } - - if (!_gtk_css_parser_read_char (parser, ident, NMSTART)) - { - parser->data = start; - g_string_set_size (ident, 0); - return NULL; - } - - while (_gtk_css_parser_read_char (parser, ident, NMCHAR)) - ; - - if (skip_whitespace) - _gtk_css_parser_skip_whitespace (parser); - - return _gtk_css_parser_get_ident (parser); -} - -gboolean -gtk_css_parser_has_token (GtkCssParser *parser, - GtkCssTokenType type) -{ - g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE); - - switch (type) - { - case GTK_CSS_TOKEN_STRING: - return *parser->data == '"' || *parser->data == '\''; - - case GTK_CSS_TOKEN_OPEN_CURLY: - return *parser->data == '{'; - - case GTK_CSS_TOKEN_CLOSE_CURLY: - return *parser->data == '}'; - - case GTK_CSS_TOKEN_OPEN_PARENS: - return *parser->data == '('; - - case GTK_CSS_TOKEN_CLOSE_PARENS: - return *parser->data == ')'; - - case GTK_CSS_TOKEN_COMMA: - return *parser->data == ','; - - case GTK_CSS_TOKEN_COLON: - return *parser->data == ':'; - - case GTK_CSS_TOKEN_SEMICOLON: - return *parser->data == ';'; - - case GTK_CSS_TOKEN_AT_KEYWORD: - return *parser->data == '@'; - - case GTK_CSS_TOKEN_EOF: - return *parser->data == 0; - - case GTK_CSS_TOKEN_IDENT: - return *parser->data != 0 && - strchr (NMSTART "-", *parser->data) != NULL; - - default: - case GTK_CSS_TOKEN_FUNCTION: - case GTK_CSS_TOKEN_HASH_UNRESTRICTED: - case GTK_CSS_TOKEN_HASH_ID: - case GTK_CSS_TOKEN_URL: - case GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION: - case GTK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION: - case GTK_CSS_TOKEN_DIMENSION: - case GTK_CSS_TOKEN_WHITESPACE: - case GTK_CSS_TOKEN_OPEN_SQUARE: - case GTK_CSS_TOKEN_CLOSE_SQUARE: - case GTK_CSS_TOKEN_CDC: - case GTK_CSS_TOKEN_CDO: - case GTK_CSS_TOKEN_DELIM: - case GTK_CSS_TOKEN_SIGNED_INTEGER: - case GTK_CSS_TOKEN_SIGNLESS_INTEGER: - case GTK_CSS_TOKEN_SIGNED_NUMBER: - case GTK_CSS_TOKEN_SIGNLESS_NUMBER: - case GTK_CSS_TOKEN_PERCENTAGE: - case GTK_CSS_TOKEN_INCLUDE_MATCH: - case GTK_CSS_TOKEN_DASH_MATCH: - case GTK_CSS_TOKEN_PREFIX_MATCH: - case GTK_CSS_TOKEN_SUFFIX_MATCH: - case GTK_CSS_TOKEN_SUBSTRING_MATCH: - case GTK_CSS_TOKEN_COLUMN: - case GTK_CSS_TOKEN_BAD_STRING: - case GTK_CSS_TOKEN_BAD_URL: - case GTK_CSS_TOKEN_COMMENT: - g_assert_not_reached (); - return FALSE; - } -} - -gboolean -gtk_css_parser_has_ident (GtkCssParser *parser, - const char *ident) -{ - gsize len; - - g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE); - g_return_val_if_fail (ident != NULL, FALSE); - - len = strlen (ident); - - return g_ascii_strncasecmp (parser->data, ident, len) == 0 && - parser->data[len] != '('; -} - -gboolean -gtk_css_parser_has_integer (GtkCssParser *parser) -{ - guint i; - - i = 0; - if (parser->data[0] == '-') - i++; - - if (parser->data[i] >= '0' && parser->data[i] <= '9') - return TRUE; - - return FALSE; -} - -gboolean -gtk_css_parser_has_function (GtkCssParser *parser, - const char *name) -{ - gsize len; - - g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE); - g_return_val_if_fail (name != NULL, FALSE); - - len = strlen (name); - - return g_ascii_strncasecmp (parser->data, name, len) == 0 && - parser->data[len] == '('; -} - -char * -gtk_css_parser_consume_string (GtkCssParser *parser) -{ - GString *str; - char quote; - - g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL); - - quote = *parser->data; - - if (quote != '"' && quote != '\'') - { - _gtk_css_parser_error (parser, "Expected a string."); - return NULL; - } - - parser->data++; - - if (parser->ident_str == NULL) - parser->ident_str = g_string_new (NULL); - - str = parser->ident_str; - g_assert (str->len == 0); - - 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 _gtk_css_parser_get_ident (parser); - } - - 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_set_size (str, 0); - return NULL; - default: - _gtk_css_parser_error (parser, - "Invalid character in string. Must be escaped."); - g_string_set_size (str, 0); - return NULL; - } - } - - g_assert_not_reached (); - return NULL; -} - -gboolean -gtk_css_parser_consume_integer (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 == '+') - goto fail; - - errno = 0; - result = g_ascii_strtoll (parser->data, &end, 10); - if (errno) - goto fail; - if (result > G_MAXINT || result < G_MININT) - goto fail; - if (parser->data == end) - goto fail; - - parser->data = end; - *value = result; - - _gtk_css_parser_skip_whitespace (parser); - - return TRUE; - -fail: - _gtk_css_parser_error (parser, "Expected an integer"); - return FALSE; -} - -gboolean -gtk_css_parser_consume_number (GtkCssParser *self, - double *number) -{ - gdouble result; - char *end; - - g_return_val_if_fail (GTK_IS_CSS_PARSER (self), FALSE); - g_return_val_if_fail (number != NULL, FALSE); - - errno = 0; - result = g_ascii_strtod (self->data, &end); - if (errno || - self->data == end) - { - _gtk_css_parser_error (self, "Expected a number"); - return FALSE; - } - - self->data = end; - *number = result; - - _gtk_css_parser_skip_whitespace (self); - - return TRUE; -} - -char * -gtk_css_parser_consume_ident (GtkCssParser *self) -{ - char *result; - - result = _gtk_css_parser_try_ident (self, TRUE); - if (result == NULL) - _gtk_css_parser_error (self, "Expected an identifier"); - - return result; -} - -gboolean -_gtk_css_parser_has_number (GtkCssParser *parser) -{ - char c; - - if (parser->data[0] == '-' || parser->data[0] == '+') - c = parser->data[1]; - else - c = parser->data[0]; - - /* ahem */ - return g_ascii_isdigit (c) || c == '.'; -} - -GtkCssValue * -gtk_css_dimension_value_parse (GtkCssParser *parser, - GtkCssNumberParseFlags flags) -{ - static const struct { - const char *name; - GtkCssUnit unit; - GtkCssNumberParseFlags required_flags; - } units[] = { - { "px", GTK_CSS_PX, GTK_CSS_PARSE_LENGTH }, - { "pt", GTK_CSS_PT, GTK_CSS_PARSE_LENGTH }, - { "em", GTK_CSS_EM, GTK_CSS_PARSE_LENGTH }, - { "ex", GTK_CSS_EX, GTK_CSS_PARSE_LENGTH }, - { "rem", GTK_CSS_REM, GTK_CSS_PARSE_LENGTH }, - { "pc", GTK_CSS_PC, GTK_CSS_PARSE_LENGTH }, - { "in", GTK_CSS_IN, GTK_CSS_PARSE_LENGTH }, - { "cm", GTK_CSS_CM, GTK_CSS_PARSE_LENGTH }, - { "mm", GTK_CSS_MM, GTK_CSS_PARSE_LENGTH }, - { "rad", GTK_CSS_RAD, GTK_CSS_PARSE_ANGLE }, - { "deg", GTK_CSS_DEG, GTK_CSS_PARSE_ANGLE }, - { "grad", GTK_CSS_GRAD, GTK_CSS_PARSE_ANGLE }, - { "turn", GTK_CSS_TURN, GTK_CSS_PARSE_ANGLE }, - { "s", GTK_CSS_S, GTK_CSS_PARSE_TIME }, - { "ms", GTK_CSS_MS, GTK_CSS_PARSE_TIME } - }; - char *end, *unit_name; - double value; - GtkCssUnit unit; - - g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL); - - errno = 0; - value = g_ascii_strtod (parser->data, &end); - if (errno) - { - _gtk_css_parser_error (parser, "not a number: %s", g_strerror (errno)); - return NULL; - } - if (parser->data == end) - { - _gtk_css_parser_error (parser, "not a number"); - return NULL; - } - - parser->data = end; - - if (flags & GTK_CSS_POSITIVE_ONLY && - value < 0) - { - _gtk_css_parser_error (parser, "negative values are not allowed."); - return NULL; - } - - unit_name = _gtk_css_parser_try_ident (parser, FALSE); - - if (unit_name) - { - guint i; - - for (i = 0; i < G_N_ELEMENTS (units); i++) - { - if (flags & units[i].required_flags && - g_ascii_strcasecmp (unit_name, units[i].name) == 0) - break; - } - - if (i >= G_N_ELEMENTS (units)) - { - _gtk_css_parser_error (parser, "'%s' is not a valid unit.", unit_name); - g_free (unit_name); - return NULL; - } - - unit = units[i].unit; - - g_free (unit_name); - } - else - { - if ((flags & GTK_CSS_PARSE_PERCENT) && - _gtk_css_parser_try (parser, "%", FALSE)) - { - unit = GTK_CSS_PERCENT; - } - else if (value == 0.0) - { - if (flags & GTK_CSS_PARSE_NUMBER) - unit = GTK_CSS_NUMBER; - else if (flags & GTK_CSS_PARSE_LENGTH) - unit = GTK_CSS_PX; - else if (flags & GTK_CSS_PARSE_ANGLE) - unit = GTK_CSS_DEG; - else if (flags & GTK_CSS_PARSE_TIME) - unit = GTK_CSS_S; - else - unit = GTK_CSS_PERCENT; - } - else if (flags & GTK_CSS_PARSE_NUMBER) - { - unit = GTK_CSS_NUMBER; - } - else - { - _gtk_css_parser_error (parser, "Unit is missing."); - return NULL; - } - } - - _gtk_css_parser_skip_whitespace (parser); - - return gtk_css_dimension_value_new (value, unit); -} - -gboolean -_gtk_css_parser_try_hash_color (GtkCssParser *parser, - GdkRGBA *rgba) -{ - if (parser->data[0] == '#' && - g_ascii_isxdigit (parser->data[1]) && - g_ascii_isxdigit (parser->data[2]) && - g_ascii_isxdigit (parser->data[3])) - { - if (g_ascii_isxdigit (parser->data[4])) - { - if (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; - if (g_ascii_isxdigit (parser->data[7]) && - g_ascii_isxdigit (parser->data[8])) - { - rgba->alpha = ((get_xdigit (parser->data[7]) << 4) + get_xdigit (parser->data[8])) / 255.0; - parser->data += 9; - } - else - { - 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 = get_xdigit (parser->data[4]) / 15.0; - parser->data += 5; - } - } - 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 TRUE; - } - - return FALSE; -} - -GFile * -gtk_css_parser_consume_url (GtkCssParser *parser) -{ - gchar *path; - GFile *file; - - if (_gtk_css_parser_try (parser, "url", FALSE)) - { - if (!_gtk_css_parser_try (parser, "(", TRUE)) - { - _gtk_css_parser_error (parser, "Expected '(' after 'url'"); - return NULL; - } - - path = gtk_css_parser_consume_string (parser); - if (path == NULL) - return NULL; - - if (!_gtk_css_parser_try (parser, ")", TRUE)) - { - _gtk_css_parser_error (parser, "No closing ')' found for 'url'"); - g_free (path); - return NULL; - } - } - else - { - path = _gtk_css_parser_try_name (parser, TRUE); - if (path == NULL) - { - _gtk_css_parser_error (parser, "Not a valid url"); - return NULL; - } - } - - file = gtk_css_parser_resolve_url (parser, path); - g_free (path); - - return file; -} - -static 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_has_token (parser, GTK_CSS_TOKEN_STRING)) - { - /* Hrm, this emits errors, and i suspect it shouldn't... */ - char *free_me = gtk_css_parser_consume_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, ')'); - if (*parser->data) - parser->data++; - break; - case '[': - parser->data++; - _gtk_css_parser_resync (parser, FALSE, ']'); - if (*parser->data) - parser->data++; - break; - case '{': - parser->data++; - _gtk_css_parser_resync (parser, FALSE, '}'); - if (*parser->data) - 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; - case '\0': - break; - case '/': - default: - parser->data++; - break; - } - } while (*parser->data); -} - -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); } void @@ -1219,99 +83,3 @@ out: g_string_append_c (str, '"'); } -gboolean -gtk_css_parser_consume_function (GtkCssParser *self, - guint min_args, - guint max_args, - guint (* parse_func) (GtkCssParser *, guint, gpointer), - gpointer data) -{ - gboolean result = FALSE; - char *function_name; - guint arg; - - function_name = _gtk_css_parser_try_ident (self, FALSE); - g_return_val_if_fail (function_name != NULL, FALSE); - g_return_val_if_fail (_gtk_css_parser_try (self, "(", TRUE), FALSE); - - arg = 0; - while (TRUE) - { - guint parse_args = parse_func (self, arg, data); - if (parse_args == 0) - break; - arg += parse_args; - if (gtk_css_parser_try_token (self, GTK_CSS_TOKEN_CLOSE_PARENS)) - { - if (arg < min_args) - { - _gtk_css_parser_error (self, "%s() requires at least %u arguments", function_name, min_args); - break; - } - else - { - result = TRUE; - break; - } - } - else if (gtk_css_parser_try_token (self, GTK_CSS_TOKEN_COMMA)) - { - if (arg >= max_args) - { - _gtk_css_parser_error (self, "Expected ')' at end of %s()", function_name); - break; - } - - continue; - } - else - { - _gtk_css_parser_error (self, "Unexpected data at end of %s() argument", function_name); - break; - } - } - - g_free (function_name); - - return result; -} - -gsize -gtk_css_parser_consume_any (GtkCssParser *parser, - const GtkCssParseOption *options, - gsize n_options, - gpointer user_data) -{ - gsize result; - gsize i; - - g_return_val_if_fail (parser != NULL, 0); - g_return_val_if_fail (options != NULL, 0); - g_return_val_if_fail (n_options < sizeof (gsize) * 8 - 1, 0); - - result = 0; - while (result != (1 << n_options) - 1) - { - for (i = 0; i < n_options; i++) - { - if (result & (1 << i)) - continue; - if (options[i].can_parse && !options[i].can_parse (parser, options[i].data, user_data)) - continue; - if (!options[i].parse (parser, options[i].data, user_data)) - return 0; - result |= 1 << i; - break; - } - if (i == n_options) - break; - } - if (result == 0) - { - _gtk_css_parser_error (parser, "No valid value given"); - return result; - } - - return result; -} - diff --git a/gtk/gtkcssparserprivate.h b/gtk/gtkcssparserprivate.h index fec25fceff..68de9ea207 100644 --- a/gtk/gtkcssparserprivate.h +++ b/gtk/gtkcssparserprivate.h @@ -22,104 +22,14 @@ #include #include "gtk/css/gtkcsstokenizerprivate.h" +#include "gtk/css/gtkcssparserprivate.h" G_BEGIN_DECLS -typedef struct _GtkCssParser GtkCssParser; - -typedef void (* GtkCssParserErrorFunc) (GtkCssParser *parser, - const GError *error, - gpointer user_data); - -typedef struct _GtkCssParseOption GtkCssParseOption; - -struct _GtkCssParseOption -{ - gboolean (* can_parse) (GtkCssParser *parser, - gpointer option_data, - gpointer user_data); - gboolean (* parse) (GtkCssParser *parser, - gpointer option_data, - gpointer user_data); - gpointer data; -}; - -GtkCssParser * _gtk_css_parser_new (const char *data, - GFile *file, - GtkCssParserErrorFunc error_func, - gpointer user_data); -void _gtk_css_parser_free (GtkCssParser *parser); - -void _gtk_css_parser_take_error (GtkCssParser *parser, - GError *error); void _gtk_css_parser_error (GtkCssParser *parser, const char *format, ...) G_GNUC_PRINTF (2, 3); -guint _gtk_css_parser_get_line (GtkCssParser *parser); -guint _gtk_css_parser_get_position (GtkCssParser *parser); -GFile * _gtk_css_parser_get_file (GtkCssParser *parser); -GFile * gtk_css_parser_resolve_url (GtkCssParser *parser, - const char *url); - -gboolean gtk_css_parser_has_token (GtkCssParser *parser, - GtkCssTokenType token_type); -gboolean gtk_css_parser_has_ident (GtkCssParser *parser, - const char *name); -gboolean gtk_css_parser_has_integer (GtkCssParser *parser); -gboolean gtk_css_parser_has_function (GtkCssParser *parser, - const char *name); - -/* 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_ident (GtkCssParser *parser, - const char *ident); -gboolean gtk_css_parser_try_delim (GtkCssParser *parser, - gunichar delim); -gboolean gtk_css_parser_try_at_keyword (GtkCssParser *parser, - const char *keyword); -gboolean gtk_css_parser_try_token (GtkCssParser *parser, - GtkCssTokenType token_type); -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_hash_color (GtkCssParser *parser, - GdkRGBA *rgba); - -char * gtk_css_parser_consume_ident (GtkCssParser *self); -char * gtk_css_parser_consume_string (GtkCssParser *self); -GFile * gtk_css_parser_consume_url (GtkCssParser *self); -gboolean gtk_css_parser_consume_number (GtkCssParser *self, - double *number); -gboolean gtk_css_parser_consume_integer (GtkCssParser *parser, - int *value); -gboolean gtk_css_parser_consume_function (GtkCssParser *self, - guint min_args, - guint max_args, - guint (* parse_func) (GtkCssParser *, guint, gpointer), - gpointer data); -gsize gtk_css_parser_consume_any (GtkCssParser *parser, - const GtkCssParseOption *options, - gsize n_options, - gpointer user_data); - -gboolean _gtk_css_parser_has_number (GtkCssParser *parser); - -void _gtk_css_parser_skip_whitespace (GtkCssParser *parser); -void _gtk_css_parser_resync (GtkCssParser *parser, - gboolean sync_at_semicolon, - char terminator); - /* XXX: Find better place to put it? */ void _gtk_css_print_string (GString *str, const char *string); diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c index 1abcaa8472..8f99c79579 100644 --- a/gtk/gtkcssprovider.c +++ b/gtk/gtkcssprovider.c @@ -140,7 +140,7 @@ static void gtk_css_provider_load_internal (GtkCssProvider *css_provider, GtkCssScanner *scanner, GFile *file, - const char *data); + GBytes *bytes); G_DEFINE_TYPE_EXTENDED (GtkCssProvider, gtk_css_provider, G_TYPE_OBJECT, 0, G_ADD_PRIVATE (GtkCssProvider) @@ -312,7 +312,7 @@ gtk_css_scanner_destroy (GtkCssScanner *scanner) if (scanner->section) gtk_css_section_unref (scanner->section); g_object_unref (scanner->provider); - _gtk_css_parser_free (scanner->parser); + gtk_css_parser_unref (scanner->parser); g_slice_free (GtkCssScanner, scanner); } @@ -336,9 +336,11 @@ gtk_css_provider_emit_error (GtkCssProvider *provider, } static void -gtk_css_scanner_parser_error (GtkCssParser *parser, - const GError *error, - gpointer user_data) +gtk_css_scanner_parser_error (GtkCssParser *parser, + const GtkCssLocation *start, + const GtkCssLocation *end, + const GError *error, + gpointer user_data) { GtkCssScanner *scanner = user_data; @@ -350,7 +352,7 @@ gtk_css_scanner_new (GtkCssProvider *provider, GtkCssScanner *parent, GtkCssSection *section, GFile *file, - const gchar *text) + GBytes *bytes) { GtkCssScanner *scanner; @@ -362,10 +364,12 @@ gtk_css_scanner_new (GtkCssProvider *provider, if (section) scanner->section = gtk_css_section_ref (section); - scanner->parser = _gtk_css_parser_new (text, - file, - gtk_css_scanner_parser_error, - scanner); + scanner->parser = gtk_css_parser_new_for_bytes (bytes, + file, + NULL, + gtk_css_scanner_parser_error, + scanner, + NULL); return scanner; } @@ -376,7 +380,7 @@ gtk_css_scanner_would_recurse (GtkCssScanner *scanner, { while (scanner) { - GFile *parser_file = _gtk_css_parser_get_file (scanner->parser); + GFile *parser_file = gtk_css_parser_get_file (scanner->parser); if (parser_file && g_file_equal (parser_file, file)) return TRUE; @@ -766,7 +770,6 @@ gtk_css_provider_reset (GtkCssProvider *css_provider) g_array_set_size (priv->rulesets, 0); _gtk_css_selector_tree_free (priv->tree); priv->tree = NULL; - } static gboolean @@ -790,6 +793,15 @@ parse_import (GtkCssScanner *scanner) if (url) { file = gtk_css_parser_resolve_url (scanner->parser, url); + if (file == NULL) + { + gtk_css_provider_error (scanner->provider, + scanner, + GTK_CSS_PARSER_ERROR, + GTK_CSS_PARSER_ERROR_IMPORT, + "Could not resolve \"%s\" to a valid URL", + url); + } g_free (url); } else @@ -802,15 +814,11 @@ parse_import (GtkCssScanner *scanner) if (file == NULL) { - _gtk_css_parser_resync (scanner->parser, TRUE, 0); - gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_IMPORT); - return TRUE; + /* nothing to do */ } - - if (!_gtk_css_parser_try (scanner->parser, ";", FALSE)) + else if (!gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_EOF)) { gtk_css_provider_invalid_token (scanner->provider, scanner, "semicolon"); - _gtk_css_parser_resync (scanner->parser, TRUE, 0); } else if (gtk_css_scanner_would_recurse (scanner, file)) { @@ -831,10 +839,9 @@ parse_import (GtkCssScanner *scanner) NULL); } - g_object_unref (file); + g_clear_object (&file); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_IMPORT); - _gtk_css_parser_skip_whitespace (scanner->parser); return TRUE; } @@ -854,15 +861,9 @@ parse_color_definition (GtkCssScanner *scanner) return FALSE; } - name = _gtk_css_parser_try_name (scanner->parser, TRUE); + name = gtk_css_parser_consume_ident (scanner->parser); if (name == NULL) { - gtk_css_provider_error_literal (scanner->provider, - scanner, - GTK_CSS_PARSER_ERROR, - GTK_CSS_PARSER_ERROR_SYNTAX, - "Not a valid color name"); - _gtk_css_parser_resync (scanner->parser, TRUE, 0); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION); return TRUE; } @@ -871,12 +872,11 @@ parse_color_definition (GtkCssScanner *scanner) if (color == NULL) { g_free (name); - _gtk_css_parser_resync (scanner->parser, TRUE, 0); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION); return TRUE; } - if (!_gtk_css_parser_try (scanner->parser, ";", TRUE)) + if (!gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_EOF)) { g_free (name); _gtk_css_value_unref (color); @@ -885,7 +885,6 @@ parse_color_definition (GtkCssScanner *scanner) GTK_CSS_PARSER_ERROR, GTK_CSS_PARSER_ERROR_SYNTAX, "Missing semicolon at end of color definition"); - _gtk_css_parser_resync (scanner->parser, TRUE, 0); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION); return TRUE; @@ -912,52 +911,39 @@ parse_keyframes (GtkCssScanner *scanner) return FALSE; } - name = _gtk_css_parser_try_ident (scanner->parser, TRUE); + name = gtk_css_parser_consume_ident (scanner->parser); if (name == NULL) { - gtk_css_provider_error_literal (scanner->provider, - scanner, - GTK_CSS_PARSER_ERROR, - GTK_CSS_PARSER_ERROR_SYNTAX, - "Expected name for keyframes"); - _gtk_css_parser_resync (scanner->parser, TRUE, 0); - goto exit; + gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_KEYFRAMES); + return FALSE; } - if (!_gtk_css_parser_try (scanner->parser, "{", TRUE)) + if (!gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_EOF)) { gtk_css_provider_error_literal (scanner->provider, scanner, GTK_CSS_PARSER_ERROR, GTK_CSS_PARSER_ERROR_SYNTAX, "Expected '{' for keyframes"); - _gtk_css_parser_resync (scanner->parser, TRUE, 0); - g_free (name); - goto exit; + gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_KEYFRAMES); + return FALSE; } + gtk_css_parser_end_block_prelude (scanner->parser); + keyframes = _gtk_css_keyframes_parse (scanner->parser); - if (keyframes == NULL) - { - _gtk_css_parser_resync (scanner->parser, TRUE, '}'); - g_free (name); - goto exit; - } + if (keyframes != NULL) + g_hash_table_insert (priv->keyframes, name, keyframes); - g_hash_table_insert (priv->keyframes, name, keyframes); - - if (!_gtk_css_parser_try (scanner->parser, "}", TRUE)) + if (!gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_EOF)) { gtk_css_provider_error_literal (scanner->provider, scanner, GTK_CSS_PARSER_ERROR, GTK_CSS_PARSER_ERROR_SYNTAX, "expected '}' after declarations"); - if (!gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_EOF)) - _gtk_css_parser_resync (scanner->parser, FALSE, 0); } -exit: gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_KEYFRAMES); return TRUE; @@ -966,22 +952,20 @@ exit: static void parse_at_keyword (GtkCssScanner *scanner) { - if (parse_import (scanner)) - return; - if (parse_color_definition (scanner)) - return; - if (parse_keyframes (scanner)) - return; + gtk_css_parser_start_semicolon_block (scanner->parser, GTK_CSS_TOKEN_OPEN_CURLY); - else + if (!parse_import (scanner) && + !parse_color_definition (scanner) && + !parse_keyframes (scanner)) { gtk_css_provider_error_literal (scanner->provider, scanner, GTK_CSS_PARSER_ERROR, GTK_CSS_PARSER_ERROR_SYNTAX, "unknown @ rule"); - _gtk_css_parser_resync (scanner->parser, TRUE, 0); } + + gtk_css_parser_end_block (scanner->parser); } static GSList * @@ -996,15 +980,13 @@ parse_selector_list (GtkCssScanner *scanner) if (select == NULL) { - g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free); - _gtk_css_parser_resync (scanner->parser, FALSE, 0); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_SELECTOR); return NULL; } selectors = g_slist_prepend (selectors, select); } - while (_gtk_css_parser_try (scanner->parser, ",", TRUE)); + while (gtk_css_parser_try_token (scanner->parser, GTK_CSS_TOKEN_COMMA)); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_SELECTOR); @@ -1019,27 +1001,37 @@ parse_declaration (GtkCssScanner *scanner, char *name; gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_DECLARATION); + gtk_css_parser_start_semicolon_block (scanner->parser, GTK_CSS_TOKEN_EOF); - name = _gtk_css_parser_try_ident (scanner->parser, TRUE); - if (name == NULL) - goto check_for_semicolon; - - property = _gtk_style_property_lookup (name); - - if (!gtk_css_parser_try_token (scanner->parser, GTK_CSS_TOKEN_COLON)) + if (gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_EOF)) { - gtk_css_provider_invalid_token (scanner->provider, scanner, "':'"); - _gtk_css_parser_resync (scanner->parser, TRUE, '}'); - g_free (name); + gtk_css_parser_warn_syntax (scanner->parser, "Empty declaration"); + gtk_css_parser_end_block (scanner->parser); + return; + } + + name = gtk_css_parser_consume_ident (scanner->parser); + if (name == NULL) + { + gtk_css_parser_end_block (scanner->parser); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION); return; } + property = _gtk_style_property_lookup (name); + if (property) { GtkCssValue *value; - g_free (name); + if (!gtk_css_parser_try_token (scanner->parser, GTK_CSS_TOKEN_COLON)) + { + gtk_css_parser_error_syntax (scanner->parser, "Expected ':'"); + g_free (name); + gtk_css_parser_end_block (scanner->parser); + gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION); + return; + } gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_VALUE); @@ -1048,22 +1040,20 @@ parse_declaration (GtkCssScanner *scanner, if (value == NULL) { - _gtk_css_parser_resync (scanner->parser, TRUE, '}'); + gtk_css_parser_end_block (scanner->parser); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION); return; } - if (!gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_SEMICOLON) && - !gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_CLOSE_CURLY) && - !gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_EOF)) + if (!gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_EOF)) { gtk_css_provider_error (scanner->provider, scanner, GTK_CSS_PARSER_ERROR, GTK_CSS_PARSER_ERROR_SYNTAX, "Junk at end of value for %s", property->name); - _gtk_css_parser_resync (scanner->parser, TRUE, '}'); + gtk_css_parser_end_block (scanner->parser); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION); return; @@ -1098,32 +1088,21 @@ parse_declaration (GtkCssScanner *scanner, gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE); } else - g_free (name); - -check_for_semicolon: - gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION); - - if (!_gtk_css_parser_try (scanner->parser, ";", TRUE)) { - if (!gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_CLOSE_CURLY) && - !gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_EOF)) - { - gtk_css_provider_error_literal (scanner->provider, - scanner, - GTK_CSS_PARSER_ERROR, - GTK_CSS_PARSER_ERROR_SYNTAX, - "Expected semicolon"); - _gtk_css_parser_resync (scanner->parser, TRUE, '}'); - } + gtk_css_parser_error_value (scanner->parser, "No property named \"%s\"", name); } + + g_free (name); + + gtk_css_parser_end_block (scanner->parser); + gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION); } static void parse_declarations (GtkCssScanner *scanner, GtkCssRuleset *ruleset) { - while (!gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_EOF) && - !gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_CLOSE_CURLY)) + while (!gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_EOF)) { parse_declaration (scanner, ruleset); } @@ -1140,40 +1119,31 @@ parse_ruleset (GtkCssScanner *scanner) selectors = parse_selector_list (scanner); if (selectors == NULL) { + gtk_css_parser_skip_until (scanner->parser, GTK_CSS_TOKEN_OPEN_CURLY); + gtk_css_parser_skip (scanner->parser); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET); return; } - if (!_gtk_css_parser_try (scanner->parser, "{", TRUE)) + if (!gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_OPEN_CURLY)) { gtk_css_provider_error_literal (scanner->provider, scanner, GTK_CSS_PARSER_ERROR, GTK_CSS_PARSER_ERROR_SYNTAX, "expected '{' after selectors"); - _gtk_css_parser_resync (scanner->parser, FALSE, 0); g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free); + gtk_css_parser_skip_until (scanner->parser, GTK_CSS_TOKEN_OPEN_CURLY); + gtk_css_parser_skip (scanner->parser); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET); return; } + gtk_css_parser_start_block (scanner->parser); + parse_declarations (scanner, &ruleset); - if (!_gtk_css_parser_try (scanner->parser, "}", TRUE)) - { - gtk_css_provider_error_literal (scanner->provider, - scanner, - GTK_CSS_PARSER_ERROR, - GTK_CSS_PARSER_ERROR_SYNTAX, - "expected '}' after declarations"); - if (!gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_EOF)) - { - _gtk_css_parser_resync (scanner->parser, FALSE, 0); - g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free); - gtk_css_ruleset_clear (&ruleset); - gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET); - } - } + gtk_css_parser_end_block (scanner->parser); css_provider_commit (scanner->provider, selectors, &ruleset); gtk_css_ruleset_clear (&ruleset); @@ -1194,13 +1164,14 @@ parse_stylesheet (GtkCssScanner *scanner) { gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_DOCUMENT); - _gtk_css_parser_skip_whitespace (scanner->parser); - while (!gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_EOF)) { - if (_gtk_css_parser_try (scanner->parser, "", TRUE)) - continue; + if (gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_CDO) || + gtk_css_parser_has_token (scanner->parser, GTK_CSS_TOKEN_CDC)) + { + gtk_css_parser_consume_token (scanner->parser); + continue; + } parse_statement (scanner); } @@ -1265,30 +1236,27 @@ static void gtk_css_provider_load_internal (GtkCssProvider *css_provider, GtkCssScanner *parent, GFile *file, - const char *text) + GBytes *bytes) { GtkCssScanner *scanner; - GBytes *bytes; - if (text == NULL) + if (bytes == NULL) { GError *load_error = NULL; bytes = g_file_load_bytes (file, NULL, NULL, &load_error); - if (bytes) - { - text = g_bytes_get_data (bytes, NULL); - } - else + if (bytes == NULL) { if (parent == NULL) { + GBytes *tmp_bytes = g_bytes_new_static ("", 0); scanner = gtk_css_scanner_new (css_provider, NULL, NULL, file, - ""); + tmp_bytes); + g_bytes_unref (tmp_bytes); gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_DOCUMENT); } @@ -1310,18 +1278,14 @@ gtk_css_provider_load_internal (GtkCssProvider *css_provider, } } } - else - { - bytes = NULL; - } - if (text) + if (bytes) { scanner = gtk_css_scanner_new (css_provider, parent, parent ? parent->section : NULL, file, - text); + bytes); parse_stylesheet (scanner); @@ -1330,9 +1294,6 @@ gtk_css_provider_load_internal (GtkCssProvider *css_provider, if (parent == NULL) gtk_css_provider_postprocess (css_provider); } - - if (bytes) - g_bytes_unref (bytes); } /** @@ -1351,27 +1312,21 @@ gtk_css_provider_load_from_data (GtkCssProvider *css_provider, const gchar *data, gssize length) { - char *free_data; + GBytes *bytes; g_return_if_fail (GTK_IS_CSS_PROVIDER (css_provider)); g_return_if_fail (data != NULL); if (length < 0) - { - length = strlen (data); - free_data = NULL; - } - else - { - free_data = g_strndup (data, length); - data = free_data; - } + length = strlen (data); + + bytes = g_bytes_new_static (data, length); gtk_css_provider_reset (css_provider); - gtk_css_provider_load_internal (css_provider, NULL, NULL, data); + gtk_css_provider_load_internal (css_provider, NULL, NULL, bytes); - g_free (free_data); + g_bytes_unref (bytes); gtk_style_provider_changed (GTK_STYLE_PROVIDER (css_provider)); } diff --git a/gtk/gtkcsssection.c b/gtk/gtkcsssection.c index b8e15f2c98..747069212a 100644 --- a/gtk/gtkcsssection.c +++ b/gtk/gtkcsssection.c @@ -43,6 +43,7 @@ _gtk_css_section_new (GtkCssSection *parent, GtkCssParser *parser) { GtkCssSection *section; + GtkCssLocation location; gtk_internal_return_val_if_fail (parser != NULL, NULL); @@ -52,12 +53,13 @@ _gtk_css_section_new (GtkCssSection *parent, section->section_type = type; if (parent) section->parent = gtk_css_section_ref (parent); - section->file = _gtk_css_parser_get_file (parser); + section->file = gtk_css_parser_get_file (parser); if (section->file) g_object_ref (section->file); - section->start_line = _gtk_css_parser_get_line (parser); - section->start_position = _gtk_css_parser_get_position (parser); section->parser = parser; + gtk_css_parser_get_location (section->parser, &location); + section->start_line = location.lines; + section->start_position = location.line_chars; return section; } @@ -82,11 +84,14 @@ _gtk_css_section_new_for_file (GtkCssSectionType type, void _gtk_css_section_end (GtkCssSection *section) { + GtkCssLocation location; + gtk_internal_return_if_fail (section != NULL); gtk_internal_return_if_fail (section->parser != NULL); - section->end_line = _gtk_css_parser_get_line (section->parser); - section->end_position = _gtk_css_parser_get_position (section->parser); + gtk_css_parser_get_location (section->parser, &location); + section->end_line = location.lines; + section->end_position = location.line_chars; section->parser = NULL; } @@ -243,12 +248,15 @@ gtk_css_section_get_start_position (const GtkCssSection *section) guint gtk_css_section_get_end_line (const GtkCssSection *section) { + GtkCssLocation location; + gtk_internal_return_val_if_fail (section != NULL, 0); - if (section->parser) - return _gtk_css_parser_get_line (section->parser); - else + if (!section->parser) return section->end_line; + + gtk_css_parser_get_location (section->parser, &location); + return location.lines; } /** @@ -269,12 +277,15 @@ gtk_css_section_get_end_line (const GtkCssSection *section) guint gtk_css_section_get_end_position (const GtkCssSection *section) { + GtkCssLocation location; + gtk_internal_return_val_if_fail (section != NULL, 0); - if (section->parser) - return _gtk_css_parser_get_position (section->parser); - else + if (!section->parser) return section->end_position; + + gtk_css_parser_get_location (section->parser, &location); + return location.line_chars; } void diff --git a/gtk/gtkcssselector.c b/gtk/gtkcssselector.c index 30de213cc7..bd1b1523f2 100644 --- a/gtk/gtkcssselector.c +++ b/gtk/gtkcssselector.c @@ -25,6 +25,7 @@ #include "gtkcssprovider.h" #include "gtkstylecontextprivate.h" +#include #if defined(_MSC_VER) && _MSC_VER >= 1500 # include #endif @@ -922,296 +923,544 @@ gtk_css_selector_new (const GtkCssSelectorClass *class, } static GtkCssSelector * -parse_selector_class (GtkCssParser *parser, - GtkCssSelector *selector, - gboolean negate) -{ - char *name; - - name = _gtk_css_parser_try_name (parser, FALSE); - - if (name == NULL) - { - _gtk_css_parser_error (parser, "Expected a valid name for class"); - if (selector) - _gtk_css_selector_free (selector); - return NULL; - } - - selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_CLASS - : >K_CSS_SELECTOR_CLASS, - selector); - selector->style_class.style_class = g_quark_from_string (name); - - g_free (name); - - return selector; -} - -static GtkCssSelector * -parse_selector_id (GtkCssParser *parser, - GtkCssSelector *selector, - gboolean negate) -{ - char *name; - - name = _gtk_css_parser_try_name (parser, FALSE); - - if (name == NULL) - { - _gtk_css_parser_error (parser, "Expected a valid name for id"); - if (selector) - _gtk_css_selector_free (selector); - return NULL; - } - - selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_ID - : >K_CSS_SELECTOR_ID, - selector); - selector->id.name = g_intern_string (name); - - g_free (name); - - return selector; -} - -static GtkCssSelector * -parse_selector_pseudo_class_nth_child (GtkCssParser *parser, +gtk_css_selector_parse_selector_class (GtkCssParser *parser, GtkCssSelector *selector, - PositionType type, gboolean negate) { - int a, b; + const GtkCssToken *token; - if (!_gtk_css_parser_try (parser, "(", TRUE)) + gtk_css_parser_consume_token (parser); + for (token = gtk_css_parser_peek_token (parser); + gtk_css_token_is (token, GTK_CSS_TOKEN_COMMENT); + token = gtk_css_parser_peek_token (parser)) { - _gtk_css_parser_error (parser, "Missing opening bracket for pseudo-class"); - if (selector) - _gtk_css_selector_free (selector); - return NULL; + gtk_css_parser_consume_token (parser); } - if (_gtk_css_parser_try (parser, "even", TRUE)) + if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT)) { - a = 2; - b = 0; - } - else if (_gtk_css_parser_try (parser, "odd", TRUE)) - { - a = 2; - b = 1; - } - else if (type == POSITION_FORWARD && - _gtk_css_parser_try (parser, "first", TRUE)) - { - a = 0; - b = 1; - } - else if (type == POSITION_FORWARD && - _gtk_css_parser_try (parser, "last", TRUE)) - { - a = 0; - b = 1; - type = POSITION_BACKWARD; - } - else - { - int multiplier; - - if (_gtk_css_parser_try (parser, "+", TRUE)) - multiplier = 1; - else if (_gtk_css_parser_try (parser, "-", TRUE)) - multiplier = -1; - else - multiplier = 1; - - if (gtk_css_parser_has_integer (parser)) - { - if (!gtk_css_parser_consume_integer (parser, &a)) - { - if (selector) - _gtk_css_selector_free (selector); - return NULL; - } - if (a < 0) - { - _gtk_css_parser_error (parser, "Expected an integer"); - if (selector) - _gtk_css_selector_free (selector); - return NULL; - } - a *= multiplier; - } - else if (gtk_css_parser_has_ident (parser, "n")) - { - a = multiplier; - } - else - { - _gtk_css_parser_error (parser, "Expected an integer"); - if (selector) - _gtk_css_selector_free (selector); - return NULL; - } - - if (_gtk_css_parser_try (parser, "n", TRUE)) - { - if (_gtk_css_parser_try (parser, "+", TRUE)) - multiplier = 1; - else if (_gtk_css_parser_try (parser, "-", TRUE)) - multiplier = -1; - else - multiplier = 1; - - if (gtk_css_parser_has_integer (parser)) - { - if (!gtk_css_parser_consume_integer (parser, &b)) - { - if (selector) - _gtk_css_selector_free (selector); - return NULL; - } - if (b < 0) - { - _gtk_css_parser_error (parser, "Expected an integer"); - if (selector) - _gtk_css_selector_free (selector); - return NULL; - } - } - else - b = 0; - - b *= multiplier; - } - else - { - b = a; - a = 0; - } - } - - if (!_gtk_css_parser_try (parser, ")", FALSE)) - { - _gtk_css_parser_error (parser, "Missing closing bracket for pseudo-class"); - if (selector) - _gtk_css_selector_free (selector); - return NULL; - } - - selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_PSEUDOCLASS_POSITION - : >K_CSS_SELECTOR_PSEUDOCLASS_POSITION, - selector); - selector->position.type = type; - selector->position.a = a; - selector->position.b = b; - - return selector; -} - -static GtkCssSelector * -parse_selector_pseudo_class (GtkCssParser *parser, - GtkCssSelector *selector, - gboolean negate) -{ - static const struct { - const char *name; - GtkStateFlags state_flag; - PositionType position_type; - int position_a; - int position_b; - } pseudo_classes[] = { - { "first-child", 0, POSITION_FORWARD, 0, 1 }, - { "last-child", 0, POSITION_BACKWARD, 0, 1 }, - { "only-child", 0, POSITION_ONLY, 0, 0 }, - { "active", GTK_STATE_FLAG_ACTIVE, }, - { "hover", GTK_STATE_FLAG_PRELIGHT, }, - { "selected", GTK_STATE_FLAG_SELECTED, }, - { "disabled", GTK_STATE_FLAG_INSENSITIVE, }, - { "indeterminate", GTK_STATE_FLAG_INCONSISTENT, }, - { "focus(visible)", GTK_STATE_FLAG_FOCUS_VISIBLE, }, - { "focus", GTK_STATE_FLAG_FOCUSED, }, - { "backdrop", GTK_STATE_FLAG_BACKDROP, }, - { "dir(ltr)", GTK_STATE_FLAG_DIR_LTR, }, - { "dir(rtl)", GTK_STATE_FLAG_DIR_RTL, }, - { "link", GTK_STATE_FLAG_LINK, }, - { "visited", GTK_STATE_FLAG_VISITED, }, - { "checked", GTK_STATE_FLAG_CHECKED, }, - { "drop(active)", GTK_STATE_FLAG_DROP_ACTIVE, } - }; - - guint i; - - if (_gtk_css_parser_try (parser, "nth-child", FALSE)) - return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_FORWARD, negate); - else if (_gtk_css_parser_try (parser, "nth-last-child", FALSE)) - return parse_selector_pseudo_class_nth_child (parser, selector, POSITION_BACKWARD, negate); - - for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++) - { - if (_gtk_css_parser_try (parser, pseudo_classes[i].name, FALSE)) - { - if (pseudo_classes[i].state_flag) - { - selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE - : >K_CSS_SELECTOR_PSEUDOCLASS_STATE, - selector); - selector->state.state = pseudo_classes[i].state_flag; - } - else - { - selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_PSEUDOCLASS_POSITION - : >K_CSS_SELECTOR_PSEUDOCLASS_POSITION, - selector); - selector->position.type = pseudo_classes[i].position_type; - selector->position.a = pseudo_classes[i].position_a; - selector->position.b = pseudo_classes[i].position_b; - } - return selector; - } - } - - _gtk_css_parser_error (parser, "Invalid name of pseudo-class"); - if (selector) - _gtk_css_selector_free (selector); - return NULL; -} - -static GtkCssSelector * -parse_selector_negation (GtkCssParser *parser, - GtkCssSelector *selector) -{ - char *name; - - name = _gtk_css_parser_try_ident (parser, FALSE); - if (name) - { - selector = gtk_css_selector_new (>K_CSS_SELECTOR_NOT_NAME, + selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_CLASS + : >K_CSS_SELECTOR_CLASS, selector); - selector->name.name = g_intern_string (name); - g_free (name); + selector->style_class.style_class = g_quark_from_string (token->string.string); + gtk_css_parser_consume_token (parser); + return selector; } - else if (_gtk_css_parser_try (parser, "*", FALSE)) - selector = gtk_css_selector_new (>K_CSS_SELECTOR_NOT_ANY, selector); - else if (_gtk_css_parser_try (parser, "#", FALSE)) - selector = parse_selector_id (parser, selector, TRUE); - else if (_gtk_css_parser_try (parser, ".", FALSE)) - selector = parse_selector_class (parser, selector, TRUE); - else if (_gtk_css_parser_try (parser, ":", FALSE)) - selector = parse_selector_pseudo_class (parser, selector, TRUE); else { - _gtk_css_parser_error (parser, "Not a valid selector for :not()"); + gtk_css_parser_error_syntax (parser, "No class name after '.' in selector"); if (selector) _gtk_css_selector_free (selector); return NULL; } +} + +static gboolean +string_has_number (const char *string, + const char *prefix, + int *number) +{ + gsize len = strlen (prefix); + char *end; + + if (g_ascii_strncasecmp (string, prefix, len) != 0) + return FALSE; + + errno = 0; + *number = strtoul (string + len, &end, 10); + if (*end != '\0' || errno != 0) + return FALSE; + + return TRUE; +} + +static gboolean +parse_plus_b (GtkCssParser *parser, + gboolean negate, + gint *b) +{ + const GtkCssToken *token; + gboolean has_seen_sign; + + token = gtk_css_parser_get_token (parser); + + if (negate) + { + has_seen_sign = TRUE; + } + else + { + if (gtk_css_token_is_delim (token, '+')) + { + gtk_css_parser_consume_token (parser); + has_seen_sign = TRUE; + } + else if (gtk_css_token_is_delim (token, '-')) + { + gtk_css_parser_consume_token (parser); + negate = TRUE; + has_seen_sign = TRUE; + } + else + { + has_seen_sign = FALSE; + } + } + + token = gtk_css_parser_get_token (parser); + if (!has_seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER)) + { + *b = token->number.number; + gtk_css_parser_consume_token (parser); + return TRUE; + } + else if (has_seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER)) + { + *b = token->number.number; + if (negate) + *b = - *b; + gtk_css_parser_consume_token (parser); + return TRUE; + } + else if (!has_seen_sign) + { + *b = 0; + return TRUE; + } - _gtk_css_parser_skip_whitespace (parser); + gtk_css_parser_error_syntax (parser, "Not a valid an+b type"); + return FALSE; +} - if (!_gtk_css_parser_try (parser, ")", FALSE)) +static gboolean +parse_n_plus_b (GtkCssParser *parser, + gint before, + gint *a, + gint *b) +{ + const GtkCssToken *token; + + token = gtk_css_parser_get_token (parser); + + if (gtk_css_token_is_ident (token, "n")) { - _gtk_css_parser_error (parser, "Missing closing bracket for :not()"); + *a = before; + gtk_css_parser_consume_token (parser); + return parse_plus_b (parser, FALSE, b); + } + else if (gtk_css_token_is_ident (token, "n-")) + { + *a = before; + gtk_css_parser_consume_token (parser); + return parse_plus_b (parser, TRUE, b); + } + else if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT) && + string_has_number (token->string.string, "n-", b)) + { + *a = before; + *b = -*b; + gtk_css_parser_consume_token (parser); + return TRUE; + } + else + { + *b = before; + *a = 0; + return TRUE; + } + + gtk_css_parser_error_syntax (parser, "Not a valid an+b type"); + return FALSE; +} + +static gboolean +parse_a_n_plus_b (GtkCssParser *parser, + gint seen_sign, + gint *a, + gint *b) +{ + const GtkCssToken *token; + + token = gtk_css_parser_get_token (parser); + + if (!seen_sign && gtk_css_token_is_ident (token, "even")) + { + *a = 2; + *b = 0; + gtk_css_parser_consume_token (parser); + return TRUE; + } + else if (!seen_sign && gtk_css_token_is_ident (token, "odd")) + { + *a = 2; + *b = 1; + gtk_css_parser_consume_token (parser); + return TRUE; + } + else if (!seen_sign && gtk_css_token_is_delim (token, '+')) + { + gtk_css_parser_consume_token (parser); + return parse_a_n_plus_b (parser, 1, a, b); + } + else if (!seen_sign && gtk_css_token_is_delim (token, '-')) + { + gtk_css_parser_consume_token (parser); + return parse_a_n_plus_b (parser, -1, a, b); + } + else if ((!seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER)) || + gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER)) + { + int x = token->number.number * (seen_sign ? seen_sign : 1); + gtk_css_parser_consume_token (parser); + + return parse_n_plus_b (parser, x , a, b); + } + else if (((!seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION)) || + gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION)) && + g_ascii_strcasecmp (token->dimension.dimension, "n") == 0) + { + *a = token->dimension.value * (seen_sign ? seen_sign : 1); + gtk_css_parser_consume_token (parser); + return parse_plus_b (parser, FALSE, b); + } + else if (((!seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION)) || + gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION)) && + g_ascii_strcasecmp (token->dimension.dimension, "n-") == 0) + { + *a = token->dimension.value * (seen_sign ? seen_sign : 1); + gtk_css_parser_consume_token (parser); + return parse_plus_b (parser, TRUE, b); + } + else if (((!seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION)) || + gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION)) && + string_has_number (token->dimension.dimension, "n-", b)) + { + *a = token->dimension.value * (seen_sign ? seen_sign : 1); + *b = -*b; + gtk_css_parser_consume_token (parser); + return TRUE; + } + else if (!seen_sign && gtk_css_token_is_ident (token, "-n")) + { + *a = -1; + gtk_css_parser_consume_token (parser); + return parse_plus_b (parser, FALSE, b); + } + else if (!seen_sign && gtk_css_token_is_ident (token, "-n-")) + { + *a = -1; + gtk_css_parser_consume_token (parser); + return parse_plus_b (parser, TRUE, b); + } + else if (!seen_sign && + gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT) && + string_has_number (token->string.string, "-n-", b)) + { + *a = -1; + *b = -*b; + gtk_css_parser_consume_token (parser); + return TRUE; + } + else if (gtk_css_token_is_ident (token, "n") || + gtk_css_token_is_ident (token, "n-")) + { + return parse_n_plus_b (parser, seen_sign ? seen_sign : 1, a, b); + } + else if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT) && + string_has_number (token->string.string, "n-", b)) + { + *a = seen_sign ? seen_sign : 1; + *b = -*b; + gtk_css_parser_consume_token (parser); + return TRUE; + } + else if (!seen_sign && gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT) && + string_has_number (token->string.string, "-n-", b)) + { + *a = -1; + *b = -*b; + gtk_css_parser_consume_token (parser); + return TRUE; + } + + gtk_css_parser_error_syntax (parser, "Not a valid an+b type"); + return FALSE; +} + +static guint +parse_a_n_plus_b_arg (GtkCssParser *parser, + guint arg, + gpointer data) +{ + gint *ab = data; + + if (!parse_a_n_plus_b (parser, FALSE, &ab[0], &ab[1])) + return 0; + + return 1; +} + +static guint +parse_dir_arg (GtkCssParser *parser, + guint arg, + gpointer data) +{ + GtkStateFlags *flag = data; + + if (gtk_css_parser_try_ident (parser, "ltr")) + { + *flag = GTK_STATE_FLAG_DIR_LTR; + return 1; + } + else if (gtk_css_parser_try_ident (parser, "rtl")) + { + *flag = GTK_STATE_FLAG_DIR_RTL; + return 1; + } + else + { + gtk_css_parser_error_value (parser, "Expected \"ltr\" or \"rtl\""); + return 0; + } +} + +static guint +parse_identifier_arg (GtkCssParser *parser, + guint arg, + gpointer data) +{ + const char *ident = data; + + if (!gtk_css_parser_try_ident (parser, ident)) + { + gtk_css_parser_error_value (parser, "Expected \"%s\"", ident); + return 0; + } + + return 1; +} + +static GtkCssSelector * +gtk_css_selector_parse_selector_pseudo_class (GtkCssParser *parser, + GtkCssSelector *selector, + gboolean negate) +{ + const GtkCssToken *token; + + gtk_css_parser_consume_token (parser); + for (token = gtk_css_parser_peek_token (parser); + gtk_css_token_is (token, GTK_CSS_TOKEN_COMMENT); + token = gtk_css_parser_peek_token (parser)) + { + gtk_css_parser_consume_token (parser); + } + + if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT)) + { + static const struct { + const char *name; + GtkStateFlags state_flag; + PositionType position_type; + int position_a; + int position_b; + } pseudo_classes[] = { + { "first-child", 0, POSITION_FORWARD, 0, 1 }, + { "last-child", 0, POSITION_BACKWARD, 0, 1 }, + { "only-child", 0, POSITION_ONLY, 0, 0 }, + { "active", GTK_STATE_FLAG_ACTIVE, }, + { "hover", GTK_STATE_FLAG_PRELIGHT, }, + { "selected", GTK_STATE_FLAG_SELECTED, }, + { "disabled", GTK_STATE_FLAG_INSENSITIVE, }, + { "indeterminate", GTK_STATE_FLAG_INCONSISTENT, }, + { "focus", GTK_STATE_FLAG_FOCUSED, }, + { "backdrop", GTK_STATE_FLAG_BACKDROP, }, + { "link", GTK_STATE_FLAG_LINK, }, + { "visited", GTK_STATE_FLAG_VISITED, }, + { "checked", GTK_STATE_FLAG_CHECKED, }, + }; + guint i; + + for (i = 0; i < G_N_ELEMENTS (pseudo_classes); i++) + { + if (g_ascii_strcasecmp (pseudo_classes[i].name, token->string.string) == 0) + { + if (pseudo_classes[i].state_flag) + { + selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE + : >K_CSS_SELECTOR_PSEUDOCLASS_STATE, + selector); + selector->state.state = pseudo_classes[i].state_flag; + } + else + { + selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_PSEUDOCLASS_POSITION + : >K_CSS_SELECTOR_PSEUDOCLASS_POSITION, + selector); + selector->position.type = pseudo_classes[i].position_type; + selector->position.a = pseudo_classes[i].position_a; + selector->position.b = pseudo_classes[i].position_b; + } + gtk_css_parser_consume_token (parser); + return selector; + } + } + + gtk_css_parser_error_value (parser, "Unknown name of pseudo-class"); + if (selector) + _gtk_css_selector_free (selector); + return NULL; + } + else if (gtk_css_token_is (token, GTK_CSS_TOKEN_FUNCTION)) + { + if (gtk_css_token_is_function (token, "nth-child")) + { + gint ab[2]; + + if (!gtk_css_parser_consume_function (parser, 1, 1, parse_a_n_plus_b_arg, ab)) + { + if (selector) + _gtk_css_selector_free (selector); + return NULL; + } + + selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_PSEUDOCLASS_POSITION + : >K_CSS_SELECTOR_PSEUDOCLASS_POSITION, + selector); + selector->position.type = POSITION_FORWARD; + selector->position.a = ab[0]; + selector->position.b = ab[1]; + } + else if (gtk_css_token_is_function (token, "nth-last-child")) + { + gint ab[2]; + + if (!gtk_css_parser_consume_function (parser, 1, 1, parse_a_n_plus_b_arg, ab)) + { + if (selector) + _gtk_css_selector_free (selector); + return NULL; + } + + selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_PSEUDOCLASS_POSITION + : >K_CSS_SELECTOR_PSEUDOCLASS_POSITION, + selector); + selector->position.type = POSITION_BACKWARD; + selector->position.a = ab[0]; + selector->position.b = ab[1]; + } + else if (gtk_css_token_is_function (token, "not")) + { + if (negate) + { + gtk_css_parser_error_syntax (parser, "Nesting of :not() not allowed"); + if (selector) + _gtk_css_selector_free (selector); + return NULL; + } + else + { + gtk_css_parser_start_block (parser); + token = gtk_css_parser_get_token (parser); + + if (gtk_css_token_is_delim (token, '*')) + { + selector = gtk_css_selector_new (>K_CSS_SELECTOR_NOT_ANY, selector); + gtk_css_parser_consume_token (parser); + } + else if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT)) + { + selector = gtk_css_selector_new (>K_CSS_SELECTOR_NOT_NAME, selector); + selector->name.name = g_intern_string (token->string.string); + gtk_css_parser_consume_token (parser); + } + else if (gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_ID)) + { + selector = gtk_css_selector_new (>K_CSS_SELECTOR_NOT_ID, selector); + selector->id.name = g_intern_string (token->string.string); + gtk_css_parser_consume_token (parser); + } + else if (gtk_css_token_is_delim (token, '.')) + { + selector = gtk_css_selector_parse_selector_class (parser, selector, TRUE); + } + else if (gtk_css_token_is (token, GTK_CSS_TOKEN_COLON)) + { + selector = gtk_css_selector_parse_selector_pseudo_class (parser, selector, TRUE); + } + else + { + gtk_css_parser_error_syntax (parser, "Invalid contents of :not() selector"); + if (selector) + _gtk_css_selector_free (selector); + selector = NULL; + return NULL; + } + + token = gtk_css_parser_get_token (parser); + if (!gtk_css_token_is (token, GTK_CSS_TOKEN_EOF)) + { + gtk_css_parser_error_syntax (parser, "Invalid contents of :not() selector"); + if (selector) + _gtk_css_selector_free (selector); + selector = NULL; + return NULL; + } + gtk_css_parser_end_block (parser); + } + } + else if (gtk_css_token_is_function (token, "dir")) + { + GtkStateFlags flag; + + if (!gtk_css_parser_consume_function (parser, 1, 1, parse_dir_arg, &flag)) + { + if (selector) + _gtk_css_selector_free (selector); + return NULL; + } + + selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE + : >K_CSS_SELECTOR_PSEUDOCLASS_STATE, + selector); + selector->state.state = flag; + } + else if (gtk_css_token_is_function (token, "drop")) + { + if (!gtk_css_parser_consume_function (parser, 1, 1, parse_identifier_arg, (gpointer) "active")) + { + if (selector) + _gtk_css_selector_free (selector); + return NULL; + } + selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE + : >K_CSS_SELECTOR_PSEUDOCLASS_STATE, + selector); + selector->state.state = GTK_STATE_FLAG_DROP_ACTIVE; + } + else if (gtk_css_token_is_function (token, "focus")) + { + if (!gtk_css_parser_consume_function (parser, 1, 1, parse_identifier_arg, (gpointer) "visible")) + { + if (selector) + _gtk_css_selector_free (selector); + return NULL; + } + selector = gtk_css_selector_new (negate ? >K_CSS_SELECTOR_NOT_PSEUDOCLASS_STATE + : >K_CSS_SELECTOR_PSEUDOCLASS_STATE, + selector); + selector->state.state = GTK_STATE_FLAG_FOCUS_VISIBLE; + } + else + { + gtk_css_parser_error_value (parser, "Unknown pseudoclass"); + if (selector) + _gtk_css_selector_free (selector); + return NULL; + } + } + else + { + gtk_css_parser_error_value (parser, "Unknown pseudoclass"); if (selector) _gtk_css_selector_free (selector); return NULL; @@ -1221,50 +1470,60 @@ parse_selector_negation (GtkCssParser *parser, } static GtkCssSelector * -parse_simple_selector (GtkCssParser *parser, - GtkCssSelector *selector) +gtk_css_selector_parse_simple_selector (GtkCssParser *parser, + GtkCssSelector *selector) { gboolean parsed_something = FALSE; - char *name; - - name = _gtk_css_parser_try_ident (parser, FALSE); - if (name) - { - selector = gtk_css_selector_new (>K_CSS_SELECTOR_NAME, selector); - selector->name.name = g_intern_string (name); - g_free (name); - parsed_something = TRUE; - } - else if (_gtk_css_parser_try (parser, "*", FALSE)) - { - selector = gtk_css_selector_new (>K_CSS_SELECTOR_ANY, selector); - parsed_something = TRUE; - } + const GtkCssToken *token; do { - if (_gtk_css_parser_try (parser, "#", FALSE)) - selector = parse_selector_id (parser, selector, FALSE); - else if (_gtk_css_parser_try (parser, ".", FALSE)) - selector = parse_selector_class (parser, selector, FALSE); - else if (_gtk_css_parser_try (parser, ":not(", TRUE)) - selector = parse_selector_negation (parser, selector); - else if (_gtk_css_parser_try (parser, ":", FALSE)) - selector = parse_selector_pseudo_class (parser, selector, FALSE); - else if (!parsed_something) + for (token = gtk_css_parser_peek_token (parser); + gtk_css_token_is (token, GTK_CSS_TOKEN_COMMENT); + token = gtk_css_parser_peek_token (parser)) { - _gtk_css_parser_error (parser, "Expected a valid selector"); - if (selector) - _gtk_css_selector_free (selector); - return NULL; + gtk_css_parser_consume_token (parser); + } + + if (!parsed_something && gtk_css_token_is_delim (token, '*')) + { + selector = gtk_css_selector_new (>K_CSS_SELECTOR_ANY, selector); + gtk_css_parser_consume_token (parser); + } + else if (!parsed_something && gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT)) + { + selector = gtk_css_selector_new (>K_CSS_SELECTOR_NAME, selector); + selector->name.name = g_intern_string (token->string.string); + gtk_css_parser_consume_token (parser); + } + else if (gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_ID)) + { + selector = gtk_css_selector_new (>K_CSS_SELECTOR_ID, selector); + selector->id.name = g_intern_string (token->string.string); + gtk_css_parser_consume_token (parser); + } + else if (gtk_css_token_is_delim (token, '.')) + { + selector = gtk_css_selector_parse_selector_class (parser, selector, FALSE); + } + else if (gtk_css_token_is (token, GTK_CSS_TOKEN_COLON)) + { + selector = gtk_css_selector_parse_selector_pseudo_class (parser, selector, FALSE); } else - break; + { + if (!parsed_something) + { + gtk_css_parser_error_syntax (parser, "Expected a valid selector"); + if (selector) + _gtk_css_selector_free (selector); + selector = NULL; + } + break; + } parsed_something = TRUE; } - while (selector && !gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF)); - - _gtk_css_parser_skip_whitespace (parser); + while (TRUE); return selector; } @@ -1273,20 +1532,59 @@ GtkCssSelector * _gtk_css_selector_parse (GtkCssParser *parser) { GtkCssSelector *selector = NULL; + const GtkCssToken *token; - while ((selector = parse_simple_selector (parser, selector)) && - !gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF) && - !gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_COMMA) && - !gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_OPEN_CURLY)) + while (TRUE) { - if (_gtk_css_parser_try (parser, "+", TRUE)) - selector = gtk_css_selector_new (>K_CSS_SELECTOR_ADJACENT, selector); - else if (_gtk_css_parser_try (parser, "~", TRUE)) - selector = gtk_css_selector_new (>K_CSS_SELECTOR_SIBLING, selector); - else if (_gtk_css_parser_try (parser, ">", TRUE)) - selector = gtk_css_selector_new (>K_CSS_SELECTOR_CHILD, selector); + gboolean seen_whitespace = FALSE; + + /* skip all whitespace and comments */ + gtk_css_parser_get_token (parser); + + selector = gtk_css_selector_parse_simple_selector (parser, selector); + if (selector == NULL) + return NULL; + + for (token = gtk_css_parser_peek_token (parser); + gtk_css_token_is (token, GTK_CSS_TOKEN_COMMENT) || + gtk_css_token_is (token, GTK_CSS_TOKEN_WHITESPACE); + token = gtk_css_parser_peek_token (parser)) + { + seen_whitespace |= gtk_css_token_is (token, GTK_CSS_TOKEN_WHITESPACE); + gtk_css_parser_consume_token (parser); + } + + if (gtk_css_token_is_delim (token, '+')) + { + selector = gtk_css_selector_new (>K_CSS_SELECTOR_ADJACENT, selector); + gtk_css_parser_consume_token (parser); + } + else if (gtk_css_token_is_delim (token, '~')) + { + selector = gtk_css_selector_new (>K_CSS_SELECTOR_SIBLING, selector); + gtk_css_parser_consume_token (parser); + } + else if (gtk_css_token_is_delim (token, '>')) + { + selector = gtk_css_selector_new (>K_CSS_SELECTOR_CHILD, selector); + gtk_css_parser_consume_token (parser); + } + else if (gtk_css_token_is (token, GTK_CSS_TOKEN_EOF) || + gtk_css_token_is (token, GTK_CSS_TOKEN_COMMA) || + gtk_css_token_is (token, GTK_CSS_TOKEN_OPEN_CURLY)) + { + break; + } + else if (seen_whitespace) + { + selector = gtk_css_selector_new (>K_CSS_SELECTOR_DESCENDANT, selector); + } else - selector = gtk_css_selector_new (>K_CSS_SELECTOR_DESCENDANT, selector); + { + gtk_css_parser_error_syntax (parser, "Expected a valid selector"); + _gtk_css_selector_free (selector); + return NULL; + } } return selector; diff --git a/gtk/gtkcssshadowvalue.c b/gtk/gtkcssshadowvalue.c index bd8746ab13..072396f6c9 100644 --- a/gtk/gtkcssshadowvalue.c +++ b/gtk/gtkcssshadowvalue.c @@ -266,10 +266,7 @@ _gtk_css_shadow_value_parse (GtkCssParser *parser, } else if (!inset && box_shadow_mode && gtk_css_parser_try_ident (parser, "inset")) { - if (values[HOFFSET] == NULL) - goto fail; inset = TRUE; - break; } else if (values[COLOR] == NULL) {