]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssparser.c
Merge branch 'bgo593793-filechooser-recent-folders-master'
[~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     {
390       _gtk_css_parser_error (parser, "Expected a string.");
391       return NULL;
392     }
393   
394   parser->data++;
395   str = g_string_new (NULL);
396
397   while (TRUE)
398     {
399       gsize len = strcspn (parser->data, "\\'\"\n\r\f");
400
401       g_string_append_len (str, parser->data, len);
402
403       parser->data += len;
404
405       switch (*parser->data)
406         {
407         case '\\':
408           _gtk_css_parser_unescape (parser, str);
409           break;
410         case '"':
411         case '\'':
412           if (*parser->data == quote)
413             {
414               parser->data++;
415               _gtk_css_parser_skip_whitespace (parser);
416               return g_string_free (str, FALSE);
417             }
418           
419           g_string_append_c (str, *parser->data);
420           parser->data++;
421           break;
422         case '\0':
423           /* FIXME: position */
424           _gtk_css_parser_error (parser, "Missing end quote in string.");
425           g_string_free (str, TRUE);
426           return NULL;
427         default:
428           _gtk_css_parser_error (parser, 
429                                  "Invalid character in string. Must be escaped.");
430           g_string_free (str, TRUE);
431           return NULL;
432         }
433     }
434
435   g_assert_not_reached ();
436   return NULL;
437 }
438
439 char *
440 _gtk_css_parser_read_uri (GtkCssParser *parser)
441 {
442   char *result;
443
444   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
445
446   if (!_gtk_css_parser_try (parser, "url(", TRUE))
447     {
448       _gtk_css_parser_error (parser, "expected 'url('");
449       return NULL;
450     }
451
452   _gtk_css_parser_skip_whitespace (parser);
453
454   if (_gtk_css_parser_is_string (parser))
455     {
456       result = _gtk_css_parser_read_string (parser);
457     }
458   else
459     {
460       GString *str = g_string_new (NULL);
461
462       while (_gtk_css_parser_read_char (parser, str, URLCHAR))
463         ;
464       result = g_string_free (str, FALSE);
465       if (result == NULL)
466         _gtk_css_parser_error (parser, "not a url");
467     }
468   
469   if (result == NULL)
470     return NULL;
471
472   _gtk_css_parser_skip_whitespace (parser);
473
474   if (*parser->data != ')')
475     {
476       _gtk_css_parser_error (parser, "missing ')' for url");
477       g_free (result);
478       return NULL;
479     }
480
481   parser->data++;
482
483   _gtk_css_parser_skip_whitespace (parser);
484
485   return result;
486 }
487
488 gboolean
489 _gtk_css_parser_try_int (GtkCssParser *parser,
490                          int          *value)
491 {
492   gint64 result;
493   char *end;
494
495   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
496   g_return_val_if_fail (value != NULL, FALSE);
497
498   /* strtoll parses a plus, but we are not allowed to */
499   if (*parser->data == '+')
500     return FALSE;
501
502   errno = 0;
503   result = g_ascii_strtoll (parser->data, &end, 10);
504   if (errno)
505     return FALSE;
506   if (result > G_MAXINT || result < G_MININT)
507     return FALSE;
508   if (parser->data == end)
509     return FALSE;
510
511   parser->data = end;
512   *value = result;
513
514   _gtk_css_parser_skip_whitespace (parser);
515
516   return TRUE;
517 }
518
519 gboolean
520 _gtk_css_parser_try_uint (GtkCssParser *parser,
521                           guint        *value)
522 {
523   guint64 result;
524   char *end;
525
526   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
527   g_return_val_if_fail (value != NULL, FALSE);
528
529   errno = 0;
530   result = g_ascii_strtoull (parser->data, &end, 10);
531   if (errno)
532     return FALSE;
533   if (result > G_MAXUINT)
534     return FALSE;
535   if (parser->data == end)
536     return FALSE;
537
538   parser->data = end;
539   *value = result;
540
541   _gtk_css_parser_skip_whitespace (parser);
542
543   return TRUE;
544 }
545
546 gboolean
547 _gtk_css_parser_try_double (GtkCssParser *parser,
548                             gdouble      *value)
549 {
550   gdouble result;
551   char *end;
552
553   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
554   g_return_val_if_fail (value != NULL, FALSE);
555
556   errno = 0;
557   result = g_ascii_strtod (parser->data, &end);
558   if (errno)
559     return FALSE;
560   if (parser->data == end)
561     return FALSE;
562
563   parser->data = end;
564   *value = result;
565
566   _gtk_css_parser_skip_whitespace (parser);
567
568   return TRUE;
569 }
570
571 typedef enum {
572   COLOR_RGBA,
573   COLOR_RGB,
574   COLOR_LIGHTER,
575   COLOR_DARKER,
576   COLOR_SHADE,
577   COLOR_ALPHA,
578   COLOR_MIX
579 } ColorType;
580
581 static GtkSymbolicColor *
582 gtk_css_parser_read_symbolic_color_function (GtkCssParser *parser,
583                                              ColorType     color)
584 {
585   GtkSymbolicColor *symbolic;
586   GtkSymbolicColor *child1, *child2;
587   double value;
588
589   if (!_gtk_css_parser_try (parser, "(", TRUE))
590     {
591       _gtk_css_parser_error (parser, "Missing opening bracket in color definition");
592       return NULL;
593     }
594
595   if (color == COLOR_RGB || color == COLOR_RGBA)
596     {
597       GdkRGBA rgba;
598       double tmp;
599       guint i;
600
601       for (i = 0; i < 3; i++)
602         {
603           if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
604             {
605               _gtk_css_parser_error (parser, "Expected ',' in color definition");
606               return NULL;
607             }
608
609           if (!_gtk_css_parser_try_double (parser, &tmp))
610             {
611               _gtk_css_parser_error (parser, "Invalid number for color value");
612               return NULL;
613             }
614           if (_gtk_css_parser_try (parser, "%", TRUE))
615             tmp /= 100.0;
616           else
617             tmp /= 255.0;
618           if (i == 0)
619             rgba.red = tmp;
620           else if (i == 1)
621             rgba.green = tmp;
622           else if (i == 2)
623             rgba.blue = tmp;
624           else
625             g_assert_not_reached ();
626         }
627
628       if (color == COLOR_RGBA)
629         {
630           if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
631             {
632               _gtk_css_parser_error (parser, "Expected ',' in color definition");
633               return NULL;
634             }
635
636           if (!_gtk_css_parser_try_double (parser, &rgba.alpha))
637             {
638               _gtk_css_parser_error (parser, "Invalid number for alpha value");
639               return NULL;
640             }
641         }
642       else
643         rgba.alpha = 1.0;
644       
645       symbolic = gtk_symbolic_color_new_literal (&rgba);
646     }
647   else
648     {
649       child1 = _gtk_css_parser_read_symbolic_color (parser);
650       if (child1 == NULL)
651         return NULL;
652
653       if (color == COLOR_MIX)
654         {
655           if (!_gtk_css_parser_try (parser, ",", TRUE))
656             {
657               _gtk_css_parser_error (parser, "Expected ',' in color definition");
658               gtk_symbolic_color_unref (child1);
659               return NULL;
660             }
661
662           child2 = _gtk_css_parser_read_symbolic_color (parser);
663           if (child2 == NULL)
664             {
665               gtk_symbolic_color_unref (child1);
666               return NULL;
667             }
668         }
669       else
670         child2 = NULL;
671
672       if (color == COLOR_LIGHTER)
673         value = 1.3;
674       else if (color == COLOR_DARKER)
675         value = 0.7;
676       else
677         {
678           if (!_gtk_css_parser_try (parser, ",", TRUE))
679             {
680               _gtk_css_parser_error (parser, "Expected ',' in color definition");
681               gtk_symbolic_color_unref (child1);
682               if (child2)
683                 gtk_symbolic_color_unref (child2);
684               return NULL;
685             }
686
687           if (!_gtk_css_parser_try_double (parser, &value))
688             {
689               _gtk_css_parser_error (parser, "Expected number in color definition");
690               gtk_symbolic_color_unref (child1);
691               if (child2)
692                 gtk_symbolic_color_unref (child2);
693               return NULL;
694             }
695         }
696       
697       switch (color)
698         {
699         case COLOR_LIGHTER:
700         case COLOR_DARKER:
701         case COLOR_SHADE:
702           symbolic = gtk_symbolic_color_new_shade (child1, value);
703           break;
704         case COLOR_ALPHA:
705           symbolic = gtk_symbolic_color_new_alpha (child1, value);
706           break;
707         case COLOR_MIX:
708           symbolic = gtk_symbolic_color_new_mix (child1, child2, value);
709           break;
710         default:
711           g_assert_not_reached ();
712           symbolic = NULL;
713         }
714
715       gtk_symbolic_color_unref (child1);
716       if (child2)
717         gtk_symbolic_color_unref (child2);
718     }
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 (symbolic);
724       return NULL;
725     }
726
727   return symbolic;
728 }
729
730 static GtkSymbolicColor *
731 gtk_css_parser_try_hash_color (GtkCssParser *parser)
732 {
733   if (parser->data[0] == '#' &&
734       g_ascii_isxdigit (parser->data[1]) &&
735       g_ascii_isxdigit (parser->data[2]) &&
736       g_ascii_isxdigit (parser->data[3]))
737     {
738       GdkRGBA rgba;
739       
740       if (g_ascii_isxdigit (parser->data[4]) &&
741           g_ascii_isxdigit (parser->data[5]) &&
742           g_ascii_isxdigit (parser->data[6]))
743         {
744           rgba.red   = ((get_xdigit (parser->data[1]) << 4) + get_xdigit (parser->data[2])) / 255.0;
745           rgba.green = ((get_xdigit (parser->data[3]) << 4) + get_xdigit (parser->data[4])) / 255.0;
746           rgba.blue  = ((get_xdigit (parser->data[5]) << 4) + get_xdigit (parser->data[6])) / 255.0;
747           rgba.alpha = 1.0;
748           parser->data += 7;
749         }
750       else
751         {
752           rgba.red   = get_xdigit (parser->data[1]) / 15.0;
753           rgba.green = get_xdigit (parser->data[2]) / 15.0;
754           rgba.blue  = get_xdigit (parser->data[3]) / 15.0;
755           rgba.alpha = 1.0;
756           parser->data += 4;
757         }
758
759       _gtk_css_parser_skip_whitespace (parser);
760
761       return gtk_symbolic_color_new_literal (&rgba);
762     }
763
764   return NULL;
765 }
766
767 GtkSymbolicColor *
768 _gtk_css_parser_read_symbolic_color (GtkCssParser *parser)
769 {
770   GtkSymbolicColor *symbolic;
771   guint color;
772   const char *names[] = {"rgba", "rgb",  "lighter", "darker", "shade", "alpha", "mix" };
773   char *name;
774
775   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
776
777   if (_gtk_css_parser_try (parser, "@", FALSE))
778     {
779       name = _gtk_css_parser_try_name (parser, TRUE);
780
781       if (name)
782         {
783           symbolic = gtk_symbolic_color_new_name (name);
784         }
785       else
786         {
787           _gtk_css_parser_error (parser, "'%s' is not a valid symbolic color name", name);
788           symbolic = NULL;
789         }
790
791       g_free (name);
792       return symbolic;
793     }
794
795   for (color = 0; color < G_N_ELEMENTS (names); color++)
796     {
797       if (_gtk_css_parser_try (parser, names[color], TRUE))
798         break;
799     }
800
801   if (color < G_N_ELEMENTS (names))
802     return gtk_css_parser_read_symbolic_color_function (parser, color);
803
804   symbolic = gtk_css_parser_try_hash_color (parser);
805   if (symbolic)
806     return symbolic;
807
808   name = _gtk_css_parser_try_name (parser, TRUE);
809   if (name)
810     {
811       GdkRGBA rgba;
812
813       if (gdk_rgba_parse (&rgba, name))
814         {
815           symbolic = gtk_symbolic_color_new_literal (&rgba);
816         }
817       else
818         {
819           _gtk_css_parser_error (parser, "'%s' is not a valid color name", name);
820           symbolic = NULL;
821         }
822       g_free (name);
823       return symbolic;
824     }
825
826   _gtk_css_parser_error (parser, "Not a color definition");
827   return NULL;
828 }
829
830 void
831 _gtk_css_parser_resync_internal (GtkCssParser *parser,
832                                  gboolean      sync_at_semicolon,
833                                  gboolean      read_sync_token,
834                                  char          terminator)
835 {
836   gsize len;
837
838   do {
839     len = strcspn (parser->data, "\\\"'/()[]{};" NEWLINE_CHARS);
840     parser->data += len;
841
842     if (gtk_css_parser_new_line (parser))
843       continue;
844
845     if (_gtk_css_parser_is_string (parser))
846       {
847         /* Hrm, this emits errors, and i suspect it shouldn't... */
848         char *free_me = _gtk_css_parser_read_string (parser);
849         g_free (free_me);
850         continue;
851       }
852
853     if (gtk_css_parser_skip_comment (parser))
854       continue;
855
856     switch (*parser->data)
857       {
858       case '\\':
859         {
860           GString *ignore = g_string_new (NULL);
861           _gtk_css_parser_unescape (parser, ignore);
862           g_string_free (ignore, TRUE);
863         }
864         break;
865       case ';':
866         if (sync_at_semicolon && !read_sync_token)
867           return;
868         parser->data++;
869         if (sync_at_semicolon)
870           {
871             _gtk_css_parser_skip_whitespace (parser);
872             return;
873           }
874         break;
875       case '(':
876         parser->data++;
877         _gtk_css_parser_resync (parser, FALSE, ')');
878         parser->data++;
879         break;
880       case '[':
881         parser->data++;
882         _gtk_css_parser_resync (parser, FALSE, ']');
883         parser->data++;
884         break;
885       case '{':
886         parser->data++;
887         _gtk_css_parser_resync (parser, FALSE, '}');
888         parser->data++;
889         if (sync_at_semicolon || !terminator)
890           {
891             _gtk_css_parser_skip_whitespace (parser);
892             return;
893           }
894         break;
895       case '}':
896       case ')':
897       case ']':
898         if (terminator == *parser->data)
899           {
900             _gtk_css_parser_skip_whitespace (parser);
901             return;
902           }
903         parser->data++;
904         continue;
905       case '/':
906       default:
907         parser->data++;
908         break;
909       }
910   } while (*parser->data);
911 }
912
913 char *
914 _gtk_css_parser_read_value (GtkCssParser *parser)
915 {
916   const char *start;
917   char *result;
918
919   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
920
921   start = parser->data;
922
923   /* This needs to be done better */
924   _gtk_css_parser_resync_internal (parser, TRUE, FALSE, '}');
925
926   result = g_strndup (start, parser->data - start);
927   if (result)
928     {
929       g_strchomp (result);
930       if (result[0] == 0)
931         {
932           g_free (result);
933           result = NULL;
934         }
935     }
936
937   if (result == NULL)
938     _gtk_css_parser_error (parser, "Expected a property value");
939
940   return result;
941 }
942
943 void
944 _gtk_css_parser_resync (GtkCssParser *parser,
945                         gboolean      sync_at_semicolon,
946                         char          terminator)
947 {
948   g_return_if_fail (GTK_IS_CSS_PARSER (parser));
949
950   _gtk_css_parser_resync_internal (parser, sync_at_semicolon, TRUE, terminator);
951 }