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