]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssparser.c
8bdcfc291d4fcd1b05efb1feace16a7dfe6bc2d4
[~andy/gtk] / gtk / gtkcssparser.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2011 Benjamin Otte <otte@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include "gtkcssparserprivate.h"
23 #include "gtkwin32themeprivate.h"
24
25 #include <errno.h>
26 #include <string.h>
27
28 /* just for the errors, yay! */
29 #include "gtkcssprovider.h"
30
31 #define NEWLINE_CHARS "\r\n"
32 #define WHITESPACE_CHARS "\f \t"
33 #define NMSTART "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
34 #define NMCHAR NMSTART "01234567890-_"
35 #define URLCHAR NMCHAR "!#$%&*~"
36
37 #define GTK_IS_CSS_PARSER(parser) ((parser) != NULL)
38
39 struct _GtkCssParser
40 {
41   const char *data;
42   GtkCssParserErrorFunc error_func;
43   gpointer              user_data;
44
45   const char *line_start;
46   guint line;
47 };
48
49 GtkCssParser *
50 _gtk_css_parser_new (const char            *data,
51                      GtkCssParserErrorFunc  error_func,
52                      gpointer               user_data)
53 {
54   GtkCssParser *parser;
55
56   g_return_val_if_fail (data != NULL, NULL);
57
58   parser = g_slice_new0 (GtkCssParser);
59
60   parser->data = data;
61   parser->error_func = error_func;
62   parser->user_data = user_data;
63
64   parser->line_start = data;
65   parser->line = 0;
66
67   return parser;
68 }
69
70 void
71 _gtk_css_parser_free (GtkCssParser *parser)
72 {
73   g_return_if_fail (GTK_IS_CSS_PARSER (parser));
74
75   g_slice_free (GtkCssParser, parser);
76 }
77
78 gboolean
79 _gtk_css_parser_is_eof (GtkCssParser *parser)
80 {
81   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), TRUE);
82
83   return *parser->data == 0;
84 }
85
86 gboolean
87 _gtk_css_parser_begins_with (GtkCssParser *parser,
88                              char          c)
89 {
90   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), TRUE);
91
92   return *parser->data == c;
93 }
94
95 gboolean
96 _gtk_css_parser_has_prefix (GtkCssParser *parser,
97                             const char   *prefix)
98 {
99   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
100
101   return g_ascii_strncasecmp (parser->data, prefix, strlen (prefix)) == 0;
102 }
103
104 guint
105 _gtk_css_parser_get_line (GtkCssParser *parser)
106 {
107   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), 1);
108
109   return parser->line;
110 }
111
112 guint
113 _gtk_css_parser_get_position (GtkCssParser *parser)
114 {
115   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), 1);
116
117   return parser->data - parser->line_start;
118 }
119
120 void
121 _gtk_css_parser_take_error (GtkCssParser *parser,
122                             GError       *error)
123 {
124   parser->error_func (parser, error, parser->user_data);
125
126   g_error_free (error);
127 }
128
129 void
130 _gtk_css_parser_error (GtkCssParser *parser,
131                        const char   *format,
132                        ...)
133 {
134   GError *error;
135
136   va_list args;
137
138   va_start (args, format);
139   error = g_error_new_valist (GTK_CSS_PROVIDER_ERROR,
140                               GTK_CSS_PROVIDER_ERROR_SYNTAX,
141                               format, args);
142   va_end (args);
143
144   _gtk_css_parser_take_error (parser, error);
145 }
146
147 static gboolean
148 gtk_css_parser_new_line (GtkCssParser *parser)
149 {
150   gboolean result = FALSE;
151
152   if (*parser->data == '\r')
153     {
154       result = TRUE;
155       parser->data++;
156     }
157   if (*parser->data == '\n')
158     {
159       result = TRUE;
160       parser->data++;
161     }
162
163   if (result)
164     {
165       parser->line++;
166       parser->line_start = parser->data;
167     }
168
169   return result;
170 }
171
172 static gboolean
173 gtk_css_parser_skip_comment (GtkCssParser *parser)
174 {
175   if (parser->data[0] != '/' ||
176       parser->data[1] != '*')
177     return FALSE;
178
179   parser->data += 2;
180
181   while (*parser->data)
182     {
183       gsize len = strcspn (parser->data, NEWLINE_CHARS "/");
184
185       parser->data += len;
186   
187       if (gtk_css_parser_new_line (parser))
188         continue;
189
190       parser->data++;
191
192       if (parser->data[-2] == '*')
193         return TRUE;
194       if (parser->data[0] == '*')
195         _gtk_css_parser_error (parser, "'/*' in comment block");
196     }
197
198   /* FIXME: position */
199   _gtk_css_parser_error (parser, "Unterminated comment");
200   return TRUE;
201 }
202
203 void
204 _gtk_css_parser_skip_whitespace (GtkCssParser *parser)
205 {
206   size_t len;
207
208   while (*parser->data)
209     {
210       if (gtk_css_parser_new_line (parser))
211         continue;
212
213       len = strspn (parser->data, WHITESPACE_CHARS);
214       if (len)
215         {
216           parser->data += len;
217           continue;
218         }
219       
220       if (!gtk_css_parser_skip_comment (parser))
221         break;
222     }
223 }
224
225 gboolean
226 _gtk_css_parser_try (GtkCssParser *parser,
227                      const char   *string,
228                      gboolean      skip_whitespace)
229 {
230   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
231   g_return_val_if_fail (string != NULL, FALSE);
232
233   if (g_ascii_strncasecmp (parser->data, string, strlen (string)) != 0)
234     return FALSE;
235
236   parser->data += strlen (string);
237
238   if (skip_whitespace)
239     _gtk_css_parser_skip_whitespace (parser);
240   return TRUE;
241 }
242
243 static guint
244 get_xdigit (char c)
245 {
246   if (c >= 'a')
247     return c - 'a' + 10;
248   else if (c >= 'A')
249     return c - 'A' + 10;
250   else
251     return c - '0';
252 }
253
254 static void
255 _gtk_css_parser_unescape (GtkCssParser *parser,
256                           GString      *str)
257 {
258   guint i;
259   gunichar result = 0;
260
261   g_assert (*parser->data == '\\');
262
263   parser->data++;
264
265   for (i = 0; i < 6; i++)
266     {
267       if (!g_ascii_isxdigit (parser->data[i]))
268         break;
269
270       result = (result << 4) + get_xdigit (parser->data[i]);
271     }
272
273   if (i != 0)
274     {
275       g_string_append_unichar (str, result);
276       parser->data += i;
277
278       /* NB: gtk_css_parser_new_line() forward data pointer itself */
279       if (!gtk_css_parser_new_line (parser) &&
280           *parser->data &&
281           strchr (WHITESPACE_CHARS, *parser->data))
282         parser->data++;
283       return;
284     }
285
286   if (gtk_css_parser_new_line (parser))
287     return;
288
289   g_string_append_c (str, *parser->data);
290   parser->data++;
291
292   return;
293 }
294
295 static gboolean
296 _gtk_css_parser_read_char (GtkCssParser *parser,
297                            GString *     str,
298                            const char *  allowed)
299 {
300   if (*parser->data == 0)
301     return FALSE;
302
303   if (strchr (allowed, *parser->data))
304     {
305       g_string_append_c (str, *parser->data);
306       parser->data++;
307       return TRUE;
308     }
309   if (*parser->data >= 127)
310     {
311       gsize len = g_utf8_skip[(guint) *(guchar *) parser->data];
312
313       g_string_append_len (str, parser->data, len);
314       parser->data += len;
315       return TRUE;
316     }
317   if (*parser->data == '\\')
318     {
319       _gtk_css_parser_unescape (parser, str);
320       return TRUE;
321     }
322
323   return FALSE;
324 }
325
326 char *
327 _gtk_css_parser_try_name (GtkCssParser *parser,
328                           gboolean      skip_whitespace)
329 {
330   GString *name;
331
332   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
333
334   name = g_string_new (NULL);
335
336   while (_gtk_css_parser_read_char (parser, name, NMCHAR))
337     ;
338
339   if (skip_whitespace)
340     _gtk_css_parser_skip_whitespace (parser);
341
342   return g_string_free (name, FALSE);
343 }
344
345 char *
346 _gtk_css_parser_try_ident (GtkCssParser *parser,
347                            gboolean      skip_whitespace)
348 {
349   const char *start;
350   GString *ident;
351
352   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
353
354   start = parser->data;
355   
356   ident = g_string_new (NULL);
357
358   if (*parser->data == '-')
359     {
360       g_string_append_c (ident, '-');
361       parser->data++;
362     }
363
364   if (!_gtk_css_parser_read_char (parser, ident, NMSTART))
365     {
366       parser->data = start;
367       g_string_free (ident, TRUE);
368       return NULL;
369     }
370
371   while (_gtk_css_parser_read_char (parser, ident, NMCHAR))
372     ;
373
374   if (skip_whitespace)
375     _gtk_css_parser_skip_whitespace (parser);
376
377   return g_string_free (ident, FALSE);
378 }
379
380 gboolean
381 _gtk_css_parser_is_string (GtkCssParser *parser)
382 {
383   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
384
385   return *parser->data == '"' || *parser->data == '\'';
386 }
387
388 char *
389 _gtk_css_parser_read_string (GtkCssParser *parser)
390 {
391   GString *str;
392   char quote;
393
394   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
395
396   quote = *parser->data;
397   
398   if (quote != '"' && quote != '\'')
399     {
400       _gtk_css_parser_error (parser, "Expected a string.");
401       return NULL;
402     }
403   
404   parser->data++;
405   str = g_string_new (NULL);
406
407   while (TRUE)
408     {
409       gsize len = strcspn (parser->data, "\\'\"\n\r\f");
410
411       g_string_append_len (str, parser->data, len);
412
413       parser->data += len;
414
415       switch (*parser->data)
416         {
417         case '\\':
418           _gtk_css_parser_unescape (parser, str);
419           break;
420         case '"':
421         case '\'':
422           if (*parser->data == quote)
423             {
424               parser->data++;
425               _gtk_css_parser_skip_whitespace (parser);
426               return g_string_free (str, FALSE);
427             }
428           
429           g_string_append_c (str, *parser->data);
430           parser->data++;
431           break;
432         case '\0':
433           /* FIXME: position */
434           _gtk_css_parser_error (parser, "Missing end quote in string.");
435           g_string_free (str, TRUE);
436           return NULL;
437         default:
438           _gtk_css_parser_error (parser, 
439                                  "Invalid character in string. Must be escaped.");
440           g_string_free (str, TRUE);
441           return NULL;
442         }
443     }
444
445   g_assert_not_reached ();
446   return NULL;
447 }
448
449 char *
450 _gtk_css_parser_read_uri (GtkCssParser *parser)
451 {
452   char *result;
453
454   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
455
456   if (!_gtk_css_parser_try (parser, "url(", TRUE))
457     {
458       _gtk_css_parser_error (parser, "expected 'url('");
459       return NULL;
460     }
461
462   _gtk_css_parser_skip_whitespace (parser);
463
464   if (_gtk_css_parser_is_string (parser))
465     {
466       result = _gtk_css_parser_read_string (parser);
467     }
468   else
469     {
470       GString *str = g_string_new (NULL);
471
472       while (_gtk_css_parser_read_char (parser, str, URLCHAR))
473         ;
474       result = g_string_free (str, FALSE);
475       if (result == NULL)
476         _gtk_css_parser_error (parser, "not a url");
477     }
478   
479   if (result == NULL)
480     return NULL;
481
482   _gtk_css_parser_skip_whitespace (parser);
483
484   if (*parser->data != ')')
485     {
486       _gtk_css_parser_error (parser, "missing ')' for url");
487       g_free (result);
488       return NULL;
489     }
490
491   parser->data++;
492
493   _gtk_css_parser_skip_whitespace (parser);
494
495   return result;
496 }
497
498 gboolean
499 _gtk_css_parser_try_int (GtkCssParser *parser,
500                          int          *value)
501 {
502   gint64 result;
503   char *end;
504
505   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
506   g_return_val_if_fail (value != NULL, FALSE);
507
508   /* strtoll parses a plus, but we are not allowed to */
509   if (*parser->data == '+')
510     return FALSE;
511
512   errno = 0;
513   result = g_ascii_strtoll (parser->data, &end, 10);
514   if (errno)
515     return FALSE;
516   if (result > G_MAXINT || result < G_MININT)
517     return FALSE;
518   if (parser->data == end)
519     return FALSE;
520
521   parser->data = end;
522   *value = result;
523
524   _gtk_css_parser_skip_whitespace (parser);
525
526   return TRUE;
527 }
528
529 gboolean
530 _gtk_css_parser_try_uint (GtkCssParser *parser,
531                           guint        *value)
532 {
533   guint64 result;
534   char *end;
535
536   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
537   g_return_val_if_fail (value != NULL, FALSE);
538
539   errno = 0;
540   result = g_ascii_strtoull (parser->data, &end, 10);
541   if (errno)
542     return FALSE;
543   if (result > G_MAXUINT)
544     return FALSE;
545   if (parser->data == end)
546     return FALSE;
547
548   parser->data = end;
549   *value = result;
550
551   _gtk_css_parser_skip_whitespace (parser);
552
553   return TRUE;
554 }
555
556 gboolean
557 _gtk_css_parser_try_double (GtkCssParser *parser,
558                             gdouble      *value)
559 {
560   gdouble result;
561   char *end;
562
563   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
564   g_return_val_if_fail (value != NULL, FALSE);
565
566   errno = 0;
567   result = g_ascii_strtod (parser->data, &end);
568   if (errno)
569     return FALSE;
570   if (parser->data == end)
571     return FALSE;
572
573   parser->data = end;
574   *value = result;
575
576   _gtk_css_parser_skip_whitespace (parser);
577
578   return TRUE;
579 }
580
581 gboolean
582 _gtk_css_parser_try_enum (GtkCssParser *parser,
583                           GType         enum_type,
584                           int          *value)
585 {
586   GEnumClass *enum_class;
587   gboolean result;
588   const char *start;
589   char *str;
590
591   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
592   g_return_val_if_fail (value != NULL, FALSE);
593
594   result = FALSE;
595
596   enum_class = g_type_class_ref (enum_type);
597
598   start = parser->data;
599
600   str = _gtk_css_parser_try_ident (parser, TRUE);
601   if (str == NULL)
602     return FALSE;
603
604   if (enum_class->n_values)
605     {
606       GEnumValue *enum_value;
607
608       for (enum_value = enum_class->values; enum_value->value_name; enum_value++)
609         {
610           if (enum_value->value_nick &&
611               g_ascii_strcasecmp (str, enum_value->value_nick) == 0)
612             {
613               *value = enum_value->value;
614               result = TRUE;
615               break;
616             }
617         }
618     }
619
620   g_free (str);
621   g_type_class_unref (enum_class);
622
623   if (!result)
624     parser->data = start;
625
626   return result;
627 }
628
629 typedef enum {
630   COLOR_RGBA,
631   COLOR_RGB,
632   COLOR_LIGHTER,
633   COLOR_DARKER,
634   COLOR_SHADE,
635   COLOR_ALPHA,
636   COLOR_MIX,
637   COLOR_WIN32
638 } ColorType;
639
640 static GtkSymbolicColor *
641 gtk_css_parser_read_symbolic_color_function (GtkCssParser *parser,
642                                              ColorType     color)
643 {
644   GtkSymbolicColor *symbolic;
645   GtkSymbolicColor *child1, *child2;
646   double value;
647
648   if (!_gtk_css_parser_try (parser, "(", TRUE))
649     {
650       _gtk_css_parser_error (parser, "Missing opening bracket in color definition");
651       return NULL;
652     }
653
654   if (color == COLOR_RGB || color == COLOR_RGBA)
655     {
656       GdkRGBA rgba;
657       double tmp;
658       guint i;
659
660       for (i = 0; i < 3; i++)
661         {
662           if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
663             {
664               _gtk_css_parser_error (parser, "Expected ',' in color definition");
665               return NULL;
666             }
667
668           if (!_gtk_css_parser_try_double (parser, &tmp))
669             {
670               _gtk_css_parser_error (parser, "Invalid number for color value");
671               return NULL;
672             }
673           if (_gtk_css_parser_try (parser, "%", TRUE))
674             tmp /= 100.0;
675           else
676             tmp /= 255.0;
677           if (i == 0)
678             rgba.red = tmp;
679           else if (i == 1)
680             rgba.green = tmp;
681           else if (i == 2)
682             rgba.blue = tmp;
683           else
684             g_assert_not_reached ();
685         }
686
687       if (color == COLOR_RGBA)
688         {
689           if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
690             {
691               _gtk_css_parser_error (parser, "Expected ',' in color definition");
692               return NULL;
693             }
694
695           if (!_gtk_css_parser_try_double (parser, &rgba.alpha))
696             {
697               _gtk_css_parser_error (parser, "Invalid number for alpha value");
698               return NULL;
699             }
700         }
701       else
702         rgba.alpha = 1.0;
703       
704       symbolic = gtk_symbolic_color_new_literal (&rgba);
705     }
706   else if (color == COLOR_WIN32)
707     {
708       symbolic = _gtk_win32_theme_color_parse (parser);
709       if (symbolic == NULL)
710         return NULL;
711     }
712   else
713     {
714       child1 = _gtk_css_parser_read_symbolic_color (parser);
715       if (child1 == NULL)
716         return NULL;
717
718       if (color == COLOR_MIX)
719         {
720           if (!_gtk_css_parser_try (parser, ",", TRUE))
721             {
722               _gtk_css_parser_error (parser, "Expected ',' in color definition");
723               gtk_symbolic_color_unref (child1);
724               return NULL;
725             }
726
727           child2 = _gtk_css_parser_read_symbolic_color (parser);
728           if (child2 == NULL)
729             {
730               gtk_symbolic_color_unref (child1);
731               return NULL;
732             }
733         }
734       else
735         child2 = NULL;
736
737       if (color == COLOR_LIGHTER)
738         value = 1.3;
739       else if (color == COLOR_DARKER)
740         value = 0.7;
741       else
742         {
743           if (!_gtk_css_parser_try (parser, ",", TRUE))
744             {
745               _gtk_css_parser_error (parser, "Expected ',' in color definition");
746               gtk_symbolic_color_unref (child1);
747               if (child2)
748                 gtk_symbolic_color_unref (child2);
749               return NULL;
750             }
751
752           if (!_gtk_css_parser_try_double (parser, &value))
753             {
754               _gtk_css_parser_error (parser, "Expected number in color definition");
755               gtk_symbolic_color_unref (child1);
756               if (child2)
757                 gtk_symbolic_color_unref (child2);
758               return NULL;
759             }
760         }
761       
762       switch (color)
763         {
764         case COLOR_LIGHTER:
765         case COLOR_DARKER:
766         case COLOR_SHADE:
767           symbolic = gtk_symbolic_color_new_shade (child1, value);
768           break;
769         case COLOR_ALPHA:
770           symbolic = gtk_symbolic_color_new_alpha (child1, value);
771           break;
772         case COLOR_MIX:
773           symbolic = gtk_symbolic_color_new_mix (child1, child2, value);
774           break;
775         default:
776           g_assert_not_reached ();
777           symbolic = NULL;
778         }
779
780       gtk_symbolic_color_unref (child1);
781       if (child2)
782         gtk_symbolic_color_unref (child2);
783     }
784
785   if (!_gtk_css_parser_try (parser, ")", TRUE))
786     {
787       _gtk_css_parser_error (parser, "Expected ')' in color definition");
788       gtk_symbolic_color_unref (symbolic);
789       return NULL;
790     }
791
792   return symbolic;
793 }
794
795 static GtkSymbolicColor *
796 gtk_css_parser_try_hash_color (GtkCssParser *parser)
797 {
798   if (parser->data[0] == '#' &&
799       g_ascii_isxdigit (parser->data[1]) &&
800       g_ascii_isxdigit (parser->data[2]) &&
801       g_ascii_isxdigit (parser->data[3]))
802     {
803       GdkRGBA rgba;
804       
805       if (g_ascii_isxdigit (parser->data[4]) &&
806           g_ascii_isxdigit (parser->data[5]) &&
807           g_ascii_isxdigit (parser->data[6]))
808         {
809           rgba.red   = ((get_xdigit (parser->data[1]) << 4) + get_xdigit (parser->data[2])) / 255.0;
810           rgba.green = ((get_xdigit (parser->data[3]) << 4) + get_xdigit (parser->data[4])) / 255.0;
811           rgba.blue  = ((get_xdigit (parser->data[5]) << 4) + get_xdigit (parser->data[6])) / 255.0;
812           rgba.alpha = 1.0;
813           parser->data += 7;
814         }
815       else
816         {
817           rgba.red   = get_xdigit (parser->data[1]) / 15.0;
818           rgba.green = get_xdigit (parser->data[2]) / 15.0;
819           rgba.blue  = get_xdigit (parser->data[3]) / 15.0;
820           rgba.alpha = 1.0;
821           parser->data += 4;
822         }
823
824       _gtk_css_parser_skip_whitespace (parser);
825
826       return gtk_symbolic_color_new_literal (&rgba);
827     }
828
829   return NULL;
830 }
831
832 GtkSymbolicColor *
833 _gtk_css_parser_read_symbolic_color (GtkCssParser *parser)
834 {
835   GtkSymbolicColor *symbolic;
836   guint color;
837   const char *names[] = {"rgba", "rgb",  "lighter", "darker", "shade", "alpha", "mix",
838                          GTK_WIN32_THEME_SYMBOLIC_COLOR_NAME};
839   char *name;
840
841   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
842
843   if (_gtk_css_parser_try (parser, "transparent", TRUE))
844     {
845       GdkRGBA transparent = { 0, 0, 0, 0 };
846       
847       return gtk_symbolic_color_new_literal (&transparent);
848     }
849
850   if (_gtk_css_parser_try (parser, "@", FALSE))
851     {
852       name = _gtk_css_parser_try_name (parser, TRUE);
853
854       if (name)
855         {
856           symbolic = gtk_symbolic_color_new_name (name);
857         }
858       else
859         {
860           _gtk_css_parser_error (parser, "'%s' is not a valid symbolic color name", name);
861           symbolic = NULL;
862         }
863
864       g_free (name);
865       return symbolic;
866     }
867
868   for (color = 0; color < G_N_ELEMENTS (names); color++)
869     {
870       if (_gtk_css_parser_try (parser, names[color], TRUE))
871         break;
872     }
873
874   if (color < G_N_ELEMENTS (names))
875     return gtk_css_parser_read_symbolic_color_function (parser, color);
876
877   symbolic = gtk_css_parser_try_hash_color (parser);
878   if (symbolic)
879     return symbolic;
880
881   name = _gtk_css_parser_try_name (parser, TRUE);
882   if (name)
883     {
884       GdkRGBA rgba;
885
886       if (gdk_rgba_parse (&rgba, name))
887         {
888           symbolic = gtk_symbolic_color_new_literal (&rgba);
889         }
890       else
891         {
892           _gtk_css_parser_error (parser, "'%s' is not a valid color name", name);
893           symbolic = NULL;
894         }
895       g_free (name);
896       return symbolic;
897     }
898
899   _gtk_css_parser_error (parser, "Not a color definition");
900   return NULL;
901 }
902
903 void
904 _gtk_css_parser_resync_internal (GtkCssParser *parser,
905                                  gboolean      sync_at_semicolon,
906                                  gboolean      read_sync_token,
907                                  char          terminator)
908 {
909   gsize len;
910
911   do {
912     len = strcspn (parser->data, "\\\"'/()[]{};" NEWLINE_CHARS);
913     parser->data += len;
914
915     if (gtk_css_parser_new_line (parser))
916       continue;
917
918     if (_gtk_css_parser_is_string (parser))
919       {
920         /* Hrm, this emits errors, and i suspect it shouldn't... */
921         char *free_me = _gtk_css_parser_read_string (parser);
922         g_free (free_me);
923         continue;
924       }
925
926     if (gtk_css_parser_skip_comment (parser))
927       continue;
928
929     switch (*parser->data)
930       {
931       case '\\':
932         {
933           GString *ignore = g_string_new (NULL);
934           _gtk_css_parser_unescape (parser, ignore);
935           g_string_free (ignore, TRUE);
936         }
937         break;
938       case ';':
939         if (sync_at_semicolon && !read_sync_token)
940           return;
941         parser->data++;
942         if (sync_at_semicolon)
943           {
944             _gtk_css_parser_skip_whitespace (parser);
945             return;
946           }
947         break;
948       case '(':
949         parser->data++;
950         _gtk_css_parser_resync (parser, FALSE, ')');
951         if (*parser->data)
952           parser->data++;
953         break;
954       case '[':
955         parser->data++;
956         _gtk_css_parser_resync (parser, FALSE, ']');
957         if (*parser->data)
958           parser->data++;
959         break;
960       case '{':
961         parser->data++;
962         _gtk_css_parser_resync (parser, FALSE, '}');
963         if (*parser->data)
964           parser->data++;
965         if (sync_at_semicolon || !terminator)
966           {
967             _gtk_css_parser_skip_whitespace (parser);
968             return;
969           }
970         break;
971       case '}':
972       case ')':
973       case ']':
974         if (terminator == *parser->data)
975           {
976             _gtk_css_parser_skip_whitespace (parser);
977             return;
978           }
979         parser->data++;
980         continue;
981       case '\0':
982         break;
983       case '/':
984       default:
985         parser->data++;
986         break;
987       }
988   } while (*parser->data);
989 }
990
991 char *
992 _gtk_css_parser_read_value (GtkCssParser *parser)
993 {
994   const char *start;
995   char *result;
996
997   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
998
999   start = parser->data;
1000
1001   /* This needs to be done better */
1002   _gtk_css_parser_resync_internal (parser, TRUE, FALSE, '}');
1003
1004   result = g_strndup (start, parser->data - start);
1005   if (result)
1006     {
1007       g_strchomp (result);
1008       if (result[0] == 0)
1009         {
1010           g_free (result);
1011           result = NULL;
1012         }
1013     }
1014
1015   if (result == NULL)
1016     _gtk_css_parser_error (parser, "Expected a property value");
1017
1018   return result;
1019 }
1020
1021 void
1022 _gtk_css_parser_resync (GtkCssParser *parser,
1023                         gboolean      sync_at_semicolon,
1024                         char          terminator)
1025 {
1026   g_return_if_fail (GTK_IS_CSS_PARSER (parser));
1027
1028   _gtk_css_parser_resync_internal (parser, sync_at_semicolon, TRUE, terminator);
1029 }