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