]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssparser.c
cssparser: Use _gtk_css_parser_error_full()
[~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   };
577   char *end, *unit;
578
579   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
580   g_return_val_if_fail (number != NULL, FALSE);
581
582   errno = 0;
583   number->unit = GTK_CSS_NUMBER;
584   number->value = g_ascii_strtod (parser->data, &end);
585   if (errno)
586     {
587       _gtk_css_parser_error (parser, "not a number: %s", g_strerror (errno));
588       return FALSE;
589     }
590   if (parser->data == end)
591     {
592       _gtk_css_parser_error (parser, "not a number");
593       return FALSE;
594     }
595
596   parser->data = end;
597
598   if (flags & GTK_CSS_POSITIVE_ONLY &&
599       number->value < 0)
600     {
601       _gtk_css_parser_error (parser, "negative values are not allowed.");
602       return FALSE;
603     }
604
605   unit = _gtk_css_parser_try_ident (parser, FALSE);
606
607   if (unit)
608     {
609       guint i;
610
611       for (i = 0; i < G_N_ELEMENTS (units); i++)
612         {
613           if (flags & units[i].required_flags &&
614               g_ascii_strcasecmp (unit, units[i].name) == 0)
615             break;
616         }
617
618       if (i >= G_N_ELEMENTS (units))
619         {
620           _gtk_css_parser_error (parser, "`%s' is not a valid unit.", unit);
621           g_free (unit);
622           return FALSE;
623         }
624
625       number->unit = units[i].unit;
626       g_free (unit);
627     }
628   else
629     {
630       if ((flags & GTK_CSS_PARSE_PERCENT) &&
631           _gtk_css_parser_try (parser, "%", FALSE))
632         {
633           number->unit = GTK_CSS_PERCENT;
634         }
635       else if (number->value == 0.0)
636         {
637           if (flags & GTK_CSS_PARSE_NUMBER)
638             number->unit = GTK_CSS_NUMBER;
639           else if (flags & GTK_CSS_PARSE_LENGTH)
640             number->unit = GTK_CSS_PX;
641           else
642             number->unit = GTK_CSS_PERCENT;
643         }
644       else if (flags & GTK_CSS_NUMBER_AS_PIXELS)
645         {
646           _gtk_css_parser_error_full (parser,
647                                       GTK_CSS_PROVIDER_ERROR_DEPRECATED,
648                                       "Not using units is deprecated. Assuming 'px'.");
649           number->unit = GTK_CSS_PX;
650         }
651       else if (flags & GTK_CSS_PARSE_NUMBER)
652         {
653           number->unit = GTK_CSS_NUMBER;
654         }
655       else
656         {
657           _gtk_css_parser_error (parser, "Unit is missing.");
658           return FALSE;
659         }
660     }
661
662   _gtk_css_parser_skip_whitespace (parser);
663
664   return TRUE;
665 }
666
667 /* XXX: we should introduce GtkCssLenght that deals with
668  * different kind of units */
669 gboolean
670 _gtk_css_parser_try_length (GtkCssParser *parser,
671                             int          *value)
672 {
673   if (!_gtk_css_parser_try_int (parser, value))
674     return FALSE;
675
676   /* FIXME: _try_uint skips spaces while the
677    * spec forbids them
678    */
679   _gtk_css_parser_try (parser, "px", TRUE);
680
681   return TRUE;
682 }
683
684 gboolean
685 _gtk_css_parser_try_enum (GtkCssParser *parser,
686                           GType         enum_type,
687                           int          *value)
688 {
689   GEnumClass *enum_class;
690   gboolean result;
691   const char *start;
692   char *str;
693
694   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
695   g_return_val_if_fail (value != NULL, FALSE);
696
697   result = FALSE;
698
699   enum_class = g_type_class_ref (enum_type);
700
701   start = parser->data;
702
703   str = _gtk_css_parser_try_ident (parser, TRUE);
704   if (str == NULL)
705     return FALSE;
706
707   if (enum_class->n_values)
708     {
709       GEnumValue *enum_value;
710
711       for (enum_value = enum_class->values; enum_value->value_name; enum_value++)
712         {
713           if (enum_value->value_nick &&
714               g_ascii_strcasecmp (str, enum_value->value_nick) == 0)
715             {
716               *value = enum_value->value;
717               result = TRUE;
718               break;
719             }
720         }
721     }
722
723   g_free (str);
724   g_type_class_unref (enum_class);
725
726   if (!result)
727     parser->data = start;
728
729   return result;
730 }
731
732 typedef enum {
733   COLOR_RGBA,
734   COLOR_RGB,
735   COLOR_LIGHTER,
736   COLOR_DARKER,
737   COLOR_SHADE,
738   COLOR_ALPHA,
739   COLOR_MIX,
740   COLOR_WIN32
741 } ColorType;
742
743 static GtkSymbolicColor *
744 gtk_css_parser_read_symbolic_color_function (GtkCssParser *parser,
745                                              ColorType     color)
746 {
747   GtkSymbolicColor *symbolic;
748   GtkSymbolicColor *child1, *child2;
749   double value;
750
751   if (!_gtk_css_parser_try (parser, "(", TRUE))
752     {
753       _gtk_css_parser_error (parser, "Missing opening bracket in color definition");
754       return NULL;
755     }
756
757   if (color == COLOR_RGB || color == COLOR_RGBA)
758     {
759       GdkRGBA rgba;
760       double tmp;
761       guint i;
762
763       for (i = 0; i < 3; i++)
764         {
765           if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
766             {
767               _gtk_css_parser_error (parser, "Expected ',' in color definition");
768               return NULL;
769             }
770
771           if (!_gtk_css_parser_try_double (parser, &tmp))
772             {
773               _gtk_css_parser_error (parser, "Invalid number for color value");
774               return NULL;
775             }
776           if (_gtk_css_parser_try (parser, "%", TRUE))
777             tmp /= 100.0;
778           else
779             tmp /= 255.0;
780           if (i == 0)
781             rgba.red = tmp;
782           else if (i == 1)
783             rgba.green = tmp;
784           else if (i == 2)
785             rgba.blue = tmp;
786           else
787             g_assert_not_reached ();
788         }
789
790       if (color == COLOR_RGBA)
791         {
792           if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
793             {
794               _gtk_css_parser_error (parser, "Expected ',' in color definition");
795               return NULL;
796             }
797
798           if (!_gtk_css_parser_try_double (parser, &rgba.alpha))
799             {
800               _gtk_css_parser_error (parser, "Invalid number for alpha value");
801               return NULL;
802             }
803         }
804       else
805         rgba.alpha = 1.0;
806       
807       symbolic = gtk_symbolic_color_new_literal (&rgba);
808     }
809   else if (color == COLOR_WIN32)
810     {
811       symbolic = _gtk_win32_theme_color_parse (parser);
812       if (symbolic == NULL)
813         return NULL;
814     }
815   else
816     {
817       child1 = _gtk_css_parser_read_symbolic_color (parser);
818       if (child1 == NULL)
819         return NULL;
820
821       if (color == COLOR_MIX)
822         {
823           if (!_gtk_css_parser_try (parser, ",", TRUE))
824             {
825               _gtk_css_parser_error (parser, "Expected ',' in color definition");
826               gtk_symbolic_color_unref (child1);
827               return NULL;
828             }
829
830           child2 = _gtk_css_parser_read_symbolic_color (parser);
831           if (child2 == NULL)
832             {
833               gtk_symbolic_color_unref (child1);
834               return NULL;
835             }
836         }
837       else
838         child2 = NULL;
839
840       if (color == COLOR_LIGHTER)
841         value = 1.3;
842       else if (color == COLOR_DARKER)
843         value = 0.7;
844       else
845         {
846           if (!_gtk_css_parser_try (parser, ",", TRUE))
847             {
848               _gtk_css_parser_error (parser, "Expected ',' in color definition");
849               gtk_symbolic_color_unref (child1);
850               if (child2)
851                 gtk_symbolic_color_unref (child2);
852               return NULL;
853             }
854
855           if (!_gtk_css_parser_try_double (parser, &value))
856             {
857               _gtk_css_parser_error (parser, "Expected number in color definition");
858               gtk_symbolic_color_unref (child1);
859               if (child2)
860                 gtk_symbolic_color_unref (child2);
861               return NULL;
862             }
863         }
864       
865       switch (color)
866         {
867         case COLOR_LIGHTER:
868         case COLOR_DARKER:
869         case COLOR_SHADE:
870           symbolic = gtk_symbolic_color_new_shade (child1, value);
871           break;
872         case COLOR_ALPHA:
873           symbolic = gtk_symbolic_color_new_alpha (child1, value);
874           break;
875         case COLOR_MIX:
876           symbolic = gtk_symbolic_color_new_mix (child1, child2, value);
877           break;
878         default:
879           g_assert_not_reached ();
880           symbolic = NULL;
881         }
882
883       gtk_symbolic_color_unref (child1);
884       if (child2)
885         gtk_symbolic_color_unref (child2);
886     }
887
888   if (!_gtk_css_parser_try (parser, ")", TRUE))
889     {
890       _gtk_css_parser_error (parser, "Expected ')' in color definition");
891       gtk_symbolic_color_unref (symbolic);
892       return NULL;
893     }
894
895   return symbolic;
896 }
897
898 static GtkSymbolicColor *
899 gtk_css_parser_try_hash_color (GtkCssParser *parser)
900 {
901   if (parser->data[0] == '#' &&
902       g_ascii_isxdigit (parser->data[1]) &&
903       g_ascii_isxdigit (parser->data[2]) &&
904       g_ascii_isxdigit (parser->data[3]))
905     {
906       GdkRGBA rgba;
907       
908       if (g_ascii_isxdigit (parser->data[4]) &&
909           g_ascii_isxdigit (parser->data[5]) &&
910           g_ascii_isxdigit (parser->data[6]))
911         {
912           rgba.red   = ((get_xdigit (parser->data[1]) << 4) + get_xdigit (parser->data[2])) / 255.0;
913           rgba.green = ((get_xdigit (parser->data[3]) << 4) + get_xdigit (parser->data[4])) / 255.0;
914           rgba.blue  = ((get_xdigit (parser->data[5]) << 4) + get_xdigit (parser->data[6])) / 255.0;
915           rgba.alpha = 1.0;
916           parser->data += 7;
917         }
918       else
919         {
920           rgba.red   = get_xdigit (parser->data[1]) / 15.0;
921           rgba.green = get_xdigit (parser->data[2]) / 15.0;
922           rgba.blue  = get_xdigit (parser->data[3]) / 15.0;
923           rgba.alpha = 1.0;
924           parser->data += 4;
925         }
926
927       _gtk_css_parser_skip_whitespace (parser);
928
929       return gtk_symbolic_color_new_literal (&rgba);
930     }
931
932   return NULL;
933 }
934
935 GtkSymbolicColor *
936 _gtk_css_parser_read_symbolic_color (GtkCssParser *parser)
937 {
938   GtkSymbolicColor *symbolic;
939   guint color;
940   const char *names[] = {"rgba", "rgb",  "lighter", "darker", "shade", "alpha", "mix",
941                          GTK_WIN32_THEME_SYMBOLIC_COLOR_NAME};
942   char *name;
943
944   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
945
946   if (_gtk_css_parser_try (parser, "transparent", TRUE))
947     {
948       GdkRGBA transparent = { 0, 0, 0, 0 };
949       
950       return gtk_symbolic_color_new_literal (&transparent);
951     }
952
953   if (_gtk_css_parser_try (parser, "@", FALSE))
954     {
955       name = _gtk_css_parser_try_name (parser, TRUE);
956
957       if (name)
958         {
959           symbolic = gtk_symbolic_color_new_name (name);
960         }
961       else
962         {
963           _gtk_css_parser_error (parser, "'%s' is not a valid symbolic color name", name);
964           symbolic = NULL;
965         }
966
967       g_free (name);
968       return symbolic;
969     }
970
971   for (color = 0; color < G_N_ELEMENTS (names); color++)
972     {
973       if (_gtk_css_parser_try (parser, names[color], TRUE))
974         break;
975     }
976
977   if (color < G_N_ELEMENTS (names))
978     return gtk_css_parser_read_symbolic_color_function (parser, color);
979
980   symbolic = gtk_css_parser_try_hash_color (parser);
981   if (symbolic)
982     return symbolic;
983
984   name = _gtk_css_parser_try_name (parser, TRUE);
985   if (name)
986     {
987       GdkRGBA rgba;
988
989       if (gdk_rgba_parse (&rgba, name))
990         {
991           symbolic = gtk_symbolic_color_new_literal (&rgba);
992         }
993       else
994         {
995           _gtk_css_parser_error (parser, "'%s' is not a valid color name", name);
996           symbolic = NULL;
997         }
998       g_free (name);
999       return symbolic;
1000     }
1001
1002   _gtk_css_parser_error (parser, "Not a color definition");
1003   return NULL;
1004 }
1005
1006 GFile *
1007 _gtk_css_parser_read_url (GtkCssParser *parser,
1008                           GFile        *base)
1009 {
1010   gchar *path;
1011   char *scheme;
1012   GFile *file;
1013
1014   if (_gtk_css_parser_try (parser, "url", FALSE))
1015     {
1016       if (!_gtk_css_parser_try (parser, "(", TRUE))
1017         {
1018           _gtk_css_parser_skip_whitespace (parser);
1019           if (_gtk_css_parser_try (parser, "(", TRUE))
1020             {
1021               _gtk_css_parser_error_full (parser,
1022                                           GTK_CSS_PROVIDER_ERROR_DEPRECATED,
1023                                           "Whitespace between 'url' and '(' is deprecated");
1024             }
1025           else
1026             {
1027               _gtk_css_parser_error (parser, "Expected '(' after 'url'");
1028               return NULL;
1029             }
1030         }
1031
1032       path = _gtk_css_parser_read_string (parser);
1033       if (path == NULL)
1034         return NULL;
1035
1036       if (!_gtk_css_parser_try (parser, ")", TRUE))
1037         {
1038           _gtk_css_parser_error (parser, "No closing ')' found for 'url'");
1039           g_free (path);
1040           return NULL;
1041         }
1042
1043       scheme = g_uri_parse_scheme (path);
1044       if (scheme != NULL)
1045         {
1046           file = g_file_new_for_uri (path);
1047           g_free (path);
1048           g_free (scheme);
1049           return file;
1050         }
1051     }
1052   else
1053     {
1054       path = _gtk_css_parser_try_name (parser, TRUE);
1055       if (path == NULL)
1056         {
1057           _gtk_css_parser_error (parser, "Not a valid url");
1058           return NULL;
1059         }
1060     }
1061
1062   file = g_file_resolve_relative_path (base, path);
1063   g_free (path);
1064
1065   return file;
1066 }
1067
1068 void
1069 _gtk_css_parser_resync_internal (GtkCssParser *parser,
1070                                  gboolean      sync_at_semicolon,
1071                                  gboolean      read_sync_token,
1072                                  char          terminator)
1073 {
1074   gsize len;
1075
1076   do {
1077     len = strcspn (parser->data, "\\\"'/()[]{};" NEWLINE_CHARS);
1078     parser->data += len;
1079
1080     if (gtk_css_parser_new_line (parser))
1081       continue;
1082
1083     if (_gtk_css_parser_is_string (parser))
1084       {
1085         /* Hrm, this emits errors, and i suspect it shouldn't... */
1086         char *free_me = _gtk_css_parser_read_string (parser);
1087         g_free (free_me);
1088         continue;
1089       }
1090
1091     if (gtk_css_parser_skip_comment (parser))
1092       continue;
1093
1094     switch (*parser->data)
1095       {
1096       case '\\':
1097         {
1098           GString *ignore = g_string_new (NULL);
1099           _gtk_css_parser_unescape (parser, ignore);
1100           g_string_free (ignore, TRUE);
1101         }
1102         break;
1103       case ';':
1104         if (sync_at_semicolon && !read_sync_token)
1105           return;
1106         parser->data++;
1107         if (sync_at_semicolon)
1108           {
1109             _gtk_css_parser_skip_whitespace (parser);
1110             return;
1111           }
1112         break;
1113       case '(':
1114         parser->data++;
1115         _gtk_css_parser_resync (parser, FALSE, ')');
1116         if (*parser->data)
1117           parser->data++;
1118         break;
1119       case '[':
1120         parser->data++;
1121         _gtk_css_parser_resync (parser, FALSE, ']');
1122         if (*parser->data)
1123           parser->data++;
1124         break;
1125       case '{':
1126         parser->data++;
1127         _gtk_css_parser_resync (parser, FALSE, '}');
1128         if (*parser->data)
1129           parser->data++;
1130         if (sync_at_semicolon || !terminator)
1131           {
1132             _gtk_css_parser_skip_whitespace (parser);
1133             return;
1134           }
1135         break;
1136       case '}':
1137       case ')':
1138       case ']':
1139         if (terminator == *parser->data)
1140           {
1141             _gtk_css_parser_skip_whitespace (parser);
1142             return;
1143           }
1144         parser->data++;
1145         continue;
1146       case '\0':
1147         break;
1148       case '/':
1149       default:
1150         parser->data++;
1151         break;
1152       }
1153   } while (*parser->data);
1154 }
1155
1156 char *
1157 _gtk_css_parser_read_value (GtkCssParser *parser)
1158 {
1159   const char *start;
1160   char *result;
1161
1162   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
1163
1164   start = parser->data;
1165
1166   /* This needs to be done better */
1167   _gtk_css_parser_resync_internal (parser, TRUE, FALSE, '}');
1168
1169   result = g_strndup (start, parser->data - start);
1170   if (result)
1171     {
1172       g_strchomp (result);
1173       if (result[0] == 0)
1174         {
1175           g_free (result);
1176           result = NULL;
1177         }
1178     }
1179
1180   if (result == NULL)
1181     _gtk_css_parser_error (parser, "Expected a property value");
1182
1183   return result;
1184 }
1185
1186 void
1187 _gtk_css_parser_resync (GtkCssParser *parser,
1188                         gboolean      sync_at_semicolon,
1189                         char          terminator)
1190 {
1191   g_return_if_fail (GTK_IS_CSS_PARSER (parser));
1192
1193   _gtk_css_parser_resync_internal (parser, sync_at_semicolon, TRUE, terminator);
1194 }