1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@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 <string.h>
21 #include <stdlib.h>
22 
23 #include <gdk-pixbuf/gdk-pixbuf.h>
24 #include <cairo-gobject.h>
25 
26 #include "gtkcssproviderprivate.h"
27 
28 #include "gtkbitmaskprivate.h"
29 #include "gtkcssarrayvalueprivate.h"
30 #include "gtkcsscolorvalueprivate.h"
31 #include "gtkcsskeyframesprivate.h"
32 #include "gtkcssparserprivate.h"
33 #include "gtkcsssectionprivate.h"
34 #include "gtkcssselectorprivate.h"
35 #include "gtkcssshorthandpropertyprivate.h"
36 #include "gtkcssstylefuncsprivate.h"
37 #include "gtksettingsprivate.h"
38 #include "gtkstyleprovider.h"
39 #include "gtkstylecontextprivate.h"
40 #include "gtkstylepropertyprivate.h"
41 #include "gtkstyleproviderprivate.h"
42 #include "gtkwidgetpath.h"
43 #include "gtkbindings.h"
44 #include "gtkmarshalers.h"
45 #include "gtkprivate.h"
46 #include "gtkintl.h"
47 #include "gtkutilsprivate.h"
48 #include "gtkversion.h"
49 
50 /**
51  * SECTION:gtkcssprovider
52  * @Short_description: CSS-like styling for widgets
53  * @Title: GtkCssProvider
54  * @See_also: #GtkStyleContext, #GtkStyleProvider
55  *
56  * GtkCssProvider is an object implementing the #GtkStyleProvider interface.
57  * It is able to parse [CSS-like][css-overview] input in order to style widgets.
58  *
59  * An application can make GTK+ parse a specific CSS style sheet by calling
60  * gtk_css_provider_load_from_file() or gtk_css_provider_load_from_resource()
61  * and adding the provider with gtk_style_context_add_provider() or
62  * gtk_style_context_add_provider_for_screen().
63 
64  * In addition, certain files will be read when GTK+ is initialized. First, the
65  * file `$XDG_CONFIG_HOME/gtk-3.0/gtk.css` is loaded if it exists. Then, GTK+
66  * loads the first existing file among
67  * `XDG_DATA_HOME/themes/THEME/gtk-VERSION/gtk.css`,
68  * `$HOME/.themes/THEME/gtk-VERSION/gtk.css`,
69  * `$XDG_DATA_DIRS/themes/THEME/gtk-VERSION/gtk.css` and
70  * `DATADIR/share/themes/THEME/gtk-VERSION/gtk.css`, where `THEME` is the name of
71  * the current theme (see the #GtkSettings:gtk-theme-name setting), `DATADIR`
72  * is the prefix configured when GTK+ was compiled (unless overridden by the
73  * `GTK_DATA_PREFIX` environment variable), and `VERSION` is the GTK+ version number.
74  * If no file is found for the current version, GTK+ tries older versions all the
75  * way back to 3.0.
76  *
77  * In the same way, GTK+ tries to load a gtk-keys.css file for the current
78  * key theme, as defined by #GtkSettings:gtk-key-theme-name.
79  */
80 
81 
82 typedef struct GtkCssRuleset GtkCssRuleset;
83 typedef struct _GtkCssScanner GtkCssScanner;
84 typedef struct _PropertyValue PropertyValue;
85 typedef struct _WidgetPropertyValue WidgetPropertyValue;
86 typedef enum ParserScope ParserScope;
87 typedef enum ParserSymbol ParserSymbol;
88 
89 struct _PropertyValue {
90   GtkCssStyleProperty *property;
91   GtkCssValue         *value;
92   GtkCssSection       *section;
93 };
94 
95 struct _WidgetPropertyValue {
96   WidgetPropertyValue *next;
97   char *name;
98   char *value;
99 
100   GtkCssSection *section;
101 };
102 
103 struct GtkCssRuleset
104 {
105   GtkCssSelector *selector;
106   GtkCssSelectorTree *selector_match;
107   WidgetPropertyValue *widget_style;
108   PropertyValue *styles;
109   GtkBitmask *set_styles;
110   guint n_styles;
111   guint owns_styles : 1;
112   guint owns_widget_style : 1;
113 };
114 
115 struct _GtkCssScanner
116 {
117   GtkCssProvider *provider;
118   GtkCssParser *parser;
119   GtkCssSection *section;
120   GtkCssScanner *parent;
121   GSList *state;
122 };
123 
124 struct _GtkCssProviderPrivate
125 {
126   GScanner *scanner;
127 
128   GHashTable *symbolic_colors;
129   GHashTable *keyframes;
130 
131   GArray *rulesets;
132   GtkCssSelectorTree *tree;
133   GResource *resource;
134   gchar *path;
135 };
136 
137 enum {
138   PARSING_ERROR,
139   LAST_SIGNAL
140 };
141 
142 static gboolean gtk_keep_css_sections = FALSE;
143 
144 static guint css_provider_signals[LAST_SIGNAL] = { 0 };
145 
146 static void gtk_css_provider_finalize (GObject *object);
147 static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);
148 static void gtk_css_style_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface);
149 static void widget_property_value_list_free (WidgetPropertyValue *head);
150 static void gtk_css_style_provider_emit_error (GtkStyleProviderPrivate *provider,
151                                                GtkCssSection           *section,
152                                                const GError            *error);
153 
154 static gboolean
155 gtk_css_provider_load_internal (GtkCssProvider *css_provider,
156                                 GtkCssScanner  *scanner,
157                                 GFile          *file,
158                                 const char     *data,
159                                 GError        **error);
160 
161 GQuark
gtk_css_provider_error_quark(void)162 gtk_css_provider_error_quark (void)
163 {
164   return g_quark_from_static_string ("gtk-css-provider-error-quark");
165 }
166 
167 G_DEFINE_TYPE_EXTENDED (GtkCssProvider, gtk_css_provider, G_TYPE_OBJECT, 0,
168                         G_ADD_PRIVATE (GtkCssProvider)
169                         G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
170                                                gtk_css_style_provider_iface_init)
171                         G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER_PRIVATE,
172                                                gtk_css_style_provider_private_iface_init));
173 
174 static void
gtk_css_provider_parsing_error(GtkCssProvider * provider,GtkCssSection * section,const GError * error)175 gtk_css_provider_parsing_error (GtkCssProvider  *provider,
176                                 GtkCssSection   *section,
177                                 const GError    *error)
178 {
179   /* Only emit a warning when we have no error handlers. This is our
180    * default handlers. And in this case erroneous CSS files are a bug
181    * and should be fixed.
182    * Note that these warnings can also be triggered by a broken theme
183    * that people installed from some weird location on the internets.
184    */
185   if (!g_signal_has_handler_pending (provider,
186                                      css_provider_signals[PARSING_ERROR],
187                                      0,
188                                      TRUE))
189     {
190       char *s = _gtk_css_section_to_string (section);
191 
192       g_warning ("Theme parsing error: %s: %s",
193                  s,
194                  error->message);
195 
196       g_free (s);
197     }
198 }
199 
200 /* This is exported privately for use in GtkInspector.
201  * It is the callers responsibility to reparse the current theme.
202  */
203 void
gtk_css_provider_set_keep_css_sections(void)204 gtk_css_provider_set_keep_css_sections (void)
205 {
206   gtk_keep_css_sections = TRUE;
207 }
208 
209 static void
gtk_css_provider_class_init(GtkCssProviderClass * klass)210 gtk_css_provider_class_init (GtkCssProviderClass *klass)
211 {
212   GObjectClass *object_class = G_OBJECT_CLASS (klass);
213 
214   if (g_getenv ("GTK_CSS_DEBUG"))
215     gtk_css_provider_set_keep_css_sections ();
216 
217   /**
218    * GtkCssProvider::parsing-error:
219    * @provider: the provider that had a parsing error
220    * @section: section the error happened in
221    * @error: The parsing error
222    *
223    * Signals that a parsing error occurred. the @path, @line and @position
224    * describe the actual location of the error as accurately as possible.
225    *
226    * Parsing errors are never fatal, so the parsing will resume after
227    * the error. Errors may however cause parts of the given
228    * data or even all of it to not be parsed at all. So it is a useful idea
229    * to check that the parsing succeeds by connecting to this signal.
230    *
231    * Note that this signal may be emitted at any time as the css provider
232    * may opt to defer parsing parts or all of the input to a later time
233    * than when a loading function was called.
234    */
235   css_provider_signals[PARSING_ERROR] =
236     g_signal_new (I_("parsing-error"),
237                   G_TYPE_FROM_CLASS (object_class),
238                   G_SIGNAL_RUN_LAST,
239                   G_STRUCT_OFFSET (GtkCssProviderClass, parsing_error),
240                   NULL, NULL,
241                   _gtk_marshal_VOID__BOXED_BOXED,
242                   G_TYPE_NONE, 2, GTK_TYPE_CSS_SECTION, G_TYPE_ERROR);
243 
244   object_class->finalize = gtk_css_provider_finalize;
245 
246   klass->parsing_error = gtk_css_provider_parsing_error;
247 }
248 
249 static void
gtk_css_ruleset_init_copy(GtkCssRuleset * new,GtkCssRuleset * ruleset,GtkCssSelector * selector)250 gtk_css_ruleset_init_copy (GtkCssRuleset       *new,
251                            GtkCssRuleset       *ruleset,
252                            GtkCssSelector      *selector)
253 {
254   memcpy (new, ruleset, sizeof (GtkCssRuleset));
255 
256   new->selector = selector;
257   /* First copy takes over ownership */
258   if (ruleset->owns_styles)
259     ruleset->owns_styles = FALSE;
260   if (ruleset->owns_widget_style)
261     ruleset->owns_widget_style = FALSE;
262   if (new->set_styles)
263     new->set_styles = _gtk_bitmask_copy (new->set_styles);
264 }
265 
266 static void
gtk_css_ruleset_clear(GtkCssRuleset * ruleset)267 gtk_css_ruleset_clear (GtkCssRuleset *ruleset)
268 {
269   if (ruleset->owns_styles)
270     {
271       guint i;
272 
273       for (i = 0; i < ruleset->n_styles; i++)
274         {
275           _gtk_css_value_unref (ruleset->styles[i].value);
276 	  ruleset->styles[i].value = NULL;
277 	  if (ruleset->styles[i].section)
278 	    gtk_css_section_unref (ruleset->styles[i].section);
279         }
280       g_free (ruleset->styles);
281     }
282   if (ruleset->set_styles)
283     _gtk_bitmask_free (ruleset->set_styles);
284   if (ruleset->owns_widget_style)
285     widget_property_value_list_free (ruleset->widget_style);
286   if (ruleset->selector)
287     _gtk_css_selector_free (ruleset->selector);
288 
289   memset (ruleset, 0, sizeof (GtkCssRuleset));
290 }
291 
292 static WidgetPropertyValue *
widget_property_value_new(char * name,GtkCssSection * section)293 widget_property_value_new (char *name, GtkCssSection *section)
294 {
295   WidgetPropertyValue *value;
296 
297   value = g_slice_new0 (WidgetPropertyValue);
298 
299   value->name = name;
300   if (gtk_keep_css_sections)
301     value->section = gtk_css_section_ref (section);
302 
303   return value;
304 }
305 
306 static void
widget_property_value_free(WidgetPropertyValue * value)307 widget_property_value_free (WidgetPropertyValue *value)
308 {
309   g_free (value->value);
310   g_free (value->name);
311   if (value->section)
312     gtk_css_section_unref (value->section);
313 
314   g_slice_free (WidgetPropertyValue, value);
315 }
316 
317 static void
widget_property_value_list_free(WidgetPropertyValue * head)318 widget_property_value_list_free (WidgetPropertyValue *head)
319 {
320   WidgetPropertyValue *l, *next;
321   for (l = head; l != NULL; l = next)
322     {
323       next = l->next;
324       widget_property_value_free (l);
325     }
326 }
327 
328 static WidgetPropertyValue *
widget_property_value_list_remove_name(WidgetPropertyValue * head,const char * name)329 widget_property_value_list_remove_name (WidgetPropertyValue *head, const char *name)
330 {
331   WidgetPropertyValue *l, **last;
332 
333   last = &head;
334 
335   for (l = head; l != NULL; l = l->next)
336     {
337       if (strcmp (l->name, name) == 0)
338 	{
339 	  *last = l->next;
340 	  widget_property_value_free (l);
341 	  break;
342 	}
343 
344       last = &l->next;
345     }
346 
347   return head;
348 }
349 
350 static void
gtk_css_ruleset_add_style(GtkCssRuleset * ruleset,char * name,WidgetPropertyValue * value)351 gtk_css_ruleset_add_style (GtkCssRuleset *ruleset,
352                            char          *name,
353                            WidgetPropertyValue *value)
354 {
355   value->next = widget_property_value_list_remove_name (ruleset->widget_style, name);
356   ruleset->widget_style = value;
357   ruleset->owns_widget_style = TRUE;
358 }
359 
360 static void
gtk_css_ruleset_add(GtkCssRuleset * ruleset,GtkCssStyleProperty * property,GtkCssValue * value,GtkCssSection * section)361 gtk_css_ruleset_add (GtkCssRuleset       *ruleset,
362                      GtkCssStyleProperty *property,
363                      GtkCssValue         *value,
364                      GtkCssSection       *section)
365 {
366   guint i;
367 
368   g_return_if_fail (ruleset->owns_styles || ruleset->n_styles == 0);
369 
370   if (ruleset->set_styles == NULL)
371     ruleset->set_styles = _gtk_bitmask_new ();
372 
373   ruleset->set_styles = _gtk_bitmask_set (ruleset->set_styles,
374                                           _gtk_css_style_property_get_id (property),
375                                           TRUE);
376 
377   ruleset->owns_styles = TRUE;
378 
379   for (i = 0; i < ruleset->n_styles; i++)
380     {
381       if (ruleset->styles[i].property == property)
382         {
383           _gtk_css_value_unref (ruleset->styles[i].value);
384 	  ruleset->styles[i].value = NULL;
385 	  if (ruleset->styles[i].section)
386 	    gtk_css_section_unref (ruleset->styles[i].section);
387           break;
388         }
389     }
390   if (i == ruleset->n_styles)
391     {
392       ruleset->n_styles++;
393       ruleset->styles = g_realloc (ruleset->styles, ruleset->n_styles * sizeof (PropertyValue));
394       ruleset->styles[i].value = NULL;
395       ruleset->styles[i].property = property;
396     }
397 
398   ruleset->styles[i].value = value;
399   if (gtk_keep_css_sections)
400     ruleset->styles[i].section = gtk_css_section_ref (section);
401   else
402     ruleset->styles[i].section = NULL;
403 }
404 
405 static void
gtk_css_scanner_destroy(GtkCssScanner * scanner)406 gtk_css_scanner_destroy (GtkCssScanner *scanner)
407 {
408   if (scanner->section)
409     gtk_css_section_unref (scanner->section);
410   g_object_unref (scanner->provider);
411   _gtk_css_parser_free (scanner->parser);
412 
413   g_slice_free (GtkCssScanner, scanner);
414 }
415 
416 static void
gtk_css_style_provider_emit_error(GtkStyleProviderPrivate * provider,GtkCssSection * section,const GError * error)417 gtk_css_style_provider_emit_error (GtkStyleProviderPrivate *provider,
418                                    GtkCssSection           *section,
419                                    const GError            *error)
420 {
421   g_signal_emit (provider, css_provider_signals[PARSING_ERROR], 0, section, error);
422 }
423 
424 static void
gtk_css_provider_emit_error(GtkCssProvider * provider,GtkCssScanner * scanner,const GError * error)425 gtk_css_provider_emit_error (GtkCssProvider *provider,
426                              GtkCssScanner  *scanner,
427                              const GError   *error)
428 {
429   gtk_css_style_provider_emit_error (GTK_STYLE_PROVIDER_PRIVATE (provider),
430                                      scanner ? scanner->section : NULL,
431                                      error);
432 }
433 
434 static void
gtk_css_scanner_parser_error(GtkCssParser * parser,const GError * error,gpointer user_data)435 gtk_css_scanner_parser_error (GtkCssParser *parser,
436                               const GError *error,
437                               gpointer      user_data)
438 {
439   GtkCssScanner *scanner = user_data;
440 
441   gtk_css_provider_emit_error (scanner->provider, scanner, error);
442 }
443 
444 static GtkCssScanner *
gtk_css_scanner_new(GtkCssProvider * provider,GtkCssScanner * parent,GtkCssSection * section,GFile * file,const gchar * text)445 gtk_css_scanner_new (GtkCssProvider *provider,
446                      GtkCssScanner  *parent,
447                      GtkCssSection  *section,
448                      GFile          *file,
449                      const gchar    *text)
450 {
451   GtkCssScanner *scanner;
452 
453   scanner = g_slice_new0 (GtkCssScanner);
454 
455   g_object_ref (provider);
456   scanner->provider = provider;
457   scanner->parent = parent;
458   if (section)
459     scanner->section = gtk_css_section_ref (section);
460 
461   scanner->parser = _gtk_css_parser_new (text,
462                                          file,
463                                          gtk_css_scanner_parser_error,
464                                          scanner);
465 
466   return scanner;
467 }
468 
469 static gboolean
gtk_css_scanner_would_recurse(GtkCssScanner * scanner,GFile * file)470 gtk_css_scanner_would_recurse (GtkCssScanner *scanner,
471                                GFile         *file)
472 {
473   while (scanner)
474     {
475       GFile *parser_file = _gtk_css_parser_get_file (scanner->parser);
476       if (parser_file && g_file_equal (parser_file, file))
477         return TRUE;
478 
479       scanner = scanner->parent;
480     }
481 
482   return FALSE;
483 }
484 
485 static void
gtk_css_scanner_push_section(GtkCssScanner * scanner,GtkCssSectionType section_type)486 gtk_css_scanner_push_section (GtkCssScanner     *scanner,
487                               GtkCssSectionType  section_type)
488 {
489   GtkCssSection *section;
490 
491   section = _gtk_css_section_new (scanner->section,
492                                   section_type,
493                                   scanner->parser);
494 
495   if (scanner->section)
496     gtk_css_section_unref (scanner->section);
497   scanner->section = section;
498 }
499 
500 static void
gtk_css_scanner_pop_section(GtkCssScanner * scanner,GtkCssSectionType check_type)501 gtk_css_scanner_pop_section (GtkCssScanner *scanner,
502                              GtkCssSectionType check_type)
503 {
504   GtkCssSection *parent;
505 
506   g_assert (gtk_css_section_get_section_type (scanner->section) == check_type);
507 
508   parent = gtk_css_section_get_parent (scanner->section);
509   if (parent)
510     gtk_css_section_ref (parent);
511 
512   _gtk_css_section_end (scanner->section);
513   gtk_css_section_unref (scanner->section);
514 
515   scanner->section = parent;
516 }
517 
518 static void
gtk_css_provider_init(GtkCssProvider * css_provider)519 gtk_css_provider_init (GtkCssProvider *css_provider)
520 {
521   GtkCssProviderPrivate *priv;
522 
523   priv = css_provider->priv = gtk_css_provider_get_instance_private (css_provider);
524 
525   priv->rulesets = g_array_new (FALSE, FALSE, sizeof (GtkCssRuleset));
526 
527   priv->symbolic_colors = g_hash_table_new_full (g_str_hash, g_str_equal,
528                                                  (GDestroyNotify) g_free,
529                                                  (GDestroyNotify) _gtk_css_value_unref);
530   priv->keyframes = g_hash_table_new_full (g_str_hash, g_str_equal,
531                                            (GDestroyNotify) g_free,
532                                            (GDestroyNotify) _gtk_css_keyframes_unref);
533 }
534 
535 static void
verify_tree_match_results(GtkCssProvider * provider,const GtkCssMatcher * matcher,GPtrArray * tree_rules)536 verify_tree_match_results (GtkCssProvider *provider,
537 			   const GtkCssMatcher *matcher,
538 			   GPtrArray *tree_rules)
539 {
540 #ifdef VERIFY_TREE
541   GtkCssProviderPrivate *priv = provider->priv;
542   GtkCssRuleset *ruleset;
543   gboolean should_match;
544   int i, j;
545 
546   for (i = 0; i < priv->rulesets->len; i++)
547     {
548       gboolean found = FALSE;
549 
550       ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i);
551 
552       for (j = 0; j < tree_rules->len; j++)
553 	{
554 	  if (ruleset == tree_rules->pdata[j])
555 	    {
556 	      found = TRUE;
557 	      break;
558 	    }
559 	}
560       should_match = _gtk_css_selector_matches (ruleset->selector, matcher);
561       if (found != !!should_match)
562 	{
563 	  g_error ("expected rule '%s' to %s, but it %s",
564 		   _gtk_css_selector_to_string (ruleset->selector),
565 		   should_match ? "match" : "not match",
566 		   found ? "matched" : "didn't match");
567 	}
568     }
569 #endif
570 }
571 
572 static void
verify_tree_get_change_results(GtkCssProvider * provider,const GtkCssMatcher * matcher,GtkCssChange change)573 verify_tree_get_change_results (GtkCssProvider *provider,
574 				const GtkCssMatcher *matcher,
575 				GtkCssChange change)
576 {
577 #ifdef VERIFY_TREE
578   {
579     GtkCssChange verify_change = 0;
580     GPtrArray *tree_rules;
581     int i;
582 
583     tree_rules = _gtk_css_selector_tree_match_all (provider->priv->tree, matcher);
584     if (tree_rules)
585       {
586         verify_tree_match_results (provider, matcher, tree_rules);
587 
588         for (i = tree_rules->len - 1; i >= 0; i--)
589           {
590 	    GtkCssRuleset *ruleset;
591 
592             ruleset = tree_rules->pdata[i];
593 
594             verify_change |= _gtk_css_selector_get_change (ruleset->selector);
595           }
596 
597         g_ptr_array_free (tree_rules, TRUE);
598       }
599 
600     if (change != verify_change)
601       {
602 	GString *s;
603 
604 	s = g_string_new ("");
605 	g_string_append (s, "expected change ");
606         gtk_css_change_print (verify_change, s);
607         g_string_append (s, ", but it was ");
608         gtk_css_change_print (change, s);
609 	if ((change & ~verify_change) != 0)
610           {
611 	    g_string_append (s, ", unexpectedly set: ");
612             gtk_css_change_print (change & ~verify_change, s);
613           }
614 	if ((~change & verify_change) != 0)
615           {
616 	    g_string_append_printf (s, ", unexpectedly not set: ");
617             gtk_css_change_print (~change & verify_change, s);
618           }
619 	g_warning (s->str);
620 	g_string_free (s, TRUE);
621       }
622   }
623 #endif
624 }
625 
626 
627 static gboolean
gtk_css_provider_get_style_property(GtkStyleProvider * provider,GtkWidgetPath * path,GtkStateFlags state,GParamSpec * pspec,GValue * value)628 gtk_css_provider_get_style_property (GtkStyleProvider *provider,
629                                      GtkWidgetPath    *path,
630                                      GtkStateFlags     state,
631                                      GParamSpec       *pspec,
632                                      GValue           *value)
633 {
634   GtkCssProvider *css_provider = GTK_CSS_PROVIDER (provider);
635   GtkCssProviderPrivate *priv = css_provider->priv;
636   WidgetPropertyValue *val;
637   GPtrArray *tree_rules;
638   GtkCssMatcher matcher;
639   gboolean found = FALSE;
640   gchar *prop_name;
641   gint i;
642 
643   if (state == gtk_widget_path_iter_get_state (path, -1))
644     {
645       gtk_widget_path_ref (path);
646     }
647   else
648     {
649       path = gtk_widget_path_copy (path);
650       gtk_widget_path_iter_set_state (path, -1, state);
651     }
652 
653   if (!_gtk_css_matcher_init (&matcher, path, NULL))
654     {
655       gtk_widget_path_unref (path);
656       return FALSE;
657     }
658 
659   tree_rules = _gtk_css_selector_tree_match_all (priv->tree, &matcher);
660   if (tree_rules)
661     {
662       verify_tree_match_results (css_provider, &matcher, tree_rules);
663 
664       prop_name = g_strdup_printf ("-%s-%s",
665                                    g_type_name (pspec->owner_type),
666                                    pspec->name);
667 
668       for (i = tree_rules->len - 1; i >= 0; i--)
669         {
670           GtkCssRuleset *ruleset = tree_rules->pdata[i];
671 
672           if (ruleset->widget_style == NULL)
673             continue;
674 
675           for (val = ruleset->widget_style; val != NULL; val = val->next)
676             {
677               if (strcmp (val->name, prop_name) == 0)
678                 {
679                   GtkCssScanner *scanner;
680 
681 	          scanner = gtk_css_scanner_new (css_provider,
682                                                  NULL,
683                                                  val->section,
684                                                  val->section != NULL ? gtk_css_section_get_file (val->section) : NULL,
685                                                  val->value);
686                   if (!val->section)
687                     gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_VALUE);
688                   found = _gtk_css_style_funcs_parse_value (value, scanner->parser);
689                   if (!val->section)
690                     gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE);
691                   gtk_css_scanner_destroy (scanner);
692 	          break;
693                 }
694             }
695 
696           if (found)
697             break;
698         }
699 
700       g_free (prop_name);
701       g_ptr_array_free (tree_rules, TRUE);
702     }
703 
704   gtk_widget_path_unref (path);
705 
706   return found;
707 }
708 
709 static void
gtk_css_style_provider_iface_init(GtkStyleProviderIface * iface)710 gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface)
711 {
712   iface->get_style_property = gtk_css_provider_get_style_property;
713 }
714 
715 static GtkCssValue *
gtk_css_style_provider_get_color(GtkStyleProviderPrivate * provider,const char * name)716 gtk_css_style_provider_get_color (GtkStyleProviderPrivate *provider,
717                                   const char              *name)
718 {
719   GtkCssProvider *css_provider = GTK_CSS_PROVIDER (provider);
720 
721   return g_hash_table_lookup (css_provider->priv->symbolic_colors, name);
722 }
723 
724 static GtkCssKeyframes *
gtk_css_style_provider_get_keyframes(GtkStyleProviderPrivate * provider,const char * name)725 gtk_css_style_provider_get_keyframes (GtkStyleProviderPrivate *provider,
726                                       const char              *name)
727 {
728   GtkCssProvider *css_provider = GTK_CSS_PROVIDER (provider);
729 
730   return g_hash_table_lookup (css_provider->priv->keyframes, name);
731 }
732 
733 static void
gtk_css_style_provider_lookup(GtkStyleProviderPrivate * provider,const GtkCssMatcher * matcher,GtkCssLookup * lookup,GtkCssChange * change)734 gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider,
735                                const GtkCssMatcher     *matcher,
736                                GtkCssLookup            *lookup,
737                                GtkCssChange            *change)
738 {
739   GtkCssProvider *css_provider;
740   GtkCssProviderPrivate *priv;
741   GtkCssRuleset *ruleset;
742   guint j;
743   int i;
744   GPtrArray *tree_rules;
745 
746   css_provider = GTK_CSS_PROVIDER (provider);
747   priv = css_provider->priv;
748 
749   tree_rules = _gtk_css_selector_tree_match_all (priv->tree, matcher);
750   if (tree_rules)
751     {
752       verify_tree_match_results (css_provider, matcher, tree_rules);
753 
754       for (i = tree_rules->len - 1; i >= 0; i--)
755         {
756           ruleset = tree_rules->pdata[i];
757 
758           if (ruleset->styles == NULL)
759             continue;
760 
761           if (!_gtk_bitmask_intersects (_gtk_css_lookup_get_missing (lookup),
762                                         ruleset->set_styles))
763           continue;
764 
765           for (j = 0; j < ruleset->n_styles; j++)
766             {
767               GtkCssStyleProperty *prop = ruleset->styles[j].property;
768               guint id = _gtk_css_style_property_get_id (prop);
769 
770               if (!_gtk_css_lookup_is_missing (lookup, id))
771                 continue;
772 
773               _gtk_css_lookup_set (lookup,
774                                    id,
775                                    ruleset->styles[j].section,
776                                   ruleset->styles[j].value);
777             }
778 
779           if (_gtk_bitmask_is_empty (_gtk_css_lookup_get_missing (lookup)))
780             break;
781         }
782 
783       g_ptr_array_free (tree_rules, TRUE);
784     }
785 
786   if (change)
787     {
788       GtkCssMatcher change_matcher;
789 
790       _gtk_css_matcher_superset_init (&change_matcher, matcher, GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_CLASS);
791 
792       *change = _gtk_css_selector_tree_get_change_all (priv->tree, &change_matcher);
793       verify_tree_get_change_results (css_provider, &change_matcher, *change);
794     }
795 }
796 
797 static void
gtk_css_style_provider_private_iface_init(GtkStyleProviderPrivateInterface * iface)798 gtk_css_style_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface)
799 {
800   iface->get_color = gtk_css_style_provider_get_color;
801   iface->get_keyframes = gtk_css_style_provider_get_keyframes;
802   iface->lookup = gtk_css_style_provider_lookup;
803   iface->emit_error = gtk_css_style_provider_emit_error;
804 }
805 
806 static void
gtk_css_provider_finalize(GObject * object)807 gtk_css_provider_finalize (GObject *object)
808 {
809   GtkCssProvider *css_provider;
810   GtkCssProviderPrivate *priv;
811   guint i;
812 
813   css_provider = GTK_CSS_PROVIDER (object);
814   priv = css_provider->priv;
815 
816   for (i = 0; i < priv->rulesets->len; i++)
817     gtk_css_ruleset_clear (&g_array_index (priv->rulesets, GtkCssRuleset, i));
818 
819   g_array_free (priv->rulesets, TRUE);
820   _gtk_css_selector_tree_free (priv->tree);
821 
822   g_hash_table_destroy (priv->symbolic_colors);
823   g_hash_table_destroy (priv->keyframes);
824 
825   if (priv->resource)
826     {
827       g_resources_unregister (priv->resource);
828       g_resource_unref (priv->resource);
829       priv->resource = NULL;
830     }
831 
832   g_free (priv->path);
833 
834   G_OBJECT_CLASS (gtk_css_provider_parent_class)->finalize (object);
835 }
836 
837 /**
838  * gtk_css_provider_new:
839  *
840  * Returns a newly created #GtkCssProvider.
841  *
842  * Returns: A new #GtkCssProvider
843  **/
844 GtkCssProvider *
gtk_css_provider_new(void)845 gtk_css_provider_new (void)
846 {
847   return g_object_new (GTK_TYPE_CSS_PROVIDER, NULL);
848 }
849 
850 static void
gtk_css_provider_take_error(GtkCssProvider * provider,GtkCssScanner * scanner,GError * error)851 gtk_css_provider_take_error (GtkCssProvider *provider,
852                              GtkCssScanner  *scanner,
853                              GError         *error)
854 {
855   gtk_css_provider_emit_error (provider, scanner, error);
856   g_error_free (error);
857 }
858 
859 static void
gtk_css_provider_error_literal(GtkCssProvider * provider,GtkCssScanner * scanner,GQuark domain,gint code,const char * message)860 gtk_css_provider_error_literal (GtkCssProvider *provider,
861                                 GtkCssScanner  *scanner,
862                                 GQuark          domain,
863                                 gint            code,
864                                 const char     *message)
865 {
866   gtk_css_provider_take_error (provider,
867                                scanner,
868                                g_error_new_literal (domain, code, message));
869 }
870 
871 static void
872 gtk_css_provider_error (GtkCssProvider *provider,
873                         GtkCssScanner  *scanner,
874                         GQuark          domain,
875                         gint            code,
876                         const char     *format,
877                         ...)  G_GNUC_PRINTF (5, 6);
878 static void
gtk_css_provider_error(GtkCssProvider * provider,GtkCssScanner * scanner,GQuark domain,gint code,const char * format,...)879 gtk_css_provider_error (GtkCssProvider *provider,
880                         GtkCssScanner  *scanner,
881                         GQuark          domain,
882                         gint            code,
883                         const char     *format,
884                         ...)
885 {
886   GError *error;
887   va_list args;
888 
889   gtk_internal_return_if_fail (GTK_IS_CSS_PROVIDER (provider));
890   gtk_internal_return_if_fail (scanner != NULL);
891 
892   va_start (args, format);
893   error = g_error_new_valist (domain, code, format, args);
894   va_end (args);
895 
896   gtk_css_provider_take_error (provider, scanner, error);
897 }
898 
899 static void
gtk_css_provider_invalid_token(GtkCssProvider * provider,GtkCssScanner * scanner,const char * expected)900 gtk_css_provider_invalid_token (GtkCssProvider *provider,
901                                 GtkCssScanner  *scanner,
902                                 const char     *expected)
903 {
904   gtk_css_provider_error (provider,
905                           scanner,
906                           GTK_CSS_PROVIDER_ERROR,
907                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
908                           "expected %s", expected);
909 }
910 
911 static void
css_provider_commit(GtkCssProvider * css_provider,GSList * selectors,GtkCssRuleset * ruleset)912 css_provider_commit (GtkCssProvider *css_provider,
913                      GSList         *selectors,
914                      GtkCssRuleset  *ruleset)
915 {
916   GtkCssProviderPrivate *priv;
917   GSList *l;
918 
919   priv = css_provider->priv;
920 
921   if (ruleset->styles == NULL && ruleset->widget_style == NULL)
922     {
923       g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
924       return;
925     }
926 
927   for (l = selectors; l; l = l->next)
928     {
929       GtkCssRuleset new;
930 
931       gtk_css_ruleset_init_copy (&new, ruleset, l->data);
932 
933       g_array_append_val (priv->rulesets, new);
934     }
935 
936   g_slist_free (selectors);
937 }
938 
939 static void
gtk_css_provider_reset(GtkCssProvider * css_provider)940 gtk_css_provider_reset (GtkCssProvider *css_provider)
941 {
942   GtkCssProviderPrivate *priv;
943   guint i;
944 
945   priv = css_provider->priv;
946 
947   if (priv->resource)
948     {
949       g_resources_unregister (priv->resource);
950       g_resource_unref (priv->resource);
951       priv->resource = NULL;
952     }
953 
954   if (priv->path)
955     {
956       g_free (priv->path);
957       priv->path = NULL;
958     }
959 
960   g_hash_table_remove_all (priv->symbolic_colors);
961   g_hash_table_remove_all (priv->keyframes);
962 
963   for (i = 0; i < priv->rulesets->len; i++)
964     gtk_css_ruleset_clear (&g_array_index (priv->rulesets, GtkCssRuleset, i));
965   g_array_set_size (priv->rulesets, 0);
966   _gtk_css_selector_tree_free (priv->tree);
967   priv->tree = NULL;
968 
969 }
970 
971 static void
gtk_css_provider_propagate_error(GtkCssProvider * provider,GtkCssSection * section,const GError * error,GError ** propagate_to)972 gtk_css_provider_propagate_error (GtkCssProvider  *provider,
973                                   GtkCssSection   *section,
974                                   const GError    *error,
975                                   GError         **propagate_to)
976 {
977 
978   char *s;
979 
980   /* don't fail for deprecations */
981   if (g_error_matches (error, GTK_CSS_PROVIDER_ERROR, GTK_CSS_PROVIDER_ERROR_DEPRECATED))
982     {
983       s = _gtk_css_section_to_string (section);
984       g_warning ("Theme parsing error: %s: %s", s, error->message);
985       g_free (s);
986       return;
987     }
988 
989   /* we already set an error. And we'd like to keep the first one */
990   if (*propagate_to)
991     return;
992 
993   *propagate_to = g_error_copy (error);
994   if (section)
995     {
996       s = _gtk_css_section_to_string (section);
997       g_prefix_error (propagate_to, "%s", s);
998       g_free (s);
999     }
1000 }
1001 
1002 static gboolean
parse_import(GtkCssScanner * scanner)1003 parse_import (GtkCssScanner *scanner)
1004 {
1005   GFile *file;
1006 
1007   gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_IMPORT);
1008 
1009   if (!_gtk_css_parser_try (scanner->parser, "@import", TRUE))
1010     {
1011       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_IMPORT);
1012       return FALSE;
1013     }
1014 
1015   if (_gtk_css_parser_is_string (scanner->parser))
1016     {
1017       char *uri;
1018 
1019       uri = _gtk_css_parser_read_string (scanner->parser);
1020       file = _gtk_css_parser_get_file_for_path (scanner->parser, uri);
1021       g_free (uri);
1022     }
1023   else
1024     {
1025       file = _gtk_css_parser_read_url (scanner->parser);
1026     }
1027 
1028   if (file == NULL)
1029     {
1030       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1031       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_IMPORT);
1032       return TRUE;
1033     }
1034 
1035   if (!_gtk_css_parser_try (scanner->parser, ";", FALSE))
1036     {
1037       gtk_css_provider_invalid_token (scanner->provider, scanner, "semicolon");
1038       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1039     }
1040   else if (gtk_css_scanner_would_recurse (scanner, file))
1041     {
1042        char *path = g_file_get_path (file);
1043        gtk_css_provider_error (scanner->provider,
1044                                scanner,
1045                                GTK_CSS_PROVIDER_ERROR,
1046                                GTK_CSS_PROVIDER_ERROR_IMPORT,
1047                                "Loading '%s' would recurse",
1048                                path);
1049        g_free (path);
1050     }
1051   else
1052     {
1053       gtk_css_provider_load_internal (scanner->provider,
1054                                       scanner,
1055                                       file,
1056                                       NULL,
1057                                       NULL);
1058     }
1059 
1060   g_object_unref (file);
1061 
1062   gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_IMPORT);
1063   _gtk_css_parser_skip_whitespace (scanner->parser);
1064 
1065   return TRUE;
1066 }
1067 
1068 static gboolean
parse_color_definition(GtkCssScanner * scanner)1069 parse_color_definition (GtkCssScanner *scanner)
1070 {
1071   GtkCssValue *color;
1072   char *name;
1073 
1074   gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
1075 
1076   if (!_gtk_css_parser_try (scanner->parser, "@define-color", TRUE))
1077     {
1078       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
1079       return FALSE;
1080     }
1081 
1082   name = _gtk_css_parser_try_name (scanner->parser, TRUE);
1083   if (name == NULL)
1084     {
1085       gtk_css_provider_error_literal (scanner->provider,
1086                                       scanner,
1087                                       GTK_CSS_PROVIDER_ERROR,
1088                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1089                                       "Not a valid color name");
1090       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1091       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
1092       return TRUE;
1093     }
1094 
1095   color = _gtk_css_color_value_parse (scanner->parser);
1096   if (color == NULL)
1097     {
1098       g_free (name);
1099       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1100       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
1101       return TRUE;
1102     }
1103 
1104   if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
1105     {
1106       g_free (name);
1107       _gtk_css_value_unref (color);
1108       gtk_css_provider_error_literal (scanner->provider,
1109                                       scanner,
1110                                       GTK_CSS_PROVIDER_ERROR,
1111                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1112                                       "Missing semicolon at end of color definition");
1113       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1114 
1115       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
1116       return TRUE;
1117     }
1118 
1119   g_hash_table_insert (scanner->provider->priv->symbolic_colors, name, color);
1120 
1121   gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
1122   return TRUE;
1123 }
1124 
1125 static gboolean
parse_binding_set(GtkCssScanner * scanner)1126 parse_binding_set (GtkCssScanner *scanner)
1127 {
1128   GtkBindingSet *binding_set;
1129   char *name;
1130 
1131   gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_BINDING_SET);
1132 
1133   if (!_gtk_css_parser_try (scanner->parser, "@binding-set", TRUE))
1134     {
1135       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_BINDING_SET);
1136       return FALSE;
1137     }
1138 
1139   name = _gtk_css_parser_try_ident (scanner->parser, TRUE);
1140   if (name == NULL)
1141     {
1142       gtk_css_provider_error_literal (scanner->provider,
1143                                       scanner,
1144                                       GTK_CSS_PROVIDER_ERROR,
1145                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1146                                       "Expected name for binding set");
1147       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1148       goto skip_semicolon;
1149     }
1150 
1151   binding_set = gtk_binding_set_find (name);
1152   if (!binding_set)
1153     {
1154       binding_set = gtk_binding_set_new (name);
1155       binding_set->parsed = TRUE;
1156     }
1157   g_free (name);
1158 
1159   if (!_gtk_css_parser_try (scanner->parser, "{", TRUE))
1160     {
1161       gtk_css_provider_error_literal (scanner->provider,
1162                                       scanner,
1163                                       GTK_CSS_PROVIDER_ERROR,
1164                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1165                                       "Expected '{' for binding set");
1166       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1167       goto skip_semicolon;
1168     }
1169 
1170   while (!_gtk_css_parser_is_eof (scanner->parser) &&
1171          !_gtk_css_parser_begins_with (scanner->parser, '}'))
1172     {
1173       name = _gtk_css_parser_read_value (scanner->parser);
1174       if (name == NULL)
1175         {
1176           _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1177           continue;
1178         }
1179 
1180       if (gtk_binding_entry_add_signal_from_string (binding_set, name) != G_TOKEN_NONE)
1181         {
1182           gtk_css_provider_error_literal (scanner->provider,
1183                                           scanner,
1184                                           GTK_CSS_PROVIDER_ERROR,
1185                                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
1186                                           "Failed to parse binding set.");
1187         }
1188 
1189       g_free (name);
1190 
1191       if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
1192         {
1193           if (!_gtk_css_parser_begins_with (scanner->parser, '}') &&
1194               !_gtk_css_parser_is_eof (scanner->parser))
1195             {
1196               gtk_css_provider_error_literal (scanner->provider,
1197                                               scanner,
1198                                               GTK_CSS_PROVIDER_ERROR,
1199                                               GTK_CSS_PROVIDER_ERROR_SYNTAX,
1200                                               "Expected semicolon");
1201               _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1202             }
1203         }
1204     }
1205 
1206   if (!_gtk_css_parser_try (scanner->parser, "}", TRUE))
1207     {
1208       gtk_css_provider_error_literal (scanner->provider,
1209                                       scanner,
1210                                       GTK_CSS_PROVIDER_ERROR,
1211                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1212                                       "expected '}' after declarations");
1213       if (!_gtk_css_parser_is_eof (scanner->parser))
1214         _gtk_css_parser_resync (scanner->parser, FALSE, 0);
1215     }
1216 
1217 skip_semicolon:
1218   if (_gtk_css_parser_begins_with (scanner->parser, ';'))
1219     {
1220       gtk_css_provider_error_literal (scanner->provider,
1221                                       scanner,
1222                                       GTK_CSS_PROVIDER_ERROR,
1223                                       GTK_CSS_PROVIDER_ERROR_DEPRECATED,
1224                                       "Nonstandard semicolon at end of binding set");
1225       _gtk_css_parser_try (scanner->parser, ";", TRUE);
1226     }
1227 
1228   gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_BINDING_SET);
1229 
1230   return TRUE;
1231 }
1232 
1233 static gboolean
parse_keyframes(GtkCssScanner * scanner)1234 parse_keyframes (GtkCssScanner *scanner)
1235 {
1236   GtkCssKeyframes *keyframes;
1237   char *name;
1238 
1239   gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_KEYFRAMES);
1240 
1241   if (!_gtk_css_parser_try (scanner->parser, "@keyframes", TRUE))
1242     {
1243       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_KEYFRAMES);
1244       return FALSE;
1245     }
1246 
1247   name = _gtk_css_parser_try_ident (scanner->parser, TRUE);
1248   if (name == NULL)
1249     {
1250       gtk_css_provider_error_literal (scanner->provider,
1251                                       scanner,
1252                                       GTK_CSS_PROVIDER_ERROR,
1253                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1254                                       "Expected name for keyframes");
1255       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1256       goto exit;
1257     }
1258 
1259   if (!_gtk_css_parser_try (scanner->parser, "{", TRUE))
1260     {
1261       gtk_css_provider_error_literal (scanner->provider,
1262                                       scanner,
1263                                       GTK_CSS_PROVIDER_ERROR,
1264                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1265                                       "Expected '{' for keyframes");
1266       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1267       g_free (name);
1268       goto exit;
1269     }
1270 
1271   keyframes = _gtk_css_keyframes_parse (scanner->parser);
1272   if (keyframes == NULL)
1273     {
1274       _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1275       g_free (name);
1276       goto exit;
1277     }
1278 
1279   g_hash_table_insert (scanner->provider->priv->keyframes, name, keyframes);
1280 
1281   if (!_gtk_css_parser_try (scanner->parser, "}", TRUE))
1282     {
1283       gtk_css_provider_error_literal (scanner->provider,
1284                                       scanner,
1285                                       GTK_CSS_PROVIDER_ERROR,
1286                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1287                                       "expected '}' after declarations");
1288       if (!_gtk_css_parser_is_eof (scanner->parser))
1289         _gtk_css_parser_resync (scanner->parser, FALSE, 0);
1290     }
1291 
1292 exit:
1293   gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_KEYFRAMES);
1294 
1295   return TRUE;
1296 }
1297 
1298 static void
parse_at_keyword(GtkCssScanner * scanner)1299 parse_at_keyword (GtkCssScanner *scanner)
1300 {
1301   if (parse_import (scanner))
1302     return;
1303   if (parse_color_definition (scanner))
1304     return;
1305   if (parse_binding_set (scanner))
1306     return;
1307   if (parse_keyframes (scanner))
1308     return;
1309 
1310   else
1311     {
1312       gtk_css_provider_error_literal (scanner->provider,
1313                                       scanner,
1314                                       GTK_CSS_PROVIDER_ERROR,
1315                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1316                                       "unknown @ rule");
1317       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1318     }
1319 }
1320 
1321 static GSList *
parse_selector_list(GtkCssScanner * scanner)1322 parse_selector_list (GtkCssScanner *scanner)
1323 {
1324   GSList *selectors = NULL;
1325 
1326   gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_SELECTOR);
1327 
1328   do {
1329       GtkCssSelector *select = _gtk_css_selector_parse (scanner->parser);
1330 
1331       if (select == NULL)
1332         {
1333           g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
1334           _gtk_css_parser_resync (scanner->parser, FALSE, 0);
1335           gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_SELECTOR);
1336           return NULL;
1337         }
1338 
1339       selectors = g_slist_prepend (selectors, select);
1340     }
1341   while (_gtk_css_parser_try (scanner->parser, ",", TRUE));
1342 
1343   gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_SELECTOR);
1344 
1345   return selectors;
1346 }
1347 
1348 static gboolean
name_is_style_property(const char * name)1349 name_is_style_property (const char *name)
1350 {
1351   if (name[0] != '-')
1352     return FALSE;
1353 
1354   if (g_str_has_prefix (name, "-gtk-"))
1355     return FALSE;
1356 
1357   return TRUE;
1358 }
1359 
1360 static void
warn_if_deprecated(GtkCssScanner * scanner,const gchar * name)1361 warn_if_deprecated (GtkCssScanner *scanner,
1362                     const gchar   *name)
1363 {
1364   gchar *n = NULL;
1365   gchar *p;
1366   const gchar *type_name;
1367   const gchar *property_name;
1368   GType type;
1369   GTypeClass *class = NULL;
1370   GParamSpec *pspec;
1371 
1372   n = g_strdup (name);
1373 
1374   /* skip initial - */
1375   type_name = n + 1;
1376 
1377   p = strchr (type_name, '-');
1378   if (!p)
1379     goto out;
1380 
1381   p[0] = '\0';
1382   property_name = p + 1;
1383 
1384   type = g_type_from_name (type_name);
1385   if (type == G_TYPE_INVALID ||
1386       !g_type_is_a (type, GTK_TYPE_WIDGET))
1387     goto out;
1388 
1389   class = g_type_class_ref (type);
1390   pspec = gtk_widget_class_find_style_property (GTK_WIDGET_CLASS (class), property_name);
1391   if (!pspec)
1392     goto out;
1393 
1394   if (!(pspec->flags & G_PARAM_DEPRECATED))
1395     goto out;
1396 
1397   _gtk_css_parser_error_full (scanner->parser,
1398                               GTK_CSS_PROVIDER_ERROR_DEPRECATED,
1399                               "The style property %s:%s is deprecated and shouldn't be "
1400                               "used anymore. It will be removed in a future version",
1401                               g_type_name (pspec->owner_type), pspec->name);
1402 
1403 out:
1404   g_free (n);
1405   if (class)
1406     g_type_class_unref (class);
1407 }
1408 
1409 static void
parse_declaration(GtkCssScanner * scanner,GtkCssRuleset * ruleset)1410 parse_declaration (GtkCssScanner *scanner,
1411                    GtkCssRuleset *ruleset)
1412 {
1413   GtkStyleProperty *property;
1414   char *name;
1415 
1416   gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_DECLARATION);
1417 
1418   name = _gtk_css_parser_try_ident (scanner->parser, TRUE);
1419   if (name == NULL)
1420     goto check_for_semicolon;
1421 
1422   property = _gtk_style_property_lookup (name);
1423   if (property == NULL && !name_is_style_property (name))
1424     {
1425       gtk_css_provider_error (scanner->provider,
1426                               scanner,
1427                               GTK_CSS_PROVIDER_ERROR,
1428                               GTK_CSS_PROVIDER_ERROR_NAME,
1429                               "'%s' is not a valid property name",
1430                               name);
1431       _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1432       g_free (name);
1433       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
1434       return;
1435     }
1436 
1437   if (property != NULL && strcmp (name, property->name) != 0)
1438     {
1439       gtk_css_provider_error (scanner->provider,
1440                               scanner,
1441                               GTK_CSS_PROVIDER_ERROR,
1442                               GTK_CSS_PROVIDER_ERROR_DEPRECATED,
1443                               "The '%s' property has been renamed to '%s'",
1444                               name, property->name);
1445     }
1446   else if (strcmp (name, "engine") == 0)
1447     {
1448       gtk_css_provider_error (scanner->provider,
1449                               scanner,
1450                               GTK_CSS_PROVIDER_ERROR,
1451                               GTK_CSS_PROVIDER_ERROR_DEPRECATED,
1452                               "The '%s' property is ignored",
1453                               name);
1454     }
1455 
1456   if (!_gtk_css_parser_try (scanner->parser, ":", TRUE))
1457     {
1458       gtk_css_provider_invalid_token (scanner->provider, scanner, "':'");
1459       _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1460       g_free (name);
1461       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
1462       return;
1463     }
1464 
1465   if (property)
1466     {
1467       GtkCssValue *value;
1468 
1469       g_free (name);
1470 
1471       gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_VALUE);
1472 
1473       value = _gtk_style_property_parse_value (property,
1474                                                scanner->parser);
1475 
1476       if (value == NULL)
1477         {
1478           _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1479           gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE);
1480           gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
1481           return;
1482         }
1483 
1484       if (!_gtk_css_parser_begins_with (scanner->parser, ';') &&
1485           !_gtk_css_parser_begins_with (scanner->parser, '}') &&
1486           !_gtk_css_parser_is_eof (scanner->parser))
1487         {
1488           gtk_css_provider_error (scanner->provider,
1489                                   scanner,
1490                                   GTK_CSS_PROVIDER_ERROR,
1491                                   GTK_CSS_PROVIDER_ERROR_SYNTAX,
1492                                   "Junk at end of value for %s", property->name);
1493           _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1494           gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE);
1495           gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
1496           return;
1497         }
1498 
1499       if (GTK_IS_CSS_SHORTHAND_PROPERTY (property))
1500         {
1501           GtkCssShorthandProperty *shorthand = GTK_CSS_SHORTHAND_PROPERTY (property);
1502           guint i;
1503 
1504           for (i = 0; i < _gtk_css_shorthand_property_get_n_subproperties (shorthand); i++)
1505             {
1506               GtkCssStyleProperty *child = _gtk_css_shorthand_property_get_subproperty (shorthand, i);
1507               GtkCssValue *sub = _gtk_css_array_value_get_nth (value, i);
1508 
1509               gtk_css_ruleset_add (ruleset, child, _gtk_css_value_ref (sub), scanner->section);
1510             }
1511 
1512             _gtk_css_value_unref (value);
1513         }
1514       else if (GTK_IS_CSS_STYLE_PROPERTY (property))
1515         {
1516           gtk_css_ruleset_add (ruleset, GTK_CSS_STYLE_PROPERTY (property), value, scanner->section);
1517         }
1518       else
1519         {
1520           g_assert_not_reached ();
1521           _gtk_css_value_unref (value);
1522         }
1523 
1524 
1525       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE);
1526     }
1527   else if (name_is_style_property (name))
1528     {
1529       char *value_str;
1530 
1531       warn_if_deprecated (scanner, name);
1532 
1533       gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_VALUE);
1534 
1535       value_str = _gtk_css_parser_read_value (scanner->parser);
1536       if (value_str)
1537         {
1538           WidgetPropertyValue *val;
1539 
1540           val = widget_property_value_new (name, scanner->section);
1541 	  val->value = value_str;
1542 
1543           gtk_css_ruleset_add_style (ruleset, name, val);
1544         }
1545       else
1546         {
1547           _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1548           gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE);
1549           gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
1550           return;
1551         }
1552 
1553       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE);
1554     }
1555   else
1556     g_free (name);
1557 
1558 check_for_semicolon:
1559   gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
1560 
1561   if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
1562     {
1563       if (!_gtk_css_parser_begins_with (scanner->parser, '}') &&
1564           !_gtk_css_parser_is_eof (scanner->parser))
1565         {
1566           gtk_css_provider_error_literal (scanner->provider,
1567                                           scanner,
1568                                           GTK_CSS_PROVIDER_ERROR,
1569                                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
1570                                           "Expected semicolon");
1571           _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1572         }
1573     }
1574 }
1575 
1576 static void
parse_declarations(GtkCssScanner * scanner,GtkCssRuleset * ruleset)1577 parse_declarations (GtkCssScanner *scanner,
1578                     GtkCssRuleset *ruleset)
1579 {
1580   while (!_gtk_css_parser_is_eof (scanner->parser) &&
1581          !_gtk_css_parser_begins_with (scanner->parser, '}'))
1582     {
1583       parse_declaration (scanner, ruleset);
1584     }
1585 }
1586 
1587 static void
parse_ruleset(GtkCssScanner * scanner)1588 parse_ruleset (GtkCssScanner *scanner)
1589 {
1590   GSList *selectors;
1591   GtkCssRuleset ruleset = { 0, };
1592 
1593   gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_RULESET);
1594 
1595   selectors = parse_selector_list (scanner);
1596   if (selectors == NULL)
1597     {
1598       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET);
1599       return;
1600     }
1601 
1602   if (!_gtk_css_parser_try (scanner->parser, "{", TRUE))
1603     {
1604       gtk_css_provider_error_literal (scanner->provider,
1605                                       scanner,
1606                                       GTK_CSS_PROVIDER_ERROR,
1607                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1608                                       "expected '{' after selectors");
1609       _gtk_css_parser_resync (scanner->parser, FALSE, 0);
1610       g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
1611       gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET);
1612       return;
1613     }
1614 
1615   parse_declarations (scanner, &ruleset);
1616 
1617   if (!_gtk_css_parser_try (scanner->parser, "}", TRUE))
1618     {
1619       gtk_css_provider_error_literal (scanner->provider,
1620                                       scanner,
1621                                       GTK_CSS_PROVIDER_ERROR,
1622                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1623                                       "expected '}' after declarations");
1624       if (!_gtk_css_parser_is_eof (scanner->parser))
1625         {
1626           _gtk_css_parser_resync (scanner->parser, FALSE, 0);
1627           g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
1628           gtk_css_ruleset_clear (&ruleset);
1629           gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET);
1630         }
1631     }
1632 
1633   css_provider_commit (scanner->provider, selectors, &ruleset);
1634   gtk_css_ruleset_clear (&ruleset);
1635   gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET);
1636 }
1637 
1638 static void
parse_statement(GtkCssScanner * scanner)1639 parse_statement (GtkCssScanner *scanner)
1640 {
1641   if (_gtk_css_parser_begins_with (scanner->parser, '@'))
1642     parse_at_keyword (scanner);
1643   else
1644     parse_ruleset (scanner);
1645 }
1646 
1647 static void
parse_stylesheet(GtkCssScanner * scanner)1648 parse_stylesheet (GtkCssScanner *scanner)
1649 {
1650   gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_DOCUMENT);
1651 
1652   _gtk_css_parser_skip_whitespace (scanner->parser);
1653 
1654   while (!_gtk_css_parser_is_eof (scanner->parser))
1655     {
1656       if (_gtk_css_parser_try (scanner->parser, "<!--", TRUE) ||
1657           _gtk_css_parser_try (scanner->parser, "-->", TRUE))
1658         continue;
1659 
1660       parse_statement (scanner);
1661     }
1662 
1663   gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DOCUMENT);
1664 }
1665 
1666 static int
gtk_css_provider_compare_rule(gconstpointer a_,gconstpointer b_)1667 gtk_css_provider_compare_rule (gconstpointer a_,
1668                                gconstpointer b_)
1669 {
1670   const GtkCssRuleset *a = (const GtkCssRuleset *) a_;
1671   const GtkCssRuleset *b = (const GtkCssRuleset *) b_;
1672   int compare;
1673 
1674   compare = _gtk_css_selector_compare (a->selector, b->selector);
1675   if (compare != 0)
1676     return compare;
1677 
1678   return 0;
1679 }
1680 
1681 static void
gtk_css_provider_postprocess(GtkCssProvider * css_provider)1682 gtk_css_provider_postprocess (GtkCssProvider *css_provider)
1683 {
1684   GtkCssProviderPrivate *priv = css_provider->priv;
1685   GtkCssSelectorTreeBuilder *builder;
1686   guint i;
1687 
1688   g_array_sort (priv->rulesets, gtk_css_provider_compare_rule);
1689 
1690   builder = _gtk_css_selector_tree_builder_new ();
1691   for (i = 0; i < priv->rulesets->len; i++)
1692     {
1693       GtkCssRuleset *ruleset;
1694 
1695       ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i);
1696 
1697       _gtk_css_selector_tree_builder_add (builder,
1698 					  ruleset->selector,
1699 					  &ruleset->selector_match,
1700 					  ruleset);
1701     }
1702 
1703   priv->tree = _gtk_css_selector_tree_builder_build (builder);
1704   _gtk_css_selector_tree_builder_free (builder);
1705 
1706 #ifndef VERIFY_TREE
1707   for (i = 0; i < priv->rulesets->len; i++)
1708     {
1709       GtkCssRuleset *ruleset;
1710 
1711       ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i);
1712 
1713       _gtk_css_selector_free (ruleset->selector);
1714       ruleset->selector = NULL;
1715     }
1716 #endif
1717 }
1718 
1719 static gboolean
gtk_css_provider_load_internal(GtkCssProvider * css_provider,GtkCssScanner * parent,GFile * file,const char * text,GError ** error)1720 gtk_css_provider_load_internal (GtkCssProvider *css_provider,
1721                                 GtkCssScanner  *parent,
1722                                 GFile          *file,
1723                                 const char     *text,
1724                                 GError        **error)
1725 {
1726   GBytes *free_bytes = NULL;
1727   GtkCssScanner *scanner;
1728   gulong error_handler;
1729 
1730   if (error)
1731     error_handler = g_signal_connect (css_provider,
1732                                       "parsing-error",
1733                                       G_CALLBACK (gtk_css_provider_propagate_error),
1734                                       error);
1735   else
1736     error_handler = 0; /* silence gcc */
1737 
1738   if (text == NULL)
1739     {
1740       GError *load_error = NULL;
1741 
1742       free_bytes = gtk_file_load_bytes (file, NULL, &load_error);
1743 
1744       if (free_bytes != NULL)
1745         {
1746           text = g_bytes_get_data (free_bytes, NULL);
1747         }
1748       else
1749         {
1750           if (parent == NULL)
1751             {
1752               scanner = gtk_css_scanner_new (css_provider,
1753                                              NULL,
1754                                              NULL,
1755                                              file,
1756                                              "");
1757 
1758               gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_DOCUMENT);
1759             }
1760           else
1761             scanner = parent;
1762 
1763           gtk_css_provider_error (css_provider,
1764                                   scanner,
1765                                   GTK_CSS_PROVIDER_ERROR,
1766                                   GTK_CSS_PROVIDER_ERROR_IMPORT,
1767                                   "Failed to import: %s",
1768                                   load_error->message);
1769 
1770           if (parent == NULL)
1771             {
1772               gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DOCUMENT);
1773 
1774               gtk_css_scanner_destroy (scanner);
1775             }
1776         }
1777     }
1778 
1779   if (text)
1780     {
1781       scanner = gtk_css_scanner_new (css_provider,
1782                                      parent,
1783                                      parent ? parent->section : NULL,
1784                                      file,
1785                                      text);
1786 
1787       parse_stylesheet (scanner);
1788 
1789       gtk_css_scanner_destroy (scanner);
1790 
1791       if (parent == NULL)
1792         gtk_css_provider_postprocess (css_provider);
1793     }
1794 
1795   if (free_bytes)
1796     g_bytes_unref (free_bytes);
1797 
1798   if (error)
1799     {
1800       g_signal_handler_disconnect (css_provider, error_handler);
1801 
1802       if (*error)
1803         {
1804           /* We clear all contents from the provider for backwards compat reasons */
1805           gtk_css_provider_reset (css_provider);
1806           return FALSE;
1807         }
1808     }
1809 
1810   return TRUE;
1811 }
1812 
1813 /**
1814  * gtk_css_provider_load_from_data:
1815  * @css_provider: a #GtkCssProvider
1816  * @data: (array length=length) (element-type guint8): CSS data loaded in memory
1817  * @length: the length of @data in bytes, or -1 for NUL terminated strings. If
1818  *   @length is not -1, the code will assume it is not NUL terminated and will
1819  *   potentially do a copy.
1820  * @error: (out) (allow-none): return location for a #GError, or %NULL
1821  *
1822  * Loads @data into @css_provider, and by doing so clears any previously loaded
1823  * information.
1824  *
1825  * Returns: %TRUE. The return value is deprecated and %FALSE will only be
1826  *     returned for backwards compatibility reasons if an @error is not
1827  *     %NULL and a loading error occurred. To track errors while loading
1828  *     CSS, connect to the #GtkCssProvider::parsing-error signal.
1829  **/
1830 gboolean
gtk_css_provider_load_from_data(GtkCssProvider * css_provider,const gchar * data,gssize length,GError ** error)1831 gtk_css_provider_load_from_data (GtkCssProvider  *css_provider,
1832                                  const gchar     *data,
1833                                  gssize           length,
1834                                  GError         **error)
1835 {
1836   char *free_data;
1837   gboolean ret;
1838 
1839   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
1840   g_return_val_if_fail (data != NULL, FALSE);
1841 
1842   if (length < 0)
1843     {
1844       length = strlen (data);
1845       free_data = NULL;
1846     }
1847   else
1848     {
1849       free_data = g_strndup (data, length);
1850       data = free_data;
1851     }
1852 
1853   gtk_css_provider_reset (css_provider);
1854 
1855   ret = gtk_css_provider_load_internal (css_provider, NULL, NULL, data, error);
1856 
1857   g_free (free_data);
1858 
1859   _gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (css_provider));
1860 
1861   return ret;
1862 }
1863 
1864 /**
1865  * gtk_css_provider_load_from_file:
1866  * @css_provider: a #GtkCssProvider
1867  * @file: #GFile pointing to a file to load
1868  * @error: (out) (allow-none): return location for a #GError, or %NULL
1869  *
1870  * Loads the data contained in @file into @css_provider, making it
1871  * clear any previously loaded information.
1872  *
1873  * Returns: %TRUE. The return value is deprecated and %FALSE will only be
1874  *     returned for backwards compatibility reasons if an @error is not
1875  *     %NULL and a loading error occurred. To track errors while loading
1876  *     CSS, connect to the #GtkCssProvider::parsing-error signal.
1877  **/
1878 gboolean
gtk_css_provider_load_from_file(GtkCssProvider * css_provider,GFile * file,GError ** error)1879 gtk_css_provider_load_from_file (GtkCssProvider  *css_provider,
1880                                  GFile           *file,
1881                                  GError         **error)
1882 {
1883   gboolean success;
1884 
1885   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
1886   g_return_val_if_fail (G_IS_FILE (file), FALSE);
1887 
1888   gtk_css_provider_reset (css_provider);
1889 
1890   success = gtk_css_provider_load_internal (css_provider, NULL, file, NULL, error);
1891 
1892   _gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (css_provider));
1893 
1894   return success;
1895 }
1896 
1897 /**
1898  * gtk_css_provider_load_from_path:
1899  * @css_provider: a #GtkCssProvider
1900  * @path: the path of a filename to load, in the GLib filename encoding
1901  * @error: (out) (allow-none): return location for a #GError, or %NULL
1902  *
1903  * Loads the data contained in @path into @css_provider, making it clear
1904  * any previously loaded information.
1905  *
1906  * Returns: %TRUE. The return value is deprecated and %FALSE will only be
1907  *     returned for backwards compatibility reasons if an @error is not
1908  *     %NULL and a loading error occurred. To track errors while loading
1909  *     CSS, connect to the #GtkCssProvider::parsing-error signal.
1910  **/
1911 gboolean
gtk_css_provider_load_from_path(GtkCssProvider * css_provider,const gchar * path,GError ** error)1912 gtk_css_provider_load_from_path (GtkCssProvider  *css_provider,
1913                                  const gchar     *path,
1914                                  GError         **error)
1915 {
1916   GFile *file;
1917   gboolean result;
1918 
1919   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
1920   g_return_val_if_fail (path != NULL, FALSE);
1921 
1922   file = g_file_new_for_path (path);
1923 
1924   result = gtk_css_provider_load_from_file (css_provider, file, error);
1925 
1926   g_object_unref (file);
1927 
1928   return result;
1929 }
1930 
1931 /**
1932  * gtk_css_provider_load_from_resource:
1933  * @css_provider: a #GtkCssProvider
1934  * @resource_path: a #GResource resource path
1935  *
1936  * Loads the data contained in the resource at @resource_path into
1937  * the #GtkCssProvider, clearing any previously loaded information.
1938  *
1939  * To track errors while loading CSS, connect to the
1940  * #GtkCssProvider::parsing-error signal.
1941  *
1942  * Since: 3.16
1943  */
1944 void
gtk_css_provider_load_from_resource(GtkCssProvider * css_provider,const gchar * resource_path)1945 gtk_css_provider_load_from_resource (GtkCssProvider *css_provider,
1946 			             const gchar    *resource_path)
1947 {
1948   GFile *file;
1949   gchar *uri, *escaped;
1950 
1951   g_return_if_fail (GTK_IS_CSS_PROVIDER (css_provider));
1952   g_return_if_fail (resource_path != NULL);
1953 
1954   escaped = g_uri_escape_string (resource_path,
1955 				 G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
1956   uri = g_strconcat ("resource://", escaped, NULL);
1957   g_free (escaped);
1958 
1959   file = g_file_new_for_uri (uri);
1960   g_free (uri);
1961 
1962   gtk_css_provider_load_from_file (css_provider, file, NULL);
1963 
1964   g_object_unref (file);
1965 }
1966 
1967 /**
1968  * gtk_css_provider_get_default:
1969  *
1970  * Returns the provider containing the style settings used as a
1971  * fallback for all widgets.
1972  *
1973  * Returns: (transfer none): The provider used for fallback styling.
1974  *          This memory is owned by GTK+, and you must not free it.
1975  *
1976  * Deprecated: 3.24: Use gtk_css_provider_new() instead.
1977  **/
1978 GtkCssProvider *
gtk_css_provider_get_default(void)1979 gtk_css_provider_get_default (void)
1980 {
1981   static GtkCssProvider *provider;
1982 
1983   if (G_UNLIKELY (!provider))
1984     {
1985       provider = gtk_css_provider_new ();
1986     }
1987 
1988   return provider;
1989 }
1990 
1991 gchar *
_gtk_get_theme_dir(void)1992 _gtk_get_theme_dir (void)
1993 {
1994   const gchar *var;
1995 
1996   var = g_getenv ("GTK_DATA_PREFIX");
1997   if (var == NULL)
1998     var = _gtk_get_data_prefix ();
1999   return g_build_filename (var, "share", "themes", NULL);
2000 }
2001 
2002 /* Return the path that this providers gtk.css was loaded from,
2003  * if it is part of a theme, otherwise NULL.
2004  */
2005 const gchar *
_gtk_css_provider_get_theme_dir(GtkCssProvider * provider)2006 _gtk_css_provider_get_theme_dir (GtkCssProvider *provider)
2007 {
2008   return provider->priv->path;
2009 }
2010 
2011 #if (GTK_MINOR_VERSION % 2)
2012 #define MINOR (GTK_MINOR_VERSION + 1)
2013 #else
2014 #define MINOR GTK_MINOR_VERSION
2015 #endif
2016 
2017 /*
2018  * Look for
2019  * $dir/$subdir/gtk-3.16/gtk-$variant.css
2020  * $dir/$subdir/gtk-3.14/gtk-$variant.css
2021  *  ...
2022  * $dir/$subdir/gtk-3.0/gtk-$variant.css
2023  * and return the first found file.
2024  * We don't check versions before 3.14,
2025  * since those GTK+ versions didn't have
2026  * the versioned loading mechanism.
2027  */
2028 static gchar *
_gtk_css_find_theme_dir(const gchar * dir,const gchar * subdir,const gchar * name,const gchar * variant)2029 _gtk_css_find_theme_dir (const gchar *dir,
2030                          const gchar *subdir,
2031                          const gchar *name,
2032                          const gchar *variant)
2033 {
2034   gchar *file;
2035   gchar *base;
2036   gchar *subsubdir;
2037   gint i;
2038   gchar *path;
2039 
2040   if (variant)
2041     file = g_strconcat ("gtk-", variant, ".css", NULL);
2042   else
2043     file = g_strdup ("gtk.css");
2044 
2045   if (subdir)
2046     base = g_build_filename (dir, subdir, name, NULL);
2047   else
2048     base = g_build_filename (dir, name, NULL);
2049 
2050   for (i = MINOR; i >= 0; i = i - 2)
2051     {
2052       if (i < 14)
2053         i = 0;
2054 
2055       subsubdir = g_strdup_printf ("gtk-3.%d", i);
2056       path = g_build_filename (base, subsubdir, file, NULL);
2057       g_free (subsubdir);
2058 
2059       if (g_file_test (path, G_FILE_TEST_EXISTS))
2060         break;
2061 
2062       g_free (path);
2063       path = NULL;
2064     }
2065 
2066   g_free (file);
2067   g_free (base);
2068 
2069   return path;
2070 }
2071 
2072 #undef MINOR
2073 
2074 static gchar *
_gtk_css_find_theme(const gchar * name,const gchar * variant)2075 _gtk_css_find_theme (const gchar *name,
2076                      const gchar *variant)
2077 {
2078   gchar *path;
2079   const char *const *dirs;
2080   int i;
2081   char *dir;
2082 
2083   /* First look in the user's data directory */
2084   path = _gtk_css_find_theme_dir (g_get_user_data_dir (), "themes", name, variant);
2085   if (path)
2086     return path;
2087 
2088   /* Next look in the user's home directory */
2089   path = _gtk_css_find_theme_dir (g_get_home_dir (), ".themes", name, variant);
2090   if (path)
2091     return path;
2092 
2093   /* Look in system data directories */
2094   dirs = g_get_system_data_dirs ();
2095   for (i = 0; dirs[i]; i++)
2096     {
2097       path = _gtk_css_find_theme_dir (dirs[i], "themes", name, variant);
2098       if (path)
2099         return path;
2100     }
2101 
2102   /* Finally, try in the default theme directory */
2103   dir = _gtk_get_theme_dir ();
2104   path = _gtk_css_find_theme_dir (dir, NULL, name, variant);
2105   g_free (dir);
2106 
2107   return path;
2108 }
2109 
2110 /**
2111  * _gtk_css_provider_load_named:
2112  * @provider: a #GtkCssProvider
2113  * @name: A theme name
2114  * @variant: (allow-none): variant to load, for example, "dark", or
2115  *     %NULL for the default
2116  *
2117  * Loads a theme from the usual theme paths. The actual process of
2118  * finding the theme might change between releases, but it is
2119  * guaranteed that this function uses the same mechanism to load the
2120  * theme than GTK uses for loading its own theme.
2121  **/
2122 void
_gtk_css_provider_load_named(GtkCssProvider * provider,const gchar * name,const gchar * variant)2123 _gtk_css_provider_load_named (GtkCssProvider *provider,
2124                               const gchar    *name,
2125                               const gchar    *variant)
2126 {
2127   gchar *path;
2128   gchar *resource_path;
2129 
2130   g_return_if_fail (GTK_IS_CSS_PROVIDER (provider));
2131   g_return_if_fail (name != NULL);
2132 
2133   gtk_css_provider_reset (provider);
2134 
2135   /* try loading the resource for the theme. This is mostly meant for built-in
2136    * themes.
2137    */
2138   if (variant)
2139     resource_path = g_strdup_printf ("/org/gtk/libgtk/theme/%s/gtk-%s.css", name, variant);
2140   else
2141     resource_path = g_strdup_printf ("/org/gtk/libgtk/theme/%s/gtk.css", name);
2142 
2143   if (g_resources_get_info (resource_path, 0, NULL, NULL, NULL))
2144     {
2145       gtk_css_provider_load_from_resource (provider, resource_path);
2146       g_free (resource_path);
2147       return;
2148     }
2149   g_free (resource_path);
2150 
2151   /* Next try looking for files in the various theme directories. */
2152   path = _gtk_css_find_theme (name, variant);
2153   if (path)
2154     {
2155       char *dir, *resource_file;
2156       GResource *resource;
2157 
2158       dir = g_path_get_dirname (path);
2159       resource_file = g_build_filename (dir, "gtk.gresource", NULL);
2160       resource = g_resource_load (resource_file, NULL);
2161       g_free (resource_file);
2162 
2163       if (resource != NULL)
2164         g_resources_register (resource);
2165 
2166       gtk_css_provider_load_from_path (provider, path, NULL);
2167 
2168       /* Only set this after load, as load_from_path will clear it */
2169       provider->priv->resource = resource;
2170       provider->priv->path = dir;
2171 
2172       g_free (path);
2173     }
2174   else
2175     {
2176       /* Things failed! Fall back! Fall back! */
2177 
2178       if (variant)
2179         {
2180           /* If there was a variant, try without */
2181           _gtk_css_provider_load_named (provider, name, NULL);
2182         }
2183       else
2184         {
2185           /* Worst case, fall back to the default */
2186           g_return_if_fail (!g_str_equal (name, DEFAULT_THEME_NAME)); /* infloop protection */
2187           _gtk_css_provider_load_named (provider, DEFAULT_THEME_NAME, NULL);
2188         }
2189     }
2190 }
2191 
2192 /**
2193  * gtk_css_provider_get_named:
2194  * @name: A theme name
2195  * @variant: (allow-none): variant to load, for example, "dark", or
2196  *     %NULL for the default
2197  *
2198  * Loads a theme from the usual theme paths
2199  *
2200  * Returns: (transfer none): a #GtkCssProvider with the theme loaded.
2201  *     This memory is owned by GTK+, and you must not free it.
2202  */
2203 GtkCssProvider *
gtk_css_provider_get_named(const gchar * name,const gchar * variant)2204 gtk_css_provider_get_named (const gchar *name,
2205                             const gchar *variant)
2206 {
2207   static GHashTable *themes = NULL;
2208   GtkCssProvider *provider;
2209   gchar *key;
2210 
2211   if (variant == NULL)
2212     key = g_strdup (name);
2213   else
2214     key = g_strconcat (name, "-", variant, NULL);
2215   if (G_UNLIKELY (!themes))
2216     themes = g_hash_table_new (g_str_hash, g_str_equal);
2217 
2218   provider = g_hash_table_lookup (themes, key);
2219 
2220   if (!provider)
2221     {
2222       provider = gtk_css_provider_new ();
2223       _gtk_css_provider_load_named (provider, name, variant);
2224       g_hash_table_insert (themes, g_strdup (key), provider);
2225     }
2226 
2227   g_free (key);
2228 
2229   return provider;
2230 }
2231 
2232 static int
compare_properties(gconstpointer a,gconstpointer b,gpointer style)2233 compare_properties (gconstpointer a, gconstpointer b, gpointer style)
2234 {
2235   const guint *ua = a;
2236   const guint *ub = b;
2237   PropertyValue *styles = style;
2238 
2239   return strcmp (_gtk_style_property_get_name (GTK_STYLE_PROPERTY (styles[*ua].property)),
2240                  _gtk_style_property_get_name (GTK_STYLE_PROPERTY (styles[*ub].property)));
2241 }
2242 
2243 static int
compare_names(gconstpointer a,gconstpointer b)2244 compare_names (gconstpointer a, gconstpointer b)
2245 {
2246   const WidgetPropertyValue *aa = a;
2247   const WidgetPropertyValue *bb = b;
2248   return strcmp (aa->name, bb->name);
2249 }
2250 
2251 static void
gtk_css_ruleset_print(const GtkCssRuleset * ruleset,GString * str)2252 gtk_css_ruleset_print (const GtkCssRuleset *ruleset,
2253                        GString             *str)
2254 {
2255   GList *values, *walk;
2256   WidgetPropertyValue *widget_value;
2257   guint i;
2258 
2259   _gtk_css_selector_tree_match_print (ruleset->selector_match, str);
2260 
2261   g_string_append (str, " {\n");
2262 
2263   if (ruleset->styles)
2264     {
2265       guint *sorted = g_new (guint, ruleset->n_styles);
2266 
2267       for (i = 0; i < ruleset->n_styles; i++)
2268         sorted[i] = i;
2269 
2270       /* so the output is identical for identical selector styles */
2271       g_qsort_with_data (sorted, ruleset->n_styles, sizeof (guint), compare_properties, ruleset->styles);
2272 
2273       for (i = 0; i < ruleset->n_styles; i++)
2274         {
2275           PropertyValue *prop = &ruleset->styles[sorted[i]];
2276           g_string_append (str, "  ");
2277           g_string_append (str, _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop->property)));
2278           g_string_append (str, ": ");
2279           _gtk_css_value_print (prop->value, str);
2280           g_string_append (str, ";\n");
2281         }
2282 
2283       g_free (sorted);
2284     }
2285 
2286   if (ruleset->widget_style)
2287     {
2288       values = NULL;
2289       for (widget_value = ruleset->widget_style; widget_value != NULL; widget_value = widget_value->next)
2290 	values = g_list_prepend (values, widget_value);
2291 
2292       /* so the output is identical for identical selector styles */
2293       values = g_list_sort (values, compare_names);
2294 
2295       for (walk = values; walk; walk = walk->next)
2296         {
2297 	  widget_value = walk->data;
2298 
2299           g_string_append (str, "  ");
2300           g_string_append (str, widget_value->name);
2301           g_string_append (str, ": ");
2302           g_string_append (str, widget_value->value);
2303           g_string_append (str, ";\n");
2304         }
2305 
2306       g_list_free (values);
2307     }
2308 
2309   g_string_append (str, "}\n");
2310 }
2311 
2312 static void
gtk_css_provider_print_colors(GHashTable * colors,GString * str)2313 gtk_css_provider_print_colors (GHashTable *colors,
2314                                GString    *str)
2315 {
2316   GList *keys, *walk;
2317 
2318   keys = g_hash_table_get_keys (colors);
2319   /* so the output is identical for identical styles */
2320   keys = g_list_sort (keys, (GCompareFunc) strcmp);
2321 
2322   for (walk = keys; walk; walk = walk->next)
2323     {
2324       const char *name = walk->data;
2325       GtkCssValue *color = g_hash_table_lookup (colors, (gpointer) name);
2326 
2327       g_string_append (str, "@define-color ");
2328       g_string_append (str, name);
2329       g_string_append (str, " ");
2330       _gtk_css_value_print (color, str);
2331       g_string_append (str, ";\n");
2332     }
2333 
2334   g_list_free (keys);
2335 }
2336 
2337 static void
gtk_css_provider_print_keyframes(GHashTable * keyframes,GString * str)2338 gtk_css_provider_print_keyframes (GHashTable *keyframes,
2339                                   GString    *str)
2340 {
2341   GList *keys, *walk;
2342 
2343   keys = g_hash_table_get_keys (keyframes);
2344   /* so the output is identical for identical styles */
2345   keys = g_list_sort (keys, (GCompareFunc) strcmp);
2346 
2347   for (walk = keys; walk; walk = walk->next)
2348     {
2349       const char *name = walk->data;
2350       GtkCssKeyframes *keyframe = g_hash_table_lookup (keyframes, (gpointer) name);
2351 
2352       if (str->len > 0)
2353         g_string_append (str, "\n");
2354       g_string_append (str, "@keyframes ");
2355       g_string_append (str, name);
2356       g_string_append (str, " {\n");
2357       _gtk_css_keyframes_print (keyframe, str);
2358       g_string_append (str, "}\n");
2359     }
2360 
2361   g_list_free (keys);
2362 }
2363 
2364 /**
2365  * gtk_css_provider_to_string:
2366  * @provider: the provider to write to a string
2367  *
2368  * Converts the @provider into a string representation in CSS
2369  * format.
2370  *
2371  * Using gtk_css_provider_load_from_data() with the return value
2372  * from this function on a new provider created with
2373  * gtk_css_provider_new() will basically create a duplicate of
2374  * this @provider.
2375  *
2376  * Returns: a new string representing the @provider.
2377  *
2378  * Since: 3.2
2379  **/
2380 char *
gtk_css_provider_to_string(GtkCssProvider * provider)2381 gtk_css_provider_to_string (GtkCssProvider *provider)
2382 {
2383   GtkCssProviderPrivate *priv;
2384   GString *str;
2385   guint i;
2386 
2387   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (provider), NULL);
2388 
2389   priv = provider->priv;
2390 
2391   str = g_string_new ("");
2392 
2393   gtk_css_provider_print_colors (priv->symbolic_colors, str);
2394   gtk_css_provider_print_keyframes (priv->keyframes, str);
2395 
2396   for (i = 0; i < priv->rulesets->len; i++)
2397     {
2398       if (str->len != 0)
2399         g_string_append (str, "\n");
2400       gtk_css_ruleset_print (&g_array_index (priv->rulesets, GtkCssRuleset, i), str);
2401     }
2402 
2403   return g_string_free (str, FALSE);
2404 }
2405 
2406