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