]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssparser.c
Updated Russian translation
[~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 (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       g_free (unit_name);
673
674       if (i >= G_N_ELEMENTS (units))
675         {
676           _gtk_css_parser_error (parser, "`%s' is not a valid unit.", unit_name);
677           return NULL;
678         }
679
680       unit = units[i].unit;
681     }
682   else
683     {
684       if ((flags & GTK_CSS_PARSE_PERCENT) &&
685           _gtk_css_parser_try (parser, "%", FALSE))
686         {
687           unit = GTK_CSS_PERCENT;
688         }
689       else if (value == 0.0)
690         {
691           if (flags & GTK_CSS_PARSE_NUMBER)
692             unit = GTK_CSS_NUMBER;
693           else if (flags & GTK_CSS_PARSE_LENGTH)
694             unit = GTK_CSS_PX;
695           else if (flags & GTK_CSS_PARSE_ANGLE)
696             unit = GTK_CSS_DEG;
697           else if (flags & GTK_CSS_PARSE_TIME)
698             unit = GTK_CSS_S;
699           else
700             unit = GTK_CSS_PERCENT;
701         }
702       else if (flags & GTK_CSS_NUMBER_AS_PIXELS)
703         {
704           _gtk_css_parser_error_full (parser,
705                                       GTK_CSS_PROVIDER_ERROR_DEPRECATED,
706                                       "Not using units is deprecated. Assuming 'px'.");
707           unit = GTK_CSS_PX;
708         }
709       else if (flags & GTK_CSS_PARSE_NUMBER)
710         {
711           unit = GTK_CSS_NUMBER;
712         }
713       else
714         {
715           _gtk_css_parser_error (parser, "Unit is missing.");
716           return NULL;
717         }
718     }
719
720   _gtk_css_parser_skip_whitespace (parser);
721
722   return _gtk_css_number_value_new (value, unit);
723 }
724
725 /* XXX: we should introduce GtkCssLenght that deals with
726  * different kind of units */
727 gboolean
728 _gtk_css_parser_try_length (GtkCssParser *parser,
729                             int          *value)
730 {
731   if (!_gtk_css_parser_try_int (parser, value))
732     return FALSE;
733
734   /* FIXME: _try_uint skips spaces while the
735    * spec forbids them
736    */
737   _gtk_css_parser_try (parser, "px", TRUE);
738
739   return TRUE;
740 }
741
742 gboolean
743 _gtk_css_parser_try_enum (GtkCssParser *parser,
744                           GType         enum_type,
745                           int          *value)
746 {
747   GEnumClass *enum_class;
748   gboolean result;
749   const char *start;
750   char *str;
751
752   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
753   g_return_val_if_fail (value != NULL, FALSE);
754
755   result = FALSE;
756
757   enum_class = g_type_class_ref (enum_type);
758
759   start = parser->data;
760
761   str = _gtk_css_parser_try_ident (parser, TRUE);
762   if (str == NULL)
763     return FALSE;
764
765   if (enum_class->n_values)
766     {
767       GEnumValue *enum_value;
768
769       for (enum_value = enum_class->values; enum_value->value_name; enum_value++)
770         {
771           if (enum_value->value_nick &&
772               g_ascii_strcasecmp (str, enum_value->value_nick) == 0)
773             {
774               *value = enum_value->value;
775               result = TRUE;
776               break;
777             }
778         }
779     }
780
781   g_free (str);
782   g_type_class_unref (enum_class);
783
784   if (!result)
785     parser->data = start;
786
787   return result;
788 }
789
790 gboolean
791 _gtk_css_parser_try_hash_color (GtkCssParser *parser,
792                                 GdkRGBA      *rgba)
793 {
794   if (parser->data[0] == '#' &&
795       g_ascii_isxdigit (parser->data[1]) &&
796       g_ascii_isxdigit (parser->data[2]) &&
797       g_ascii_isxdigit (parser->data[3]))
798     {
799       if (g_ascii_isxdigit (parser->data[4]) &&
800           g_ascii_isxdigit (parser->data[5]) &&
801           g_ascii_isxdigit (parser->data[6]))
802         {
803           rgba->red   = ((get_xdigit (parser->data[1]) << 4) + get_xdigit (parser->data[2])) / 255.0;
804           rgba->green = ((get_xdigit (parser->data[3]) << 4) + get_xdigit (parser->data[4])) / 255.0;
805           rgba->blue  = ((get_xdigit (parser->data[5]) << 4) + get_xdigit (parser->data[6])) / 255.0;
806           rgba->alpha = 1.0;
807           parser->data += 7;
808         }
809       else
810         {
811           rgba->red   = get_xdigit (parser->data[1]) / 15.0;
812           rgba->green = get_xdigit (parser->data[2]) / 15.0;
813           rgba->blue  = get_xdigit (parser->data[3]) / 15.0;
814           rgba->alpha = 1.0;
815           parser->data += 4;
816         }
817
818       _gtk_css_parser_skip_whitespace (parser);
819
820       return TRUE;
821     }
822
823   return FALSE;
824 }
825
826 GFile *
827 _gtk_css_parser_read_url (GtkCssParser *parser)
828 {
829   gchar *path;
830   char *scheme;
831   GFile *file;
832
833   if (_gtk_css_parser_try (parser, "url", FALSE))
834     {
835       if (!_gtk_css_parser_try (parser, "(", TRUE))
836         {
837           _gtk_css_parser_skip_whitespace (parser);
838           if (_gtk_css_parser_try (parser, "(", TRUE))
839             {
840               _gtk_css_parser_error_full (parser,
841                                           GTK_CSS_PROVIDER_ERROR_DEPRECATED,
842                                           "Whitespace between 'url' and '(' is deprecated");
843             }
844           else
845             {
846               _gtk_css_parser_error (parser, "Expected '(' after 'url'");
847               return NULL;
848             }
849         }
850
851       path = _gtk_css_parser_read_string (parser);
852       if (path == NULL)
853         return NULL;
854
855       if (!_gtk_css_parser_try (parser, ")", TRUE))
856         {
857           _gtk_css_parser_error (parser, "No closing ')' found for 'url'");
858           g_free (path);
859           return NULL;
860         }
861
862       scheme = g_uri_parse_scheme (path);
863       if (scheme != NULL)
864         {
865           file = g_file_new_for_uri (path);
866           g_free (path);
867           g_free (scheme);
868           return file;
869         }
870     }
871   else
872     {
873       path = _gtk_css_parser_try_name (parser, TRUE);
874       if (path == NULL)
875         {
876           _gtk_css_parser_error (parser, "Not a valid url");
877           return NULL;
878         }
879     }
880
881   file = _gtk_css_parser_get_file_for_path (parser, path);
882   g_free (path);
883
884   return file;
885 }
886
887 void
888 _gtk_css_parser_resync_internal (GtkCssParser *parser,
889                                  gboolean      sync_at_semicolon,
890                                  gboolean      read_sync_token,
891                                  char          terminator)
892 {
893   gsize len;
894
895   do {
896     len = strcspn (parser->data, "\\\"'/()[]{};" NEWLINE_CHARS);
897     parser->data += len;
898
899     if (gtk_css_parser_new_line (parser))
900       continue;
901
902     if (_gtk_css_parser_is_string (parser))
903       {
904         /* Hrm, this emits errors, and i suspect it shouldn't... */
905         char *free_me = _gtk_css_parser_read_string (parser);
906         g_free (free_me);
907         continue;
908       }
909
910     if (gtk_css_parser_skip_comment (parser))
911       continue;
912
913     switch (*parser->data)
914       {
915       case '\\':
916         {
917           GString *ignore = g_string_new (NULL);
918           _gtk_css_parser_unescape (parser, ignore);
919           g_string_free (ignore, TRUE);
920         }
921         break;
922       case ';':
923         if (sync_at_semicolon && !read_sync_token)
924           return;
925         parser->data++;
926         if (sync_at_semicolon)
927           {
928             _gtk_css_parser_skip_whitespace (parser);
929             return;
930           }
931         break;
932       case '(':
933         parser->data++;
934         _gtk_css_parser_resync (parser, FALSE, ')');
935         if (*parser->data)
936           parser->data++;
937         break;
938       case '[':
939         parser->data++;
940         _gtk_css_parser_resync (parser, FALSE, ']');
941         if (*parser->data)
942           parser->data++;
943         break;
944       case '{':
945         parser->data++;
946         _gtk_css_parser_resync (parser, FALSE, '}');
947         if (*parser->data)
948           parser->data++;
949         if (sync_at_semicolon || !terminator)
950           {
951             _gtk_css_parser_skip_whitespace (parser);
952             return;
953           }
954         break;
955       case '}':
956       case ')':
957       case ']':
958         if (terminator == *parser->data)
959           {
960             _gtk_css_parser_skip_whitespace (parser);
961             return;
962           }
963         parser->data++;
964         continue;
965       case '\0':
966         break;
967       case '/':
968       default:
969         parser->data++;
970         break;
971       }
972   } while (*parser->data);
973 }
974
975 char *
976 _gtk_css_parser_read_value (GtkCssParser *parser)
977 {
978   const char *start;
979   char *result;
980
981   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
982
983   start = parser->data;
984
985   /* This needs to be done better */
986   _gtk_css_parser_resync_internal (parser, TRUE, FALSE, '}');
987
988   result = g_strndup (start, parser->data - start);
989   if (result)
990     {
991       g_strchomp (result);
992       if (result[0] == 0)
993         {
994           g_free (result);
995           result = NULL;
996         }
997     }
998
999   if (result == NULL)
1000     _gtk_css_parser_error (parser, "Expected a property value");
1001
1002   return result;
1003 }
1004
1005 void
1006 _gtk_css_parser_resync (GtkCssParser *parser,
1007                         gboolean      sync_at_semicolon,
1008                         char          terminator)
1009 {
1010   g_return_if_fail (GTK_IS_CSS_PARSER (parser));
1011
1012   _gtk_css_parser_resync_internal (parser, sync_at_semicolon, TRUE, terminator);
1013 }