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