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