1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3
* st-theme.c: A set of CSS stylesheets used for rule matching
5
* Copyright 2003-2004 Dodji Seketeli
6
* Copyright 2008, 2009 Red Hat, Inc.
8
* This program is free software; you can redistribute it and/or modify it
9
* under the terms and conditions of the GNU Lesser General Public License,
10
* version 2.1, as published by the Free Software Foundation.
12
* This program is distributed in the hope it will be useful, but WITHOUT ANY
13
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
17
* You should have received a copy of the GNU Lesser General Public License
18
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20
* This file started as a cut-and-paste of cr-sel-eng.c from libcroco.
22
* In moving it to hippo-canvas:
23
* - Reformatted and otherwise edited to match our coding style
24
* - Switched from handling xmlNode to handling HippoStyle
25
* - Simplified by removing things that we don't need or that don't
26
* make sense in our context.
27
* - The code to get a list of matching properties works quite differently;
28
* we order things in priority order, but we don't actually try to
29
* coalesce properties with the same name.
31
* In moving it to GNOME Shell:
32
* - Renamed again to StTheme
33
* - Reformatted to match the gnome-shell coding style
34
* - Removed notion of "theme engine" from hippo-canvas
35
* - pseudo-class matching changed from link enum to strings
36
* - Some code simplification
45
#include "st-theme-node.h"
46
#include "st-theme-private.h"
48
static GObject *st_theme_constructor (GType type,
49
guint n_construct_properties,
50
GObjectConstructParam *construct_properties);
52
static void st_theme_finalize (GObject *object);
53
static void st_theme_set_property (GObject *object,
57
static void st_theme_get_property (GObject *object,
66
char *application_stylesheet;
67
char *default_stylesheet;
68
char *theme_stylesheet;
69
GSList *custom_stylesheets;
71
GHashTable *stylesheets_by_filename;
72
GHashTable *filenames_by_stylesheet;
79
GObjectClass parent_class;
85
PROP_APPLICATION_STYLESHEET,
86
PROP_THEME_STYLESHEET,
87
PROP_DEFAULT_STYLESHEET
90
G_DEFINE_TYPE (StTheme, st_theme, G_TYPE_OBJECT)
92
/* Quick strcmp. Test only for == 0 or != 0, not < 0 or > 0. */
93
#define strqcmp(str,lit,lit_len) \
94
(strlen (str) != (lit_len) || memcmp (str, lit, lit_len))
97
st_theme_init (StTheme *theme)
99
theme->stylesheets_by_filename = g_hash_table_new_full (g_str_hash, g_str_equal,
100
(GDestroyNotify)g_free, (GDestroyNotify)cr_stylesheet_unref);
101
theme->filenames_by_stylesheet = g_hash_table_new (g_direct_hash, g_direct_equal);
105
st_theme_class_init (StThemeClass *klass)
107
GObjectClass *object_class = G_OBJECT_CLASS (klass);
109
object_class->constructor = st_theme_constructor;
110
object_class->finalize = st_theme_finalize;
111
object_class->set_property = st_theme_set_property;
112
object_class->get_property = st_theme_get_property;
115
* StTheme:application-stylesheet:
117
* The highest priority stylesheet, representing application-specific
118
* styling; this is associated with the CSS "author" stylesheet.
120
g_object_class_install_property (object_class,
121
PROP_APPLICATION_STYLESHEET,
122
g_param_spec_string ("application-stylesheet",
123
"Application Stylesheet",
124
"Stylesheet with application-specific styling",
126
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
129
* StTheme:theme-stylesheet:
131
* The second priority stylesheet, representing theme-specific styling;
132
* this is associated with the CSS "user" stylesheet.
134
g_object_class_install_property (object_class,
135
PROP_THEME_STYLESHEET,
136
g_param_spec_string ("theme-stylesheet",
138
"Stylesheet with theme-specific styling",
140
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
143
* StTheme:default-stylesheet:
145
* The lowest priority stylesheet, representing global default
146
* styling; this is associated with the CSS "user agent" stylesheet.
148
g_object_class_install_property (object_class,
149
PROP_DEFAULT_STYLESHEET,
150
g_param_spec_string ("default-stylesheet",
151
"Default Stylesheet",
152
"Stylesheet with global default styling",
154
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
158
static CRStyleSheet *
159
parse_stylesheet (const char *filename,
162
enum CRStatus status;
163
CRStyleSheet *stylesheet;
165
if (filename == NULL)
168
status = cr_om_parser_simply_parse_file ((const guchar *) filename,
174
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
175
"Error parsing stylesheet '%s'; errcode:%d", filename, status);
179
/* Extension stylesheet */
180
stylesheet->app_data = GUINT_TO_POINTER (FALSE);
186
_st_theme_parse_declaration_list (const char *str)
188
return cr_declaration_parse_list_from_buf ((const guchar *)str,
192
/* Just g_warning for now until we have something nicer to do */
193
static CRStyleSheet *
194
parse_stylesheet_nofail (const char *filename)
196
GError *error = NULL;
197
CRStyleSheet *result;
199
result = parse_stylesheet (filename, &error);
202
g_warning ("%s", error->message);
203
g_clear_error (&error);
209
insert_stylesheet (StTheme *theme,
210
const char *filename,
211
CRStyleSheet *stylesheet)
215
if (stylesheet == NULL)
218
filename_copy = g_strdup(filename);
219
cr_stylesheet_ref (stylesheet);
221
g_hash_table_insert (theme->stylesheets_by_filename, filename_copy, stylesheet);
222
g_hash_table_insert (theme->filenames_by_stylesheet, stylesheet, filename_copy);
226
st_theme_load_stylesheet (StTheme *theme,
230
CRStyleSheet *stylesheet;
232
stylesheet = parse_stylesheet (path, error);
236
stylesheet->app_data = GUINT_TO_POINTER (TRUE);
238
insert_stylesheet (theme, path, stylesheet);
239
cr_stylesheet_ref (stylesheet);
240
theme->custom_stylesheets = g_slist_prepend (theme->custom_stylesheets, stylesheet);
246
st_theme_unload_stylesheet (StTheme *theme,
249
CRStyleSheet *stylesheet;
251
stylesheet = g_hash_table_lookup (theme->stylesheets_by_filename, path);
255
if (!g_slist_find (theme->custom_stylesheets, stylesheet))
258
theme->custom_stylesheets = g_slist_remove (theme->custom_stylesheets, stylesheet);
259
g_hash_table_remove (theme->stylesheets_by_filename, path);
260
g_hash_table_remove (theme->filenames_by_stylesheet, stylesheet);
261
cr_stylesheet_unref (stylesheet);
265
* st_theme_get_custom_stylesheets:
266
* @theme: an #StTheme
268
* Returns: (transfer full) (element-type utf8): the list of stylesheet filenames
269
* that were loaded with st_theme_load_stylesheet()
272
st_theme_get_custom_stylesheets (StTheme *theme)
274
GSList *result = NULL;
277
for (iter = theme->custom_stylesheets; iter; iter = iter->next)
279
CRStyleSheet *stylesheet = iter->data;
280
gchar *filename = g_hash_table_lookup (theme->filenames_by_stylesheet, stylesheet);
282
result = g_slist_prepend (result, g_strdup (filename));
289
st_theme_constructor (GType type,
290
guint n_construct_properties,
291
GObjectConstructParam *construct_properties)
295
CRStyleSheet *application_stylesheet;
296
CRStyleSheet *theme_stylesheet;
297
CRStyleSheet *default_stylesheet;
299
object = (*G_OBJECT_CLASS (st_theme_parent_class)->constructor) (type,
300
n_construct_properties,
301
construct_properties);
302
theme = ST_THEME (object);
304
application_stylesheet = parse_stylesheet_nofail (theme->application_stylesheet);
305
theme_stylesheet = parse_stylesheet_nofail (theme->theme_stylesheet);
306
default_stylesheet = parse_stylesheet_nofail (theme->default_stylesheet);
308
theme->cascade = cr_cascade_new (application_stylesheet,
312
if (theme->cascade == NULL)
313
g_error ("Out of memory when creating cascade object");
315
insert_stylesheet (theme, theme->application_stylesheet, application_stylesheet);
316
insert_stylesheet (theme, theme->theme_stylesheet, theme_stylesheet);
317
insert_stylesheet (theme, theme->default_stylesheet, default_stylesheet);
323
st_theme_finalize (GObject * object)
325
StTheme *theme = ST_THEME (object);
327
g_slist_foreach (theme->custom_stylesheets, (GFunc) cr_stylesheet_unref, NULL);
328
g_slist_free (theme->custom_stylesheets);
329
theme->custom_stylesheets = NULL;
331
g_hash_table_destroy (theme->stylesheets_by_filename);
332
g_hash_table_destroy (theme->filenames_by_stylesheet);
334
g_free (theme->application_stylesheet);
335
g_free (theme->theme_stylesheet);
336
g_free (theme->default_stylesheet);
340
cr_cascade_unref (theme->cascade);
341
theme->cascade = NULL;
344
G_OBJECT_CLASS (st_theme_parent_class)->finalize (object);
348
st_theme_set_property (GObject *object,
353
StTheme *theme = ST_THEME (object);
357
case PROP_APPLICATION_STYLESHEET:
359
const char *path = g_value_get_string (value);
361
if (path != theme->application_stylesheet)
363
g_free (theme->application_stylesheet);
364
theme->application_stylesheet = g_strdup (path);
369
case PROP_THEME_STYLESHEET:
371
const char *path = g_value_get_string (value);
373
if (path != theme->theme_stylesheet)
375
g_free (theme->theme_stylesheet);
376
theme->theme_stylesheet = g_strdup (path);
381
case PROP_DEFAULT_STYLESHEET:
383
const char *path = g_value_get_string (value);
385
if (path != theme->default_stylesheet)
387
g_free (theme->default_stylesheet);
388
theme->default_stylesheet = g_strdup (path);
394
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
400
st_theme_get_property (GObject *object,
405
StTheme *theme = ST_THEME (object);
409
case PROP_APPLICATION_STYLESHEET:
410
g_value_set_string (value, theme->application_stylesheet);
412
case PROP_THEME_STYLESHEET:
413
g_value_set_string (value, theme->theme_stylesheet);
415
case PROP_DEFAULT_STYLESHEET:
416
g_value_set_string (value, theme->default_stylesheet);
419
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
426
* @application_stylesheet: The highest priority stylesheet, representing application-specific
427
* styling; this is associated with the CSS "author" stylesheet, may be %NULL
428
* @theme_stylesheet: The second priority stylesheet, representing theme-specific styling ;
429
* this is associated with the CSS "user" stylesheet, may be %NULL
430
* @default_stylesheet: The lowest priority stylesheet, representing global default styling;
431
* this is associated with the CSS "user agent" stylesheet, may be %NULL
433
* Return value: the newly created theme object
436
st_theme_new (const char *application_stylesheet,
437
const char *theme_stylesheet,
438
const char *default_stylesheet)
440
StTheme *theme = g_object_new (ST_TYPE_THEME,
441
"application-stylesheet", application_stylesheet,
442
"theme-stylesheet", theme_stylesheet,
443
"default-stylesheet", default_stylesheet,
450
string_in_list (GString *stryng,
455
for (cur = list; *cur;)
457
while (*cur && cr_utils_is_white_space (*cur))
460
if (strncmp (cur, stryng->str, stryng->len) == 0)
463
if ((!*cur) || cr_utils_is_white_space (*cur))
467
/* skip to next whitespace character */
468
while (*cur && !cr_utils_is_white_space (*cur))
476
pseudo_class_add_sel_matches_style (StTheme *a_this,
477
CRAdditionalSel *a_add_sel,
480
const char *node_pseudo_class;
482
g_return_val_if_fail (a_this
484
&& a_add_sel->content.pseudo
485
&& a_add_sel->content.pseudo->name
486
&& a_add_sel->content.pseudo->name->stryng
487
&& a_add_sel->content.pseudo->name->stryng->str
490
node_pseudo_class = st_theme_node_get_pseudo_class (a_node);
492
if (node_pseudo_class == NULL)
495
return string_in_list (a_add_sel->content.pseudo->name->stryng, node_pseudo_class);
499
* class_add_sel_matches_style:
500
* @a_add_sel: The class additional selector to consider.
501
* @a_node: The style node to consider.
503
* Returns: %TRUE if the class additional selector matches
504
* the style node given in argument, %FALSE otherwise.
507
class_add_sel_matches_style (CRAdditionalSel *a_add_sel,
510
const char *element_class;
512
g_return_val_if_fail (a_add_sel
513
&& a_add_sel->type == CLASS_ADD_SELECTOR
514
&& a_add_sel->content.class_name
515
&& a_add_sel->content.class_name->stryng
516
&& a_add_sel->content.class_name->stryng->str
519
element_class = st_theme_node_get_element_class (a_node);
520
if (element_class == NULL)
523
return string_in_list (a_add_sel->content.class_name->stryng, element_class);
527
*@return TRUE if the additional attribute selector matches
528
*the current style node given in argument, FALSE otherwise.
529
*@param a_add_sel the additional attribute selector to consider.
530
*@param a_node the style node to consider.
533
id_add_sel_matches_style (CRAdditionalSel *a_add_sel,
536
gboolean result = FALSE;
539
g_return_val_if_fail (a_add_sel
540
&& a_add_sel->type == ID_ADD_SELECTOR
541
&& a_add_sel->content.id_name
542
&& a_add_sel->content.id_name->stryng
543
&& a_add_sel->content.id_name->stryng->str
545
g_return_val_if_fail (a_add_sel
546
&& a_add_sel->type == ID_ADD_SELECTOR
549
id = st_theme_node_get_element_id (a_node);
553
if (!strqcmp (id, a_add_sel->content.id_name->stryng->str,
554
a_add_sel->content.id_name->stryng->len))
564
*additional_selector_matches_style:
565
*Evaluates if a given additional selector matches an style node.
566
*@param a_add_sel the additional selector to consider.
567
*@param a_node the style node to consider.
568
*@return TRUE is a_add_sel matches a_node, FALSE otherwise.
571
additional_selector_matches_style (StTheme *a_this,
572
CRAdditionalSel *a_add_sel,
575
CRAdditionalSel *cur_add_sel = NULL;
577
g_return_val_if_fail (a_add_sel, FALSE);
579
for (cur_add_sel = a_add_sel; cur_add_sel; cur_add_sel = cur_add_sel->next)
581
switch (cur_add_sel->type)
583
case NO_ADD_SELECTOR:
585
case CLASS_ADD_SELECTOR:
586
if (!class_add_sel_matches_style (cur_add_sel, a_node))
589
case ID_ADD_SELECTOR:
590
if (!id_add_sel_matches_style (cur_add_sel, a_node))
593
case ATTRIBUTE_ADD_SELECTOR:
594
g_warning ("Attribute selectors not supported");
596
case PSEUDO_CLASS_ADD_SELECTOR:
597
if (!pseudo_class_add_sel_matches_style (a_this, cur_add_sel, a_node))
607
element_name_matches_type (const char *element_name,
610
if (element_type == G_TYPE_NONE)
612
return strcmp (element_name, "stage") == 0;
616
GType match_type = g_type_from_name (element_name);
617
if (match_type == G_TYPE_INVALID)
620
return g_type_is_a (element_type, match_type);
625
*Evaluate a selector (a simple selectors list) and says
626
*if it matches the style node given in parameter.
627
*The algorithm used here is the following:
628
*Walk the combinator separated list of simple selectors backward, starting
629
*from the end of the list. For each simple selector, looks if
630
*if matches the current style.
632
*@param a_this the selection engine.
633
*@param a_sel the simple selection list.
634
*@param a_node the style node.
635
*@param a_result out parameter. Set to true if the
636
*selector matches the style node, FALSE otherwise.
637
*@param a_recurse if set to TRUE, the function will walk to
638
*the next simple selector (after the evaluation of the current one)
639
*and recursively evaluate it. Must be usually set to TRUE unless you
640
*know what you are doing.
643
sel_matches_style_real (StTheme *a_this,
647
gboolean a_eval_sel_list_from_end,
650
CRSimpleSel *cur_sel = NULL;
651
StThemeNode *cur_node = NULL;
656
if (a_eval_sel_list_from_end)
658
/*go and get the last simple selector of the list */
659
for (cur_sel = a_sel; cur_sel && cur_sel->next; cur_sel = cur_sel->next)
668
cur_type = st_theme_node_get_element_type (cur_node);
672
if (((cur_sel->type_mask & TYPE_SELECTOR)
674
&& cur_sel->name->stryng
675
&& cur_sel->name->stryng->str)
677
(element_name_matches_type (cur_sel->name->stryng->str, cur_type)))
678
|| (cur_sel->type_mask & UNIVERSAL_SELECTOR))
681
*this simple selector
682
*matches the current style node
683
*Let's see if the preceding
684
*simple selectors also match
685
*their style node counterpart.
687
if (cur_sel->add_sel)
689
if (additional_selector_matches_style (a_this, cur_sel->add_sel, cur_node))
690
goto walk_a_step_in_expr;
695
goto walk_a_step_in_expr;
697
if (!(cur_sel->type_mask & TYPE_SELECTOR)
698
&& !(cur_sel->type_mask & UNIVERSAL_SELECTOR))
700
if (!cur_sel->add_sel)
702
if (additional_selector_matches_style (a_this, cur_sel->add_sel, cur_node))
703
goto walk_a_step_in_expr;
713
if (a_recurse == FALSE)
720
*here, depending on the combinator of cur_sel
721
*choose the axis of the element tree traversal
722
*and walk one step in the element tree.
727
switch (cur_sel->combinator)
732
case COMB_WS: /*descendant selector */
734
StThemeNode *n = NULL;
737
*walk the element tree upward looking for a parent
738
*style that matches the preceding selector.
740
for (n = st_theme_node_get_parent (a_node); n; n = st_theme_node_get_parent (n))
742
enum CRStatus status;
743
gboolean matches = FALSE;
745
status = sel_matches_style_real (a_this, cur_sel->prev, n, &matches, FALSE, TRUE);
753
cur_type = st_theme_node_get_element_type (cur_node);
761
*didn't find any ancestor that matches
762
*the previous simple selector.
767
*in this case, the preceding simple sel
768
*will have been interpreted twice, which
769
*is a cpu and mem waste ... I need to find
770
*another way to do this. Anyway, this is
771
*my first attempt to write this function and
772
*I am a bit clueless.
778
g_warning ("+ combinators are not supported");
782
cur_node = st_theme_node_get_parent (cur_node);
785
cur_type = st_theme_node_get_element_type (cur_node);
792
cur_sel = cur_sel->prev;
796
*if we reached this point, it means the selector matches
806
add_matched_properties (StTheme *a_this,
807
CRStyleSheet *a_nodesheet,
811
CRStatement *cur_stmt = NULL;
812
CRSelector *sel_list = NULL;
813
CRSelector *cur_sel = NULL;
814
gboolean matches = FALSE;
815
enum CRStatus status = CR_OK;
818
*walk through the list of statements and,
819
*get the selectors list inside the statements that
820
*contain some, and try to match our style node in these
823
for (cur_stmt = a_nodesheet->statements; cur_stmt; cur_stmt = cur_stmt->next)
826
*initialyze the selector list in which we will
827
*really perform the search.
832
*get the the damn selector list in
833
*which we have to look
835
switch (cur_stmt->type)
838
if (cur_stmt->kind.ruleset && cur_stmt->kind.ruleset->sel_list)
840
sel_list = cur_stmt->kind.ruleset->sel_list;
844
case AT_MEDIA_RULE_STMT:
845
if (cur_stmt->kind.media_rule
846
&& cur_stmt->kind.media_rule->rulesets
847
&& cur_stmt->kind.media_rule->rulesets->kind.ruleset
848
&& cur_stmt->kind.media_rule->rulesets->kind.ruleset->sel_list)
850
sel_list = cur_stmt->kind.media_rule->rulesets->kind.ruleset->sel_list;
854
case AT_IMPORT_RULE_STMT:
856
CRAtImportRule *import_rule = cur_stmt->kind.import_rule;
858
if (import_rule->sheet == NULL)
860
char *filename = NULL;
862
if (import_rule->url->stryng && import_rule->url->stryng->str)
863
filename = _st_theme_resolve_url (a_this,
865
import_rule->url->stryng->str);
868
import_rule->sheet = parse_stylesheet (filename, NULL);
870
if (import_rule->sheet)
872
insert_stylesheet (a_this, filename, import_rule->sheet);
873
/* refcount of stylesheets starts off at zero, so we don't need to unref! */
877
/* Set a marker to avoid repeatedly trying to parse a non-existent or
880
import_rule->sheet = (CRStyleSheet *) - 1;
887
if (import_rule->sheet != (CRStyleSheet *) - 1)
889
add_matched_properties (a_this, import_rule->sheet,
902
*now, we have a comma separated selector list to look in.
903
*let's walk it and try to match the style node
904
*on each item of the list.
906
for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next)
908
if (!cur_sel->simple_sel)
911
status = sel_matches_style_real (a_this, cur_sel->simple_sel, a_node, &matches, TRUE, TRUE);
913
if (status == CR_OK && matches)
915
CRDeclaration *cur_decl = NULL;
917
/* In order to sort the matching properties, we need to compute the
918
* specificity of the selector that actually matched this
919
* element. In a non-thread-safe fashion, we store it in the
920
* ruleset. (Fixing this would mean cut-and-pasting
921
* cr_simple_sel_compute_specificity(), and have no need for
922
* thread-safety anyways.)
924
* Once we've sorted the properties, the specificity no longer
925
* matters and it can be safely overriden.
927
cr_simple_sel_compute_specificity (cur_sel->simple_sel);
929
cur_stmt->specificity = cur_sel->simple_sel->specificity;
931
for (cur_decl = cur_stmt->kind.ruleset->decl_list; cur_decl; cur_decl = cur_decl->next)
932
g_ptr_array_add (props, cur_decl);
938
#define ORIGIN_OFFSET_IMPORTANT (NB_ORIGINS)
939
#define ORIGIN_OFFSET_EXTENSION (NB_ORIGINS * 2)
942
get_origin (const CRDeclaration * decl)
944
enum CRStyleOrigin origin = decl->parent_statement->parent_sheet->origin;
945
gboolean is_extension_sheet = GPOINTER_TO_UINT (decl->parent_statement->parent_sheet->app_data);
948
origin += ORIGIN_OFFSET_IMPORTANT;
950
if (is_extension_sheet)
951
origin += ORIGIN_OFFSET_EXTENSION;
956
/* Order of comparison is so that higher priority statements compare after
957
* lower priority statements */
959
compare_declarations (gconstpointer a,
962
/* g_ptr_array_sort() is broooken */
963
CRDeclaration *decl_a = *(CRDeclaration **) a;
964
CRDeclaration *decl_b = *(CRDeclaration **) b;
966
int origin_a = get_origin (decl_a);
967
int origin_b = get_origin (decl_b);
969
if (origin_a != origin_b)
970
return origin_a - origin_b;
972
if (decl_a->parent_statement->specificity != decl_b->parent_statement->specificity)
973
return decl_a->parent_statement->specificity - decl_b->parent_statement->specificity;
979
_st_theme_get_matched_properties (StTheme *theme,
982
enum CRStyleOrigin origin = 0;
983
CRStyleSheet *sheet = NULL;
984
GPtrArray *props = g_ptr_array_new ();
987
g_return_val_if_fail (ST_IS_THEME (theme), NULL);
988
g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
990
for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++)
992
sheet = cr_cascade_get_sheet (theme->cascade, origin);
996
add_matched_properties (theme, sheet, node, props);
999
for (iter = theme->custom_stylesheets; iter; iter = iter->next)
1000
add_matched_properties (theme, iter->data, node, props);
1002
/* We count on a stable sort here so that later declarations come
1003
* after earlier declarations */
1004
g_ptr_array_sort (props, compare_declarations);
1009
/* Resolve an url from an url() reference in a stylesheet into an absolute
1010
* local filename, if possible. The resolution here is distinctly lame and
1011
* will fail on many examples.
1014
_st_theme_resolve_url (StTheme *theme,
1015
CRStyleSheet *base_stylesheet,
1018
const char *base_filename = NULL;
1022
/* Handle absolute file:/ URLs */
1023
if (g_str_has_prefix (url, "file:") ||
1024
g_str_has_prefix (url, "File:") ||
1025
g_str_has_prefix (url, "FILE:"))
1027
GError *error = NULL;
1030
filename = g_filename_from_uri (url, NULL, &error);
1031
if (filename == NULL)
1033
g_warning ("%s", error->message);
1034
g_error_free (error);
1040
/* Guard against http:/ URLs */
1042
if (g_str_has_prefix (url, "http:") ||
1043
g_str_has_prefix (url, "Http:") ||
1044
g_str_has_prefix (url, "HTTP:"))
1046
g_warning ("Http URL '%s' in theme stylesheet is not supported", url);
1050
/* Assume anything else is a relative URL, and "resolve" it
1053
return g_strdup (url);
1055
base_filename = g_hash_table_lookup (theme->filenames_by_stylesheet, base_stylesheet);
1057
if (base_filename == NULL)
1059
g_warning ("Can't get base to resolve url '%s'", url);
1063
dirname = g_path_get_dirname (base_filename);
1064
filename = g_build_filename (dirname, url, NULL);