]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssparser.c
Add support for win32 theme colors as symbolic colors
[~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 typedef enum {
573   COLOR_RGBA,
574   COLOR_RGB,
575   COLOR_LIGHTER,
576   COLOR_DARKER,
577   COLOR_SHADE,
578   COLOR_ALPHA,
579   COLOR_MIX,
580   COLOR_WIN32
581 } ColorType;
582
583 static GtkSymbolicColor *
584 gtk_css_parser_read_symbolic_color_function (GtkCssParser *parser,
585                                              ColorType     color)
586 {
587   GtkSymbolicColor *symbolic;
588   GtkSymbolicColor *child1, *child2;
589   double value;
590
591   if (!_gtk_css_parser_try (parser, "(", TRUE))
592     {
593       _gtk_css_parser_error (parser, "Missing opening bracket in color definition");
594       return NULL;
595     }
596
597   if (color == COLOR_RGB || color == COLOR_RGBA)
598     {
599       GdkRGBA rgba;
600       double tmp;
601       guint i;
602
603       for (i = 0; i < 3; i++)
604         {
605           if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
606             {
607               _gtk_css_parser_error (parser, "Expected ',' in color definition");
608               return NULL;
609             }
610
611           if (!_gtk_css_parser_try_double (parser, &tmp))
612             {
613               _gtk_css_parser_error (parser, "Invalid number for color value");
614               return NULL;
615             }
616           if (_gtk_css_parser_try (parser, "%", TRUE))
617             tmp /= 100.0;
618           else
619             tmp /= 255.0;
620           if (i == 0)
621             rgba.red = tmp;
622           else if (i == 1)
623             rgba.green = tmp;
624           else if (i == 2)
625             rgba.blue = tmp;
626           else
627             g_assert_not_reached ();
628         }
629
630       if (color == COLOR_RGBA)
631         {
632           if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
633             {
634               _gtk_css_parser_error (parser, "Expected ',' in color definition");
635               return NULL;
636             }
637
638           if (!_gtk_css_parser_try_double (parser, &rgba.alpha))
639             {
640               _gtk_css_parser_error (parser, "Invalid number for alpha value");
641               return NULL;
642             }
643         }
644       else
645         rgba.alpha = 1.0;
646       
647       symbolic = gtk_symbolic_color_new_literal (&rgba);
648     }
649   else if (color == COLOR_WIN32)
650     {
651       symbolic = _gtk_win32_theme_color_parse (parser);
652       if (symbolic == NULL)
653         return NULL;
654     }
655   else
656     {
657       child1 = _gtk_css_parser_read_symbolic_color (parser);
658       if (child1 == NULL)
659         return NULL;
660
661       if (color == COLOR_MIX)
662         {
663           if (!_gtk_css_parser_try (parser, ",", TRUE))
664             {
665               _gtk_css_parser_error (parser, "Expected ',' in color definition");
666               gtk_symbolic_color_unref (child1);
667               return NULL;
668             }
669
670           child2 = _gtk_css_parser_read_symbolic_color (parser);
671           if (child2 == NULL)
672             {
673               gtk_symbolic_color_unref (child1);
674               return NULL;
675             }
676         }
677       else
678         child2 = NULL;
679
680       if (color == COLOR_LIGHTER)
681         value = 1.3;
682       else if (color == COLOR_DARKER)
683         value = 0.7;
684       else
685         {
686           if (!_gtk_css_parser_try (parser, ",", TRUE))
687             {
688               _gtk_css_parser_error (parser, "Expected ',' in color definition");
689               gtk_symbolic_color_unref (child1);
690               if (child2)
691                 gtk_symbolic_color_unref (child2);
692               return NULL;
693             }
694
695           if (!_gtk_css_parser_try_double (parser, &value))
696             {
697               _gtk_css_parser_error (parser, "Expected number in color definition");
698               gtk_symbolic_color_unref (child1);
699               if (child2)
700                 gtk_symbolic_color_unref (child2);
701               return NULL;
702             }
703         }
704       
705       switch (color)
706         {
707         case COLOR_LIGHTER:
708         case COLOR_DARKER:
709         case COLOR_SHADE:
710           symbolic = gtk_symbolic_color_new_shade (child1, value);
711           break;
712         case COLOR_ALPHA:
713           symbolic = gtk_symbolic_color_new_alpha (child1, value);
714           break;
715         case COLOR_MIX:
716           symbolic = gtk_symbolic_color_new_mix (child1, child2, value);
717           break;
718         default:
719           g_assert_not_reached ();
720           symbolic = NULL;
721         }
722
723       gtk_symbolic_color_unref (child1);
724       if (child2)
725         gtk_symbolic_color_unref (child2);
726     }
727
728   if (!_gtk_css_parser_try (parser, ")", TRUE))
729     {
730       _gtk_css_parser_error (parser, "Expected ')' in color definition");
731       gtk_symbolic_color_unref (symbolic);
732       return NULL;
733     }
734
735   return symbolic;
736 }
737
738 static GtkSymbolicColor *
739 gtk_css_parser_try_hash_color (GtkCssParser *parser)
740 {
741   if (parser->data[0] == '#' &&
742       g_ascii_isxdigit (parser->data[1]) &&
743       g_ascii_isxdigit (parser->data[2]) &&
744       g_ascii_isxdigit (parser->data[3]))
745     {
746       GdkRGBA rgba;
747       
748       if (g_ascii_isxdigit (parser->data[4]) &&
749           g_ascii_isxdigit (parser->data[5]) &&
750           g_ascii_isxdigit (parser->data[6]))
751         {
752           rgba.red   = ((get_xdigit (parser->data[1]) << 4) + get_xdigit (parser->data[2])) / 255.0;
753           rgba.green = ((get_xdigit (parser->data[3]) << 4) + get_xdigit (parser->data[4])) / 255.0;
754           rgba.blue  = ((get_xdigit (parser->data[5]) << 4) + get_xdigit (parser->data[6])) / 255.0;
755           rgba.alpha = 1.0;
756           parser->data += 7;
757         }
758       else
759         {
760           rgba.red   = get_xdigit (parser->data[1]) / 15.0;
761           rgba.green = get_xdigit (parser->data[2]) / 15.0;
762           rgba.blue  = get_xdigit (parser->data[3]) / 15.0;
763           rgba.alpha = 1.0;
764           parser->data += 4;
765         }
766
767       _gtk_css_parser_skip_whitespace (parser);
768
769       return gtk_symbolic_color_new_literal (&rgba);
770     }
771
772   return NULL;
773 }
774
775 GtkSymbolicColor *
776 _gtk_css_parser_read_symbolic_color (GtkCssParser *parser)
777 {
778   GtkSymbolicColor *symbolic;
779   guint color;
780   const char *names[] = {"rgba", "rgb",  "lighter", "darker", "shade", "alpha", "mix",
781                          GTK_WIN32_THEME_SYMBOLIC_COLOR_NAME};
782   char *name;
783
784   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
785
786   if (_gtk_css_parser_try (parser, "@", FALSE))
787     {
788       name = _gtk_css_parser_try_name (parser, TRUE);
789
790       if (name)
791         {
792           symbolic = gtk_symbolic_color_new_name (name);
793         }
794       else
795         {
796           _gtk_css_parser_error (parser, "'%s' is not a valid symbolic color name", name);
797           symbolic = NULL;
798         }
799
800       g_free (name);
801       return symbolic;
802     }
803
804   for (color = 0; color < G_N_ELEMENTS (names); color++)
805     {
806       if (_gtk_css_parser_try (parser, names[color], TRUE))
807         break;
808     }
809
810   if (color < G_N_ELEMENTS (names))
811     return gtk_css_parser_read_symbolic_color_function (parser, color);
812
813   symbolic = gtk_css_parser_try_hash_color (parser);
814   if (symbolic)
815     return symbolic;
816
817   name = _gtk_css_parser_try_name (parser, TRUE);
818   if (name)
819     {
820       GdkRGBA rgba;
821
822       if (gdk_rgba_parse (&rgba, name))
823         {
824           symbolic = gtk_symbolic_color_new_literal (&rgba);
825         }
826       else
827         {
828           _gtk_css_parser_error (parser, "'%s' is not a valid color name", name);
829           symbolic = NULL;
830         }
831       g_free (name);
832       return symbolic;
833     }
834
835   _gtk_css_parser_error (parser, "Not a color definition");
836   return NULL;
837 }
838
839 void
840 _gtk_css_parser_resync_internal (GtkCssParser *parser,
841                                  gboolean      sync_at_semicolon,
842                                  gboolean      read_sync_token,
843                                  char          terminator)
844 {
845   gsize len;
846
847   do {
848     len = strcspn (parser->data, "\\\"'/()[]{};" NEWLINE_CHARS);
849     parser->data += len;
850
851     if (gtk_css_parser_new_line (parser))
852       continue;
853
854     if (_gtk_css_parser_is_string (parser))
855       {
856         /* Hrm, this emits errors, and i suspect it shouldn't... */
857         char *free_me = _gtk_css_parser_read_string (parser);
858         g_free (free_me);
859         continue;
860       }
861
862     if (gtk_css_parser_skip_comment (parser))
863       continue;
864
865     switch (*parser->data)
866       {
867       case '\\':
868         {
869           GString *ignore = g_string_new (NULL);
870           _gtk_css_parser_unescape (parser, ignore);
871           g_string_free (ignore, TRUE);
872         }
873         break;
874       case ';':
875         if (sync_at_semicolon && !read_sync_token)
876           return;
877         parser->data++;
878         if (sync_at_semicolon)
879           {
880             _gtk_css_parser_skip_whitespace (parser);
881             return;
882           }
883         break;
884       case '(':
885         parser->data++;
886         _gtk_css_parser_resync (parser, FALSE, ')');
887         if (*parser->data)
888           parser->data++;
889         break;
890       case '[':
891         parser->data++;
892         _gtk_css_parser_resync (parser, FALSE, ']');
893         if (*parser->data)
894           parser->data++;
895         break;
896       case '{':
897         parser->data++;
898         _gtk_css_parser_resync (parser, FALSE, '}');
899         if (*parser->data)
900           parser->data++;
901         if (sync_at_semicolon || !terminator)
902           {
903             _gtk_css_parser_skip_whitespace (parser);
904             return;
905           }
906         break;
907       case '}':
908       case ')':
909       case ']':
910         if (terminator == *parser->data)
911           {
912             _gtk_css_parser_skip_whitespace (parser);
913             return;
914           }
915         parser->data++;
916         continue;
917       case '\0':
918         break;
919       case '/':
920       default:
921         parser->data++;
922         break;
923       }
924   } while (*parser->data);
925 }
926
927 char *
928 _gtk_css_parser_read_value (GtkCssParser *parser)
929 {
930   const char *start;
931   char *result;
932
933   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
934
935   start = parser->data;
936
937   /* This needs to be done better */
938   _gtk_css_parser_resync_internal (parser, TRUE, FALSE, '}');
939
940   result = g_strndup (start, parser->data - start);
941   if (result)
942     {
943       g_strchomp (result);
944       if (result[0] == 0)
945         {
946           g_free (result);
947           result = NULL;
948         }
949     }
950
951   if (result == NULL)
952     _gtk_css_parser_error (parser, "Expected a property value");
953
954   return result;
955 }
956
957 void
958 _gtk_css_parser_resync (GtkCssParser *parser,
959                         gboolean      sync_at_semicolon,
960                         char          terminator)
961 {
962   g_return_if_fail (GTK_IS_CSS_PARSER (parser));
963
964   _gtk_css_parser_resync_internal (parser, sync_at_semicolon, TRUE, terminator);
965 }