1
/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
4
* This file is part of The Croco Library
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of version 2.1 of the GNU Lesser General Public
8
* License as published by the Free Software Foundation.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU Lesser
16
* General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21
* See COPYRIGHTS file for copyright informations.
25
#include "cr-sel-eng.h"
26
#include "cr-node-iface.h"
30
*The definition of the #CRSelEng class.
31
*The #CRSelEng is actually the "Selection Engine"
32
*class. This is highly experimental for at the moment and
33
*its api is very likely to change in a near future.
36
#define PRIVATE(a_this) (a_this)->priv
38
struct CRPseudoClassSelHandlerEntry {
40
enum CRPseudoType type;
41
CRPseudoClassSelectorHandler handler;
44
struct _CRSelEngPriv {
46
gboolean case_sensitive;
48
CRNodeIface const *node_iface;
51
*where to store the next statement
52
*to be visited so that we can remember
53
*it from one method call to another.
55
CRStatement *cur_stmt;
57
gint pcs_handlers_size;
60
static gboolean class_add_sel_matches_node (CRAdditionalSel * a_add_sel,
61
CRNodeIface const * a_node_iface, CRXMLNodePtr a_node);
63
static gboolean id_add_sel_matches_node (CRAdditionalSel * a_add_sel,
64
CRNodeIface const * a_node_iface, CRXMLNodePtr a_node);
66
static gboolean attr_add_sel_matches_node (CRAdditionalSel * a_add_sel,
67
CRNodeIface const * a_node_iface, CRXMLNodePtr a_node);
69
static enum CRStatus sel_matches_node_real (CRSelEng * a_this,
73
gboolean a_eval_sel_list_from_end,
76
static enum CRStatus cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
84
static enum CRStatus put_css_properties_in_props_list (CRPropList ** a_props,
88
static gboolean pseudo_class_add_sel_matches_node (CRSelEng * a_this,
93
static gboolean lang_pseudo_class_handler (CRSelEng * a_this,
94
CRAdditionalSel * a_sel,
97
static gboolean first_child_pseudo_class_handler (CRSelEng * a_this,
98
CRAdditionalSel * a_sel,
101
static CRXMLNodePtr get_next_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node);
103
static CRXMLNodePtr get_next_child_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node);
105
static CRXMLNodePtr get_prev_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node);
107
static CRXMLNodePtr get_next_parent_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node);
110
cr_sel_eng_set_node_iface (CRSelEng *const a_this, CRNodeIface const *const a_node_iface)
112
/* Allow NULL: the caller may be just ensuring that the previous node_iface
113
value doesn't get used until next cr_sel_eng_set_node_iface call. */
114
PRIVATE(a_this)->node_iface = a_node_iface;
118
lang_pseudo_class_handler (CRSelEng *const a_this,
119
CRAdditionalSel * a_sel, CRXMLNodePtr a_node)
121
CRNodeIface const *node_iface;
122
CRXMLNodePtr node = a_node;
123
gboolean result = FALSE;
125
g_return_val_if_fail (a_this && PRIVATE (a_this)
126
&& a_sel && a_sel->content.pseudo
127
&& a_sel->content.pseudo
128
&& a_sel->content.pseudo->name
129
&& a_sel->content.pseudo->name->stryng
132
node_iface = PRIVATE(a_this)->node_iface;
134
if (strncmp (a_sel->content.pseudo->name->stryng->str,
136
|| !a_sel->content.pseudo->type == FUNCTION_PSEUDO) {
137
cr_utils_trace_info ("This handler is for :lang only");
140
/*lang code should exist and be at least of length 2 */
141
if (!a_sel->content.pseudo->extra
142
|| !a_sel->content.pseudo->extra->stryng
143
|| a_sel->content.pseudo->extra->stryng->len < 2)
145
for (; node; node = get_next_parent_element_node (node_iface, node)) {
146
char *val = node_iface->getProp (node, "lang");
149
a_sel->content.pseudo->extra->stryng->str,
150
a_sel->content.pseudo->extra->stryng->len)) {
154
node_iface->freePropVal (val);
163
first_child_pseudo_class_handler (CRSelEng *const a_this,
164
CRAdditionalSel * a_sel, CRXMLNodePtr const a_node)
166
CRNodeIface const *node_iface = NULL;
167
CRXMLNodePtr node = NULL, parent = NULL;
169
g_return_val_if_fail (a_this && PRIVATE (a_this)
170
&& a_sel && a_sel->content.pseudo
171
&& a_sel->content.pseudo
172
&& a_sel->content.pseudo->name
173
&& a_sel->content.pseudo->name->stryng
176
if (strcmp (a_sel->content.pseudo->name->stryng->str,
178
|| !a_sel->content.pseudo->type == IDENT_PSEUDO) {
179
cr_utils_trace_info ("This handler is for :first-child only");
182
node_iface = PRIVATE(a_this)->node_iface;
183
parent = node_iface->getParentNode (a_node);
186
node = get_next_child_element_node (node_iface, parent);
187
return (node == a_node);
191
pseudo_class_add_sel_matches_node (CRSelEng * a_this,
192
CRAdditionalSel * a_add_sel,
195
enum CRStatus status = CR_OK;
196
CRPseudoClassSelectorHandler handler = NULL;
198
g_return_val_if_fail (a_this && PRIVATE (a_this)
200
&& a_add_sel->content.pseudo
201
&& a_add_sel->content.pseudo->name
202
&& a_add_sel->content.pseudo->name->stryng
203
&& a_add_sel->content.pseudo->name->stryng->str
206
status = cr_sel_eng_get_pseudo_class_selector_handler
207
(a_this, a_add_sel->content.pseudo->name->stryng->str,
208
a_add_sel->content.pseudo->type, &handler);
209
if (status != CR_OK || !handler)
212
return handler (a_this, a_add_sel, a_node);
216
*@param a_add_sel the class additional selector to consider.
217
*@param a_node the xml node to consider.
218
*@return TRUE if the class additional selector matches
219
*the xml node given in argument, FALSE otherwise.
222
class_add_sel_matches_node (CRAdditionalSel * a_add_sel,
223
CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
225
gboolean result = FALSE;
228
g_return_val_if_fail (a_add_sel
229
&& a_add_sel->type == CLASS_ADD_SELECTOR
230
&& a_add_sel->content.class_name
231
&& a_add_sel->content.class_name->stryng
232
&& a_add_sel->content.class_name->stryng->str
235
klass = a_node_iface->getProp (a_node, "class");
238
for (cur = klass; cur && *cur; cur++) {
240
&& cr_utils_is_white_space (*cur)
245
a_add_sel->content.class_name->stryng->str,
246
a_add_sel->content.class_name->stryng->len)) {
247
cur += a_add_sel->content.class_name->stryng->len;
249
|| cr_utils_is_white_space (*cur) == TRUE)
255
a_node_iface->freePropVal (klass);
263
*@return TRUE if the additional attribute selector matches
264
*the current xml node given in argument, FALSE otherwise.
265
*@param a_add_sel the additional attribute selector to consider.
266
*@param a_node the xml node to consider.
269
id_add_sel_matches_node (CRAdditionalSel * a_add_sel,
270
CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
272
gboolean result = FALSE;
275
g_return_val_if_fail (a_add_sel
276
&& a_add_sel->type == ID_ADD_SELECTOR
277
&& a_add_sel->content.id_name
278
&& a_add_sel->content.id_name->stryng
279
&& a_add_sel->content.id_name->stryng->str
281
g_return_val_if_fail (a_add_sel
282
&& a_add_sel->type == ID_ADD_SELECTOR
285
id = a_node_iface->getProp (a_node, "id");
287
if (!strncmp (id, a_add_sel->content.id_name->stryng->str,
288
a_add_sel->content.id_name->stryng->len)) {
291
a_node_iface->freePropVal (id);
298
*Returns TRUE if the instance of #CRAdditional selector matches
299
*the node given in parameter, FALSE otherwise.
300
*@param a_add_sel the additional selector to evaluate.
301
*@param a_node the xml node against whitch the selector is to
303
*return TRUE if the additional selector matches the current xml node
307
attr_add_sel_matches_node (CRAdditionalSel * a_add_sel,
308
CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
310
CRAttrSel *cur_sel = NULL;
312
g_return_val_if_fail (a_add_sel
313
&& a_add_sel->type == ATTRIBUTE_ADD_SELECTOR
316
for (cur_sel = a_add_sel->content.attr_sel;
317
cur_sel; cur_sel = cur_sel->next) {
319
|| !cur_sel->name->stryng
320
|| !cur_sel->name->stryng->str)
323
char *const value = a_node_iface->getProp (a_node, cur_sel->name->stryng->str);
325
goto free_and_return_false;
327
switch (cur_sel->match_way) {
333
|| !cur_sel->value->stryng
334
|| !cur_sel->value->stryng->str) {
335
goto free_and_return_false;
339
cur_sel->value->stryng->str)) {
340
goto free_and_return_false;
346
char const *ptr1 = NULL,
349
gboolean found = FALSE;
352
*here, make sure value is a space
353
*separated list of "words", where one
354
*value is exactly cur_sel->value->str
356
for (cur = value; *cur; cur++) {
358
*set ptr1 to the first non white space
361
while (cr_utils_is_white_space (*cur)
369
*set ptr2 to the end the word.
371
while (!cr_utils_is_white_space (*cur)
379
cur_sel->value->stryng->str,
388
goto free_and_return_false;
395
char const *ptr1 = NULL,
398
gboolean found = FALSE;
401
*here, make sure value is an hyphen
402
*separated list of "words", each of which
403
*starting with "cur_sel->value->str"
405
for (cur = value; *cur; cur++) {
410
while (*cur != '-' && *cur)
416
(ptr1, ptr2 - ptr1 + 1,
417
cur_sel->value->stryng->str)
425
goto free_and_return_false;
430
goto free_and_return_false;
433
a_node_iface->freePropVal (value);
436
free_and_return_false:
437
a_node_iface->freePropVal (value);
445
*Evaluates if a given additional selector matches an xml node.
446
*@param a_add_sel the additional selector to consider.
447
*@param a_node the xml node to consider.
448
*@return TRUE is a_add_sel matches a_node, FALSE otherwise.
451
additional_selector_matches_node (CRSelEng * a_this,
452
CRAdditionalSel * a_add_sel,
455
CRAdditionalSel *cur_add_sel = NULL, *tail = NULL ;
456
gboolean evaluated = FALSE ;
458
for (tail = a_add_sel ;
462
g_return_val_if_fail (tail, FALSE) ;
464
for (cur_add_sel = tail ;
466
cur_add_sel = cur_add_sel->prev) {
469
if (cur_add_sel->type == NO_ADD_SELECTOR) {
473
if (cur_add_sel->type == CLASS_ADD_SELECTOR
474
&& cur_add_sel->content.class_name
475
&& cur_add_sel->content.class_name->stryng
476
&& cur_add_sel->content.class_name->stryng->str) {
477
if (!class_add_sel_matches_node (cur_add_sel,
478
PRIVATE(a_this)->node_iface,
483
} else if (cur_add_sel->type == ID_ADD_SELECTOR
484
&& cur_add_sel->content.id_name
485
&& cur_add_sel->content.id_name->stryng
486
&& cur_add_sel->content.id_name->stryng->str) {
487
if (!id_add_sel_matches_node (cur_add_sel,
488
PRIVATE(a_this)->node_iface,
493
} else if (cur_add_sel->type == ATTRIBUTE_ADD_SELECTOR
494
&& cur_add_sel->content.attr_sel) {
496
*here, call a function that does the match
497
*against an attribute additionnal selector
500
if (!attr_add_sel_matches_node (cur_add_sel,
501
PRIVATE(a_this)->node_iface,
506
} else if (cur_add_sel->type == PSEUDO_CLASS_ADD_SELECTOR
507
&& cur_add_sel->content.pseudo) {
508
if (pseudo_class_add_sel_matches_node
509
(a_this, cur_add_sel, a_node)) {
515
if (evaluated == TRUE)
521
get_next_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
523
CRXMLNodePtr cur_node = a_node;
525
g_return_val_if_fail (a_node, NULL);
528
cur_node = a_node_iface->getNextSibling (cur_node);
529
} while (cur_node && !a_node_iface->isElementNode(cur_node));
533
/* TODO: Consider renaming this to get_first_child_element_node.
534
(cf get_first_parent_element_node, which does getParent until element node
535
rather than getNextSibling). */
537
get_next_child_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
539
CRXMLNodePtr cur_node = NULL;
541
g_return_val_if_fail (a_node, NULL);
543
cur_node = a_node_iface->getFirstChild (a_node);
546
if (a_node_iface->isElementNode (cur_node))
548
return get_next_element_node (a_node_iface, cur_node);
552
get_prev_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
554
CRXMLNodePtr cur_node = a_node;
556
g_return_val_if_fail (a_node, NULL);
559
cur_node = a_node_iface->getPrevSibling (cur_node);
560
} while (cur_node && !a_node_iface->isElementNode(cur_node));
565
get_next_parent_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
567
CRXMLNodePtr cur_node = a_node;
569
g_return_val_if_fail (a_node, NULL);
572
cur_node = a_node_iface->getParentNode (cur_node);
573
} while (cur_node && !a_node_iface->isElementNode (cur_node));
578
*Evaluate a selector (a simple selectors list) and says
579
*if it matches the xml node given in parameter.
580
*The algorithm used here is the following:
581
*Walk the combinator separated list of simple selectors backward, starting
582
*from the end of the list. For each simple selector, looks if
583
*if matches the current node.
585
*@param a_this the selection engine.
586
*@param a_sel the simple selection list.
587
*@param a_node the xml node.
588
*@param a_result out parameter. Set to true if the
589
*selector matches the xml node, FALSE otherwise.
590
*@param a_recurse if set to TRUE, the function will walk to
591
*the next simple selector (after the evaluation of the current one)
592
*and recursively evaluate it. Must be usually set to TRUE unless you
593
*know what you are doing.
596
sel_matches_node_real (CRSelEng * a_this, CRSimpleSel * a_sel,
597
CRXMLNodePtr a_node, gboolean * a_result,
598
gboolean a_eval_sel_list_from_end,
601
CRSimpleSel *cur_sel = NULL;
602
CRXMLNodePtr cur_node = NULL;
603
CRNodeIface const *node_iface = NULL;
605
g_return_val_if_fail (a_this && PRIVATE (a_this)
607
&& a_result, CR_BAD_PARAM_ERROR);
609
node_iface = PRIVATE(a_this)->node_iface;
612
if (!node_iface->isElementNode(a_node))
615
if (a_eval_sel_list_from_end == TRUE) {
616
/*go and get the last simple selector of the list */
617
for (cur_sel = a_sel;
618
cur_sel && cur_sel->next; cur_sel = cur_sel->next) ;
623
for (cur_node = a_node; cur_sel; cur_sel = cur_sel->prev) {
624
if (((cur_sel->type_mask & TYPE_SELECTOR)
626
&& cur_sel->name->stryng
627
&& cur_sel->name->stryng->str)
628
&& (!strcmp (cur_sel->name->stryng->str,
629
node_iface->getLocalName(cur_node))))
630
|| (cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
632
*this simple selector
633
*matches the current xml node
634
*Let's see if the preceding
635
*simple selectors also match
636
*their xml node counterpart.
638
if (cur_sel->add_sel) {
639
if (additional_selector_matches_node (a_this, cur_sel->add_sel,
641
goto walk_a_step_in_expr;
646
goto walk_a_step_in_expr;
649
if (!(cur_sel->type_mask & TYPE_SELECTOR)
650
&& !(cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
651
if (!cur_sel->add_sel) {
654
if (additional_selector_matches_node
655
(a_this, cur_sel->add_sel, cur_node)
657
goto walk_a_step_in_expr;
666
if (a_recurse == FALSE) {
672
*here, depending on the combinator of cur_sel
673
*choose the axis of the xml tree traversal
674
*and walk one step in the xml tree.
679
switch (cur_sel->combinator) {
683
case COMB_WS: /*descendant selector */
685
CRXMLNodePtr n = NULL;
686
enum CRStatus status = CR_OK;
687
gboolean matches = FALSE;
690
*walk the xml tree upward looking for a parent
691
*node that matches the preceding selector.
693
for (n = node_iface->getParentNode (cur_node);
695
n = node_iface->getParentNode (n)) {
696
status = sel_matches_node_real
697
(a_this, cur_sel->prev,
698
n, &matches, FALSE, TRUE);
703
if (matches == TRUE) {
711
*didn't find any ancestor that matches
712
*the previous simple selector.
717
*in this case, the preceding simple sel
718
*will have been interpreted twice, which
719
*is a cpu and mem waste ... I need to find
720
*another way to do this. Anyway, this is
721
*my first attempt to write this function and
722
*I am a bit clueless.
728
cur_node = get_prev_element_node (node_iface, cur_node);
734
cur_node = get_next_parent_element_node (node_iface, cur_node);
746
*if we reached this point, it means the selector matches
757
*Returns array of the ruleset statements that matches the
759
*The engine keeps in memory the last statement he
760
*visited during the match. So, the next call
761
*to this function will eventually return a rulesets list starting
762
*from the last ruleset statement visited during the previous call.
763
*The enable users to get matching rulesets in an incremental way.
764
*Note that for each statement returned,
765
*the engine calculates the specificity of the selector
766
*that matched the xml node and stores it in the "specifity" field
767
*of the statement structure.
769
*@param a_sel_eng the current selection engine
770
*@param a_node the xml node for which the request
772
*@param a_sel_list the list of selectors to perform the search in.
773
*@param a_rulesets in/out parameter. A pointer to the
774
*returned array of rulesets statements that match the xml node
775
*given in parameter. The caller allocates the array before calling this
777
*@param a_len in/out parameter the length (in sizeof (#CRStatement*))
778
*of the returned array.
779
*(the length of a_rulesets, more precisely).
780
*The caller must set it to the length of a_ruleset prior to calling this
781
*function. In return, the function sets it to the length
782
*(in sizeof (#CRStatement)) of the actually returned CRStatement array.
783
*@return CR_OUTPUT_TOO_SHORT_ERROR if found more rulesets than the size
784
*of the a_rulesets array. In this case, the first *a_len rulesets found
785
*are put in a_rulesets, and a further call will return the following
786
*ruleset(s) following the same principle.
787
*@return CR_OK if all the rulesets found have been returned. In this
788
*case, *a_len is set to the actual number of ruleset found.
789
*@return CR_BAD_PARAM_ERROR in case any of the given parameter are
790
*bad (e.g null pointer).
791
*@return CR_ERROR if any other error occured.
794
cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
795
CRStyleSheet * a_stylesheet,
797
CRStatement ** a_rulesets,
800
CRStatement *cur_stmt = NULL;
801
CRSelector *sel_list = NULL,
803
gboolean matches = FALSE;
804
enum CRStatus status = CR_OK;
807
g_return_val_if_fail (a_this
809
&& a_node && a_rulesets, CR_BAD_PARAM_ERROR);
811
if (!a_stylesheet->statements) {
818
*if this stylesheet is "new one"
819
*let's remember it for subsequent calls.
821
if (PRIVATE (a_this)->sheet != a_stylesheet) {
822
PRIVATE (a_this)->sheet = a_stylesheet;
823
PRIVATE (a_this)->cur_stmt = a_stylesheet->statements;
827
*walk through the list of statements and,
828
*get the selectors list inside the statements that
829
*contain some, and try to match our xml node in these
832
for (cur_stmt = PRIVATE (a_this)->cur_stmt, i = 0;
833
(PRIVATE (a_this)->cur_stmt = cur_stmt);
834
cur_stmt = cur_stmt->next) {
836
*initialyze the selector list in which we will
837
*really perform the search.
842
*get the the damn selector list in
843
*which we have to look
845
switch (cur_stmt->type) {
847
if (cur_stmt->kind.ruleset
848
&& cur_stmt->kind.ruleset->sel_list) {
849
sel_list = cur_stmt->kind.ruleset->sel_list;
853
case AT_MEDIA_RULE_STMT:
854
if (cur_stmt->kind.media_rule
855
&& cur_stmt->kind.media_rule->rulesets
856
&& cur_stmt->kind.media_rule->rulesets->
858
&& cur_stmt->kind.media_rule->rulesets->
859
kind.ruleset->sel_list) {
861
cur_stmt->kind.media_rule->
862
rulesets->kind.ruleset->sel_list;
866
case AT_IMPORT_RULE_STMT:
868
*some recursivity may be needed here.
869
*I don't like this :(
880
*now, we have a comma separated selector list to look in.
881
*let's walk it and try to match the xml_node
882
*on each item of the list.
884
for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next) {
885
if (!cur_sel->simple_sel)
888
status = cr_sel_eng_matches_node
889
(a_this, cur_sel->simple_sel,
892
if (status == CR_OK && matches == TRUE) {
894
*bingo!!! we found one ruleset that
895
*matches that fucking node.
896
*lets put it in the out array.
900
a_rulesets[i] = cur_stmt;
904
*For the cascade computing algorithm
905
*(which is gonna take place later)
906
*we must compute the specificity
907
*(css2 spec chap 6.4.1) of the selector
908
*that matched the current xml node
909
*and store it in the css2 statement
910
*(statement == ruleset here).
912
status = cr_simple_sel_compute_specificity (cur_sel->simple_sel);
914
g_return_val_if_fail (status == CR_OK,
916
cur_stmt->specificity =
917
cur_sel->simple_sel->
922
return CR_OUTPUT_TOO_SHORT_ERROR;
929
*if we reached this point, it means
930
*we reached the end of stylesheet.
931
*no need to store any info about the stylesheet
934
g_return_val_if_fail (!PRIVATE (a_this)->cur_stmt, CR_ERROR);
935
PRIVATE (a_this)->sheet = NULL;
941
put_css_properties_in_props_list (CRPropList ** a_props, CRStatement * a_stmt)
943
CRPropList *props = NULL,
946
CRDeclaration *cur_decl = NULL;
948
g_return_val_if_fail (a_props && a_stmt
949
&& a_stmt->type == RULESET_STMT
950
&& a_stmt->kind.ruleset, CR_BAD_PARAM_ERROR);
954
for (cur_decl = a_stmt->kind.ruleset->decl_list;
955
cur_decl; cur_decl = cur_decl->next) {
961
if (!cur_decl->property
962
|| !cur_decl->property->stryng
963
|| !cur_decl->property->stryng->str)
966
*First, test if the property is not
967
*already present in our properties list
968
*If yes, apply the cascading rules to
969
*compute the precedence. If not, insert
970
*the property into the list
972
cr_prop_list_lookup_prop (props,
977
tmp_props = cr_prop_list_append2
978
(props, cur_decl->property, cur_decl);
987
*A property with the same name already exists.
989
*some cascading rules
990
*to compute the precedence.
992
cr_prop_list_get_decl (pair, &decl);
993
g_return_val_if_fail (decl, CR_ERROR);
996
*first, look at the origin.
998
*"for normal declarations,
999
*author style sheets override user
1000
*style sheets which override
1001
*the default style sheet."
1003
if (decl->parent_statement
1004
&& decl->parent_statement->parent_sheet
1005
&& (decl->parent_statement->parent_sheet->origin
1006
< a_stmt->parent_sheet->origin)) {
1008
*if the already selected declaration
1009
*is marked as being !important the current
1010
*declaration must not overide it
1011
*(unless the already selected declaration
1014
if (decl->important == TRUE
1015
&& decl->parent_statement->parent_sheet->origin
1019
tmp_props = cr_prop_list_unlink (props, pair);
1021
cr_prop_list_destroy (pair);
1025
props = cr_prop_list_append2
1026
(props, cur_decl->property, cur_decl);
1029
} else if (decl->parent_statement
1030
&& decl->parent_statement->parent_sheet
1031
&& (decl->parent_statement->
1032
parent_sheet->origin
1033
> a_stmt->parent_sheet->origin)) {
1035
("We should not reach this line\n");
1040
*A property with the same
1041
*name and the same origin already exists.
1042
*shit. This is lasting longer than expected ...
1043
*Luckily, the spec says in 6.4.1:
1044
*"more specific selectors will override
1047
*"if two rules have the same weight,
1048
*origin and specificity,
1049
*the later specified wins"
1051
if (a_stmt->specificity
1052
>= decl->parent_statement->specificity) {
1053
if (decl->important == TRUE)
1055
props = cr_prop_list_unlink (props, pair);
1057
cr_prop_list_destroy (pair);
1060
props = cr_prop_list_append2 (props,
1065
/*TODO: this may leak. Check this out */
1072
set_style_from_props (CRStyle * a_style, CRPropList * a_props)
1074
CRPropList *cur = NULL;
1075
CRDeclaration *decl = NULL;
1077
for (cur = a_props; cur; cur = cr_prop_list_get_next (cur)) {
1078
cr_prop_list_get_decl (cur, &decl);
1079
cr_style_set_style_from_decl (a_style, decl);
1084
/****************************************
1086
****************************************/
1089
*Creates a new instance of #CRSelEng.
1090
*@return the newly built instance of #CRSelEng of
1091
*NULL if an error occurs.
1094
cr_sel_eng_new (void)
1096
CRSelEng *result = NULL;
1098
result = (CRSelEng *) g_try_malloc (sizeof (CRSelEng));
1100
cr_utils_trace_info ("Out of memory");
1103
memset (result, 0, sizeof (CRSelEng));
1105
PRIVATE (result) = (CRSelEngPriv *) g_try_malloc (sizeof (CRSelEngPriv));
1106
if (!PRIVATE (result)) {
1107
cr_utils_trace_info ("Out of memory");
1111
memset (PRIVATE (result), 0, sizeof (CRSelEngPriv));
1112
cr_sel_eng_register_pseudo_class_sel_handler
1113
(result, "first-child",
1114
IDENT_PSEUDO, /*(CRPseudoClassSelectorHandler)*/
1115
first_child_pseudo_class_handler);
1116
cr_sel_eng_register_pseudo_class_sel_handler
1118
FUNCTION_PSEUDO, /*(CRPseudoClassSelectorHandler)*/
1119
lang_pseudo_class_handler);
1125
*Adds a new handler entry in the handlers entry table.
1126
*@param a_this the current instance of #CRSelEng
1127
*@param a_pseudo_class_sel_name the name of the pseudo class selector.
1128
*@param a_pseudo_class_type the type of the pseudo class selector.
1129
*@param a_handler the actual handler or callback to be called during
1130
*the selector evaluation process.
1131
*@return CR_OK, upon successful completion, an error code otherwise.
1134
cr_sel_eng_register_pseudo_class_sel_handler (CRSelEng * a_this,
1136
enum CRPseudoType a_type,
1137
CRPseudoClassSelectorHandler
1140
struct CRPseudoClassSelHandlerEntry *handler_entry = NULL;
1143
g_return_val_if_fail (a_this && PRIVATE (a_this)
1144
&& a_handler && a_name, CR_BAD_PARAM_ERROR);
1146
handler_entry = (struct CRPseudoClassSelHandlerEntry *) g_try_malloc
1147
(sizeof (struct CRPseudoClassSelHandlerEntry));
1148
if (!handler_entry) {
1149
return CR_OUT_OF_MEMORY_ERROR;
1151
memset (handler_entry, 0,
1152
sizeof (struct CRPseudoClassSelHandlerEntry));
1153
handler_entry->name = g_strdup (a_name);
1154
handler_entry->type = a_type;
1155
handler_entry->handler = a_handler;
1156
list = g_list_append (PRIVATE (a_this)->pcs_handlers, handler_entry);
1158
return CR_OUT_OF_MEMORY_ERROR;
1160
PRIVATE (a_this)->pcs_handlers = list;
1165
cr_sel_eng_unregister_pseudo_class_sel_handler (CRSelEng * a_this,
1167
enum CRPseudoType a_type)
1170
*deleted_elem = NULL;
1171
gboolean found = FALSE;
1172
struct CRPseudoClassSelHandlerEntry *entry = NULL;
1174
g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1176
for (elem = PRIVATE (a_this)->pcs_handlers;
1177
elem; elem = g_list_next (elem)) {
1178
entry = (struct CRPseudoClassSelHandlerEntry *) elem->data;
1179
if (!strcmp (entry->name, a_name)
1180
&& entry->type == a_type) {
1186
return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
1187
PRIVATE (a_this)->pcs_handlers = g_list_delete_link
1188
(PRIVATE (a_this)->pcs_handlers, elem);
1189
entry = (struct CRPseudoClassSelHandlerEntry *) elem->data;
1191
g_free (entry->name);
1195
g_list_free (deleted_elem);
1201
*Unregisters all the pseudo class sel handlers
1202
*and frees all the associated allocated datastructures.
1203
*@param a_this the current instance of #CRSelEng .
1204
*@return CR_OK upon succesful completion, an error code
1208
cr_sel_eng_unregister_all_pseudo_class_sel_handlers (CRSelEng * a_this)
1211
struct CRPseudoClassSelHandlerEntry *entry = NULL;
1213
g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1215
if (!PRIVATE (a_this)->pcs_handlers)
1217
for (elem = PRIVATE (a_this)->pcs_handlers;
1218
elem; elem = g_list_next (elem)) {
1219
entry = (struct CRPseudoClassSelHandlerEntry *) elem->data;
1223
g_free (entry->name);
1229
g_list_free (PRIVATE (a_this)->pcs_handlers);
1230
PRIVATE (a_this)->pcs_handlers = NULL;
1235
cr_sel_eng_get_pseudo_class_selector_handler (CRSelEng * a_this,
1237
enum CRPseudoType a_type,
1238
CRPseudoClassSelectorHandler *
1242
struct CRPseudoClassSelHandlerEntry *entry = NULL;
1243
gboolean found = FALSE;
1245
g_return_val_if_fail (a_this && PRIVATE (a_this)
1246
&& a_name, CR_BAD_PARAM_ERROR);
1248
for (elem = PRIVATE (a_this)->pcs_handlers;
1249
elem; elem = g_list_next (elem)) {
1250
entry = (struct CRPseudoClassSelHandlerEntry *) elem->data;
1251
if (!strcmp (a_name, entry->name)
1252
&& entry->type == a_type) {
1259
return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
1260
*a_handler = entry->handler;
1265
*Evaluates a chained list of simple selectors (known as a css2 selector).
1266
*Says wheter if this selector matches the xml node given in parameter or
1268
*@param a_this the selection engine.
1269
*@param a_sel the simple selector against which the xml node
1270
*is going to be matched.
1271
*@param a_node the node against which the selector is going to be matched.
1272
*@param a_result out parameter. The result of the match. Is set to
1273
*TRUE if the selector matches the node, FALSE otherwise. This value
1274
*is considered if and only if this functions returns CR_OK.
1275
*@return the CR_OK if the selection ran correctly, an error code otherwise.
1278
cr_sel_eng_matches_node (CRSelEng * a_this, CRSimpleSel * a_sel,
1279
CRXMLNodePtr a_node, gboolean * a_result)
1281
g_return_val_if_fail (a_this && PRIVATE (a_this)
1283
&& a_result, CR_BAD_PARAM_ERROR);
1285
if (!PRIVATE(a_this)->node_iface->isElementNode (a_node)) {
1290
return sel_matches_node_real (a_this, a_sel,
1296
*Returns an array of pointers to selectors that matches
1297
*the xml node given in parameter.
1299
*@param a_this the current instance of the selection engine.
1300
*@param a_sheet the stylesheet that holds the selectors.
1301
*@param a_node the xml node to consider during the walk thru
1303
*@param a_rulesets out parameter. A pointer to an array of
1304
*rulesets statement pointers. *a_rulesets is allocated by
1305
*this function and must be freed by the caller. However, the caller
1306
*must not alter the rulesets statements pointer because they
1307
*point to statements that are still in the css stylesheet.
1308
*@param a_len the length of *a_ruleset.
1309
*@return CR_OK upon sucessfull completion, an error code otherwise.
1312
cr_sel_eng_get_matched_rulesets (CRSelEng * a_this,
1313
CRStyleSheet * a_sheet,
1314
CRXMLNodePtr a_node,
1315
CRStatement *** a_rulesets, gulong * a_len)
1317
CRStatement **stmts_tab = NULL;
1318
enum CRStatus status = CR_OK;
1319
gulong tab_size = 0,
1322
gushort stmts_chunck_size = 8;
1324
g_return_val_if_fail (a_this
1327
&& a_rulesets && *a_rulesets == NULL
1328
&& a_len, CR_BAD_PARAM_ERROR);
1330
stmts_tab = (CRStatement **) g_try_malloc (stmts_chunck_size * sizeof (CRStatement *));
1333
cr_utils_trace_info ("Out of memory");
1337
memset (stmts_tab, 0, stmts_chunck_size * sizeof (CRStatement *));
1339
tab_size = stmts_chunck_size;
1342
while ((status = cr_sel_eng_get_matched_rulesets_real
1343
(a_this, a_sheet, a_node, stmts_tab + index, &tab_len))
1344
== CR_OUTPUT_TOO_SHORT_ERROR) {
1345
stmts_tab = (CRStatement **) g_try_realloc (stmts_tab,
1346
(tab_size + stmts_chunck_size)
1347
* sizeof (CRStatement *));
1349
cr_utils_trace_info ("Out of memory");
1353
tab_size += stmts_chunck_size;
1355
tab_len = tab_size - index;
1358
tab_len = tab_size - stmts_chunck_size + tab_len;
1359
*a_rulesets = stmts_tab;
1378
cr_sel_eng_get_matched_properties_from_cascade (CRSelEng * a_this,
1379
CRCascade * a_cascade,
1380
CRXMLNodePtr a_node,
1381
CRPropList ** a_props)
1383
CRStatement **stmts_tab = NULL;
1384
enum CRStatus status = CR_OK;
1385
gulong tab_size = 0,
1389
enum CRStyleOrigin origin;
1390
gushort stmts_chunck_size = 8;
1391
CRStyleSheet *sheet = NULL;
1393
g_return_val_if_fail (a_this
1395
&& a_node && a_props, CR_BAD_PARAM_ERROR);
1397
for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin = (enum CRStyleOrigin) (origin + 1)) {
1398
sheet = cr_cascade_get_sheet (a_cascade, origin);
1401
if (tab_size - index < 1) {
1402
stmts_tab = (CRStatement **) g_try_realloc
1403
(stmts_tab, (tab_size + stmts_chunck_size)
1404
* sizeof (CRStatement *));
1406
cr_utils_trace_info ("Out of memory");
1410
tab_size += stmts_chunck_size;
1412
*compute the max size left for
1413
*cr_sel_eng_get_matched_rulesets_real()'s output tab
1415
tab_len = tab_size - index;
1417
while ((status = cr_sel_eng_get_matched_rulesets_real
1418
(a_this, sheet, a_node, stmts_tab + index, &tab_len))
1419
== CR_OUTPUT_TOO_SHORT_ERROR) {
1420
stmts_tab = (CRStatement **) g_try_realloc
1421
(stmts_tab, (tab_size + stmts_chunck_size)
1422
* sizeof (CRStatement *));
1424
cr_utils_trace_info ("Out of memory");
1428
tab_size += stmts_chunck_size;
1431
*compute the max size left for
1432
*cr_sel_eng_get_matched_rulesets_real()'s output tab
1434
tab_len = tab_size - index;
1436
if (status != CR_OK) {
1437
cr_utils_trace_info ("Error while running "
1442
tab_len = tab_size - index;
1446
*TODO, walk down the stmts_tab and build the
1447
*property_name/declaration hashtable.
1448
*Make sure one can walk from the declaration to
1451
for (i = 0; i < index; i++) {
1452
CRStatement *stmt = stmts_tab[i];
1456
switch (stmt->type) {
1458
if (!stmt->parent_sheet)
1460
status = put_css_properties_in_props_list
1479
cr_sel_eng_get_matched_style (CRSelEng * a_this,
1480
CRCascade * a_cascade,
1481
CRXMLNodePtr a_node,
1482
CRStyle * a_parent_style,
1484
gboolean a_set_props_to_initial_values)
1486
enum CRStatus status = CR_OK;
1488
CRPropList *props = NULL;
1490
g_return_val_if_fail (a_this && a_cascade
1491
&& a_node && a_style, CR_BAD_PARAM_ERROR);
1493
status = cr_sel_eng_get_matched_properties_from_cascade
1494
(a_this, a_cascade, a_node, &props);
1496
g_return_val_if_fail (status == CR_OK, status);
1499
*a_style = cr_style_new (a_set_props_to_initial_values) ;
1500
g_return_val_if_fail (*a_style, CR_ERROR);
1502
if (a_set_props_to_initial_values == TRUE) {
1503
cr_style_set_props_to_initial_values (*a_style) ;
1505
cr_style_set_props_to_default_values (*a_style);
1508
(*a_style)->parent_style = a_parent_style;
1510
set_style_from_props (*a_style, props);
1512
cr_prop_list_destroy (props);
1520
*The destructor of #CRSelEng
1521
*@param a_this the current instance of the selection engine.
1524
cr_sel_eng_destroy (CRSelEng * a_this)
1526
g_return_if_fail (a_this);
1528
if (!PRIVATE (a_this))
1530
if (PRIVATE (a_this)->pcs_handlers) {
1531
cr_sel_eng_unregister_all_pseudo_class_sel_handlers
1533
PRIVATE (a_this)->pcs_handlers = NULL ;
1535
g_free (PRIVATE (a_this));
1536
PRIVATE (a_this) = NULL;