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