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 3 of the GNU 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.
26 #include "cr-sel-eng.h"
31 *The definition of the #CRSelEng class.
32 *The #CRSelEng is actually the "Selection Engine"
33 *class. This is highly experimental for at the moment and
34 *its api is very likely to change in a near future.
37 #define PRIVATE(a_this) (a_this)->priv
39 struct CRPseudoClassSelHandlerEntry {
41 enum CRPseudoType type;
42 CRPseudoClassSelectorHandler handler;
45 struct _CRSelEngPriv {
47 gboolean case_sensitive;
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,
63 static gboolean id_add_sel_matches_node (CRAdditionalSel * a_add_sel,
66 static gboolean attr_add_sel_matches_node (CRAdditionalSel * a_add_sel,
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 xmlNode *get_next_element_node (xmlNode * a_node);
103 static xmlNode *get_next_child_element_node (xmlNode * a_node);
105 static xmlNode *get_prev_element_node (xmlNode * a_node);
107 static xmlNode *get_next_parent_element_node (xmlNode * a_node);
110 lang_pseudo_class_handler (CRSelEng * a_this,
111 CRAdditionalSel * a_sel, xmlNode * a_node)
113 xmlNode *node = a_node;
115 gboolean result = FALSE;
117 g_return_val_if_fail (a_this && PRIVATE (a_this)
118 && a_sel && a_sel->content.pseudo
119 && a_sel->content.pseudo
120 && a_sel->content.pseudo->name
121 && a_sel->content.pseudo->name->stryng
122 && a_node, CR_BAD_PARAM_ERROR);
124 if (strncmp (a_sel->content.pseudo->name->stryng->str,
126 || !a_sel->content.pseudo->type == FUNCTION_PSEUDO) {
127 cr_utils_trace_info ("This handler is for :lang only");
128 return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
130 /*lang code should exist and be at least of length 2 */
131 if (!a_sel->content.pseudo->extra
132 || !a_sel->content.pseudo->extra->stryng
133 || a_sel->content.pseudo->extra->stryng->len < 2)
135 for (; node; node = get_next_parent_element_node (node)) {
136 val = xmlGetProp (node, "lang");
139 a_sel->content.pseudo->extra->stryng->str,
140 a_sel->content.pseudo->extra->stryng->len)) {
153 first_child_pseudo_class_handler (CRSelEng * a_this,
154 CRAdditionalSel * a_sel, xmlNode * a_node)
156 xmlNode *node = NULL;
158 g_return_val_if_fail (a_this && PRIVATE (a_this)
159 && a_sel && a_sel->content.pseudo
160 && a_sel->content.pseudo
161 && a_sel->content.pseudo->name
162 && a_sel->content.pseudo->name->stryng
163 && a_node, CR_BAD_PARAM_ERROR);
165 if (strcmp (a_sel->content.pseudo->name->stryng->str,
167 || !a_sel->content.pseudo->type == IDENT_PSEUDO) {
168 cr_utils_trace_info ("This handler is for :first-child only");
169 return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
173 node = get_next_child_element_node (a_node->parent);
180 pseudo_class_add_sel_matches_node (CRSelEng * a_this,
181 CRAdditionalSel * a_add_sel,
184 enum CRStatus status = CR_OK;
185 CRPseudoClassSelectorHandler handler = NULL;
187 g_return_val_if_fail (a_this && PRIVATE (a_this)
189 && a_add_sel->content.pseudo
190 && a_add_sel->content.pseudo->name
191 && a_add_sel->content.pseudo->name->stryng
192 && a_add_sel->content.pseudo->name->stryng->str
193 && a_node, CR_BAD_PARAM_ERROR);
195 status = cr_sel_eng_get_pseudo_class_selector_handler
196 (a_this, a_add_sel->content.pseudo->name->stryng->str,
197 a_add_sel->content.pseudo->type, &handler);
198 if (status != CR_OK || !handler)
201 return handler (a_this, a_add_sel, a_node);
205 *@param a_add_sel the class additional selector to consider.
206 *@param a_node the xml node to consider.
207 *@return TRUE if the class additional selector matches
208 *the xml node given in argument, FALSE otherwise.
211 class_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
213 gboolean result = FALSE;
214 xmlChar *klass = NULL,
217 g_return_val_if_fail (a_add_sel
218 && a_add_sel->type == CLASS_ADD_SELECTOR
219 && a_add_sel->content.class_name
220 && a_add_sel->content.class_name->stryng
221 && a_add_sel->content.class_name->stryng->str
224 if (xmlHasProp (a_node, "class")) {
225 klass = xmlGetProp (a_node, "class");
226 for (cur = klass; cur && *cur; cur++) {
228 && cr_utils_is_white_space (*cur)
233 a_add_sel->content.class_name->stryng->str,
234 a_add_sel->content.class_name->stryng->len)) {
235 cur += a_add_sel->content.class_name->stryng->len;
237 || cr_utils_is_white_space (*cur) == TRUE)
253 *@return TRUE if the additional attribute selector matches
254 *the current xml node given in argument, FALSE otherwise.
255 *@param a_add_sel the additional attribute selector to consider.
256 *@param a_node the xml node to consider.
259 id_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
261 gboolean result = FALSE;
264 g_return_val_if_fail (a_add_sel
265 && a_add_sel->type == ID_ADD_SELECTOR
266 && a_add_sel->content.id_name
267 && a_add_sel->content.id_name->stryng
268 && a_add_sel->content.id_name->stryng->str
270 g_return_val_if_fail (a_add_sel
271 && a_add_sel->type == ID_ADD_SELECTOR
274 if (xmlHasProp (a_node, "id")) {
275 id = xmlGetProp (a_node, "id");
276 if (!strncmp (id, a_add_sel->content.id_name->stryng->str,
277 a_add_sel->content.id_name->stryng->len)) {
289 *Returns TRUE if the instance of #CRAdditional selector matches
290 *the node given in parameter, FALSE otherwise.
291 *@param a_add_sel the additional selector to evaluate.
292 *@param a_node the xml node against whitch the selector is to
294 *return TRUE if the additional selector matches the current xml node
298 attr_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
300 CRAttrSel *cur_sel = NULL;
302 g_return_val_if_fail (a_add_sel
303 && a_add_sel->type == ATTRIBUTE_ADD_SELECTOR
306 for (cur_sel = a_add_sel->content.attr_sel;
307 cur_sel; cur_sel = cur_sel->next) {
308 switch (cur_sel->match_way) {
311 || !cur_sel->name->stryng
312 || !cur_sel->name->stryng->str)
315 if (!xmlHasProp (a_node,
316 cur_sel->name->stryng->str))
322 xmlChar *value = NULL;
325 || !cur_sel->name->stryng
326 || !cur_sel->name->stryng->str
328 || !cur_sel->value->stryng
329 || !cur_sel->value->stryng->str)
334 cur_sel->name->stryng->str))
339 cur_sel->name->stryng->str);
344 cur_sel->value->stryng->str)) {
354 xmlChar *value = NULL,
358 gboolean found = FALSE;
362 cur_sel->name->stryng->str))
366 cur_sel->name->stryng->str);
372 *here, make sure value is a space
373 *separated list of "words", where one
374 *value is exactly cur_sel->value->str
376 for (cur = value; *cur; cur++) {
378 *set ptr1 to the first non white space
381 while (cr_utils_is_white_space
382 (*cur) == TRUE && *cur)
389 *set ptr2 to the end the word.
391 while (cr_utils_is_white_space
392 (*cur) == FALSE && *cur)
399 cur_sel->value->stryng->str,
407 if (found == FALSE) {
417 xmlChar *value = NULL,
421 gboolean found = FALSE;
425 cur_sel->name->stryng->str))
429 cur_sel->name->stryng->str);
432 *here, make sure value is an hyphen
433 *separated list of "words", each of which
434 *starting with "cur_sel->value->str"
436 for (cur = value; *cur; cur++) {
441 while (*cur != '-' && *cur)
447 (ptr1, ptr2 - ptr1 + 1,
448 cur_sel->value->stryng->str)
455 if (found == FALSE) {
471 *Evaluates if a given additional selector matches an xml node.
472 *@param a_add_sel the additional selector to consider.
473 *@param a_node the xml node to consider.
474 *@return TRUE is a_add_sel matches a_node, FALSE otherwise.
477 additional_selector_matches_node (CRSelEng * a_this,
478 CRAdditionalSel * a_add_sel,
481 CRAdditionalSel *cur_add_sel = NULL, *tail = NULL ;
482 gboolean evaluated = FALSE ;
484 for (tail = a_add_sel ;
488 g_return_val_if_fail (tail, FALSE) ;
490 for (cur_add_sel = tail ;
492 cur_add_sel = cur_add_sel->prev) {
495 if (cur_add_sel->type == NO_ADD_SELECTOR) {
499 if (cur_add_sel->type == CLASS_ADD_SELECTOR
500 && cur_add_sel->content.class_name
501 && cur_add_sel->content.class_name->stryng
502 && cur_add_sel->content.class_name->stryng->str) {
503 if (class_add_sel_matches_node (cur_add_sel,
508 } else if (cur_add_sel->type == ID_ADD_SELECTOR
509 && cur_add_sel->content.id_name
510 && cur_add_sel->content.id_name->stryng
511 && cur_add_sel->content.id_name->stryng->str) {
512 if (id_add_sel_matches_node (cur_add_sel, a_node) == FALSE) {
516 } else if (cur_add_sel->type == ATTRIBUTE_ADD_SELECTOR
517 && cur_add_sel->content.attr_sel) {
519 *here, call a function that does the match
520 *against an attribute additionnal selector
523 if (attr_add_sel_matches_node (cur_add_sel, a_node)
528 } else if (cur_add_sel->type == PSEUDO_CLASS_ADD_SELECTOR
529 && cur_add_sel->content.pseudo) {
530 if (pseudo_class_add_sel_matches_node
531 (a_this, cur_add_sel, a_node) == TRUE) {
537 if (evaluated == TRUE)
543 get_next_element_node (xmlNode * a_node)
545 xmlNode *cur_node = NULL;
547 g_return_val_if_fail (a_node, NULL);
549 cur_node = a_node->next;
550 while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
551 cur_node = cur_node->next;
557 get_next_child_element_node (xmlNode * a_node)
559 xmlNode *cur_node = NULL;
561 g_return_val_if_fail (a_node, NULL);
563 cur_node = a_node->children;
566 if (a_node->children->type == XML_ELEMENT_NODE)
567 return a_node->children;
568 return get_next_element_node (a_node->children);
572 get_prev_element_node (xmlNode * a_node)
574 xmlNode *cur_node = NULL;
576 g_return_val_if_fail (a_node, NULL);
578 cur_node = a_node->prev;
579 while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
580 cur_node = cur_node->prev;
586 get_next_parent_element_node (xmlNode * a_node)
588 xmlNode *cur_node = NULL;
590 g_return_val_if_fail (a_node, NULL);
592 cur_node = a_node->parent;
593 while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
594 cur_node = cur_node->parent;
600 *Evaluate a selector (a simple selectors list) and says
601 *if it matches the xml node given in parameter.
602 *The algorithm used here is the following:
603 *Walk the combinator separated list of simple selectors backward, starting
604 *from the end of the list. For each simple selector, looks if
605 *if matches the current node.
607 *@param a_this the selection engine.
608 *@param a_sel the simple selection list.
609 *@param a_node the xml node.
610 *@param a_result out parameter. Set to true if the
611 *selector matches the xml node, FALSE otherwise.
612 *@param a_recurse if set to TRUE, the function will walk to
613 *the next simple selector (after the evaluation of the current one)
614 *and recursively evaluate it. Must be usually set to TRUE unless you
615 *know what you are doing.
618 sel_matches_node_real (CRSelEng * a_this, CRSimpleSel * a_sel,
619 xmlNode * a_node, gboolean * a_result,
620 gboolean a_eval_sel_list_from_end,
623 CRSimpleSel *cur_sel = NULL;
624 xmlNode *cur_node = NULL;
626 g_return_val_if_fail (a_this && PRIVATE (a_this)
628 && a_result, CR_BAD_PARAM_ERROR);
632 if (a_node->type != XML_ELEMENT_NODE)
635 if (a_eval_sel_list_from_end == TRUE) {
636 /*go and get the last simple selector of the list */
637 for (cur_sel = a_sel;
638 cur_sel && cur_sel->next; cur_sel = cur_sel->next) ;
643 for (cur_node = a_node; cur_sel; cur_sel = cur_sel->prev) {
644 if (((cur_sel->type_mask & TYPE_SELECTOR)
646 && cur_sel->name->stryng
647 && cur_sel->name->stryng->str)
648 && (!strcmp (cur_sel->name->stryng->str,
650 || (cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
652 *this simple selector
653 *matches the current xml node
654 *Let's see if the preceding
655 *simple selectors also match
656 *their xml node counterpart.
658 if (cur_sel->add_sel) {
659 if (additional_selector_matches_node (a_this, cur_sel->add_sel,
661 goto walk_a_step_in_expr;
666 goto walk_a_step_in_expr;
669 if (!(cur_sel->type_mask & TYPE_SELECTOR)
670 && !(cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
671 if (!cur_sel->add_sel) {
674 if (additional_selector_matches_node
675 (a_this, cur_sel->add_sel, cur_node)
677 goto walk_a_step_in_expr;
686 if (a_recurse == FALSE) {
692 *here, depending on the combinator of cur_sel
693 *choose the axis of the xml tree traversal
694 *and walk one step in the xml tree.
699 switch (cur_sel->combinator) {
703 case COMB_WS: /*descendant selector */
706 enum CRStatus status = CR_OK;
707 gboolean matches = FALSE;
710 *walk the xml tree upward looking for a parent
711 *node that matches the preceding selector.
713 for (n = cur_node->parent; n; n = n->parent) {
714 status = sel_matches_node_real
715 (a_this, cur_sel->prev,
716 n, &matches, FALSE, TRUE);
721 if (matches == TRUE) {
729 *didn't find any ancestor that matches
730 *the previous simple selector.
735 *in this case, the preceding simple sel
736 *will have been interpreted twice, which
737 *is a cpu and mem waste ... I need to find
738 *another way to do this. Anyway, this is
739 *my first attempt to write this function and
740 *I am a bit clueless.
746 cur_node = get_prev_element_node (cur_node);
752 cur_node = get_next_parent_element_node (cur_node);
764 *if we reached this point, it means the selector matches
775 *Returns array of the ruleset statements that matches the
777 *The engine keeps in memory the last statement he
778 *visited during the match. So, the next call
779 *to this function will eventually return a rulesets list starting
780 *from the last ruleset statement visited during the previous call.
781 *The enable users to get matching rulesets in an incremental way.
782 *Note that for each statement returned,
783 *the engine calculates the specificity of the selector
784 *that matched the xml node and stores it in the "specifity" field
785 *of the statement structure.
787 *@param a_sel_eng the current selection engine
788 *@param a_node the xml node for which the request
790 *@param a_sel_list the list of selectors to perform the search in.
791 *@param a_rulesets in/out parameter. A pointer to the
792 *returned array of rulesets statements that match the xml node
793 *given in parameter. The caller allocates the array before calling this
795 *@param a_len in/out parameter the length (in sizeof (#CRStatement*))
796 *of the returned array.
797 *(the length of a_rulesets, more precisely).
798 *The caller must set it to the length of a_ruleset prior to calling this
799 *function. In return, the function sets it to the length
800 *(in sizeof (#CRStatement)) of the actually returned CRStatement array.
801 *@return CR_OUTPUT_TOO_SHORT_ERROR if found more rulesets than the size
802 *of the a_rulesets array. In this case, the first *a_len rulesets found
803 *are put in a_rulesets, and a further call will return the following
804 *ruleset(s) following the same principle.
805 *@return CR_OK if all the rulesets found have been returned. In this
806 *case, *a_len is set to the actual number of ruleset found.
807 *@return CR_BAD_PARAM_ERROR in case any of the given parameter are
808 *bad (e.g null pointer).
809 *@return CR_ERROR if any other error occured.
812 cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
813 CRStyleSheet * a_stylesheet,
815 CRStatement ** a_rulesets,
818 CRStatement *cur_stmt = NULL;
819 CRSelector *sel_list = NULL,
821 gboolean matches = FALSE;
822 enum CRStatus status = CR_OK;
825 g_return_val_if_fail (a_this
827 && a_node && a_rulesets, CR_BAD_PARAM_ERROR);
829 if (!a_stylesheet->statements) {
836 *if this stylesheet is "new one"
837 *let's remember it for subsequent calls.
839 if (PRIVATE (a_this)->sheet != a_stylesheet) {
840 PRIVATE (a_this)->sheet = a_stylesheet;
841 PRIVATE (a_this)->cur_stmt = a_stylesheet->statements;
845 *walk through the list of statements and,
846 *get the selectors list inside the statements that
847 *contain some, and try to match our xml node in these
850 for (cur_stmt = PRIVATE (a_this)->cur_stmt, i = 0;
851 (PRIVATE (a_this)->cur_stmt = cur_stmt);
852 cur_stmt = cur_stmt->next) {
854 *initialyze the selector list in which we will
855 *really perform the search.
860 *get the the damn selector list in
861 *which we have to look
863 switch (cur_stmt->type) {
865 if (cur_stmt->kind.ruleset
866 && cur_stmt->kind.ruleset->sel_list) {
867 sel_list = cur_stmt->kind.ruleset->sel_list;
871 case AT_MEDIA_RULE_STMT:
872 if (cur_stmt->kind.media_rule
873 && cur_stmt->kind.media_rule->rulesets
874 && cur_stmt->kind.media_rule->rulesets->
876 && cur_stmt->kind.media_rule->rulesets->
877 kind.ruleset->sel_list) {
879 cur_stmt->kind.media_rule->
880 rulesets->kind.ruleset->sel_list;
884 case AT_IMPORT_RULE_STMT:
886 *some recursivity may be needed here.
887 *I don't like this :(
898 *now, we have a comma separated selector list to look in.
899 *let's walk it and try to match the xml_node
900 *on each item of the list.
902 for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next) {
903 if (!cur_sel->simple_sel)
906 status = cr_sel_eng_matches_node
907 (a_this, cur_sel->simple_sel,
910 if (status == CR_OK && matches == TRUE) {
912 *bingo!!! we found one ruleset that
913 *matches that fucking node.
914 *lets put it in the out array.
918 a_rulesets[i] = cur_stmt;
922 *For the cascade computing algorithm
923 *(which is gonna take place later)
924 *we must compute the specificity
925 *(css2 spec chap 6.4.1) of the selector
926 *that matched the current xml node
927 *and store it in the css2 statement
928 *(statement == ruleset here).
930 status = cr_simple_sel_compute_specificity (cur_sel->simple_sel);
932 g_return_val_if_fail (status == CR_OK,
934 cur_stmt->specificity =
935 cur_sel->simple_sel->
940 return CR_OUTPUT_TOO_SHORT_ERROR;
947 *if we reached this point, it means
948 *we reached the end of stylesheet.
949 *no need to store any info about the stylesheet
952 g_return_val_if_fail (!PRIVATE (a_this)->cur_stmt, CR_ERROR);
953 PRIVATE (a_this)->sheet = NULL;
959 put_css_properties_in_props_list (CRPropList ** a_props, CRStatement * a_stmt)
961 CRPropList *props = NULL,
964 CRDeclaration *cur_decl = NULL;
966 g_return_val_if_fail (a_props && a_stmt
967 && a_stmt->type == RULESET_STMT
968 && a_stmt->kind.ruleset, CR_BAD_PARAM_ERROR);
972 for (cur_decl = a_stmt->kind.ruleset->decl_list;
973 cur_decl; cur_decl = cur_decl->next) {
979 if (!cur_decl->property
980 || !cur_decl->property->stryng
981 || !cur_decl->property->stryng->str)
984 *First, test if the property is not
985 *already present in our properties list
986 *If yes, apply the cascading rules to
987 *compute the precedence. If not, insert
988 *the property into the list
990 cr_prop_list_lookup_prop (props,
995 tmp_props = cr_prop_list_append2
996 (props, cur_decl->property, cur_decl);
1005 *A property with the same name already exists.
1007 *some cascading rules
1008 *to compute the precedence.
1010 cr_prop_list_get_decl (pair, &decl);
1011 g_return_val_if_fail (decl, CR_ERROR);
1014 *first, look at the origin.
1016 *"for normal declarations,
1017 *author style sheets override user
1018 *style sheets which override
1019 *the default style sheet."
1021 if (decl->parent_statement
1022 && decl->parent_statement->parent_sheet
1023 && (decl->parent_statement->parent_sheet->origin
1024 < a_stmt->parent_sheet->origin)) {
1026 *if the already selected declaration
1027 *is marked as being !important the current
1028 *declaration must not overide it
1029 *(unless the already selected declaration
1032 if (decl->important == TRUE
1033 && decl->parent_statement->parent_sheet->origin
1037 tmp_props = cr_prop_list_unlink (props, pair);
1039 cr_prop_list_destroy (pair);
1043 props = cr_prop_list_append2
1044 (props, cur_decl->property, cur_decl);
1047 } else if (decl->parent_statement
1048 && decl->parent_statement->parent_sheet
1049 && (decl->parent_statement->
1050 parent_sheet->origin
1051 > a_stmt->parent_sheet->origin)) {
1053 ("We should not reach this line\n");
1058 *A property with the same
1059 *name and the same origin already exists.
1060 *shit. This is lasting longer than expected ...
1061 *Luckily, the spec says in 6.4.1:
1062 *"more specific selectors will override
1065 *"if two rules have the same weight,
1066 *origin and specificity,
1067 *the later specified wins"
1069 if (a_stmt->specificity
1070 >= decl->parent_statement->specificity) {
1071 if (decl->important == TRUE)
1073 props = cr_prop_list_unlink (props, pair);
1075 cr_prop_list_destroy (pair);
1078 props = cr_prop_list_append2 (props,
1083 /*TODO: this may leak. Check this out */
1090 set_style_from_props (CRStyle * a_style, CRPropList * a_props)
1092 CRPropList *cur = NULL;
1093 CRDeclaration *decl = NULL;
1095 for (cur = a_props; cur; cur = cr_prop_list_get_next (cur)) {
1096 cr_prop_list_get_decl (cur, &decl);
1097 cr_style_set_style_from_decl (a_style, decl);
1102 /****************************************
1104 ****************************************/
1108 *Creates a new instance of #CRSelEng.
1110 *Returns the newly built instance of #CRSelEng of
1111 *NULL if an error occurs.
1114 cr_sel_eng_new (void)
1116 CRSelEng *result = NULL;
1118 result = g_try_malloc (sizeof (CRSelEng));
1120 cr_utils_trace_info ("Out of memory");
1123 memset (result, 0, sizeof (CRSelEng));
1125 PRIVATE (result) = g_try_malloc (sizeof (CRSelEngPriv));
1126 if (!PRIVATE (result)) {
1127 cr_utils_trace_info ("Out of memory");
1131 memset (PRIVATE (result), 0, sizeof (CRSelEngPriv));
1132 cr_sel_eng_register_pseudo_class_sel_handler
1133 (result, (guchar *) "first-child",
1134 IDENT_PSEUDO, (CRPseudoClassSelectorHandler)
1135 first_child_pseudo_class_handler);
1136 cr_sel_eng_register_pseudo_class_sel_handler
1137 (result, (guchar *) "lang",
1138 FUNCTION_PSEUDO, (CRPseudoClassSelectorHandler)
1139 lang_pseudo_class_handler);
1145 * cr_sel_eng_register_pseudo_class_sel_handler:
1146 *@a_this: the current instance of #CRSelEng
1147 *@a_pseudo_class_sel_name: the name of the pseudo class selector.
1148 *@a_pseudo_class_type: the type of the pseudo class selector.
1149 *@a_handler: the actual handler or callback to be called during
1150 *the selector evaluation process.
1152 *Adds a new handler entry in the handlers entry table.
1154 *Returns CR_OK, upon successful completion, an error code otherwise.
1157 cr_sel_eng_register_pseudo_class_sel_handler (CRSelEng * a_this,
1159 enum CRPseudoType a_type,
1160 CRPseudoClassSelectorHandler
1163 struct CRPseudoClassSelHandlerEntry *handler_entry = NULL;
1166 g_return_val_if_fail (a_this && PRIVATE (a_this)
1167 && a_handler && a_name, CR_BAD_PARAM_ERROR);
1169 handler_entry = g_try_malloc
1170 (sizeof (struct CRPseudoClassSelHandlerEntry));
1171 if (!handler_entry) {
1172 return CR_OUT_OF_MEMORY_ERROR;
1174 memset (handler_entry, 0,
1175 sizeof (struct CRPseudoClassSelHandlerEntry));
1176 handler_entry->name = g_strdup (a_name);
1177 handler_entry->type = a_type;
1178 handler_entry->handler = a_handler;
1179 list = g_list_append (PRIVATE (a_this)->pcs_handlers, handler_entry);
1181 return CR_OUT_OF_MEMORY_ERROR;
1183 PRIVATE (a_this)->pcs_handlers = list;
1188 cr_sel_eng_unregister_pseudo_class_sel_handler (CRSelEng * a_this,
1190 enum CRPseudoType a_type)
1193 *deleted_elem = NULL;
1194 gboolean found = FALSE;
1195 struct CRPseudoClassSelHandlerEntry *entry = NULL;
1197 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1199 for (elem = PRIVATE (a_this)->pcs_handlers;
1200 elem; elem = g_list_next (elem)) {
1202 if (!strcmp (entry->name, a_name)
1203 && entry->type == a_type) {
1209 return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
1210 PRIVATE (a_this)->pcs_handlers = g_list_delete_link
1211 (PRIVATE (a_this)->pcs_handlers, elem);
1214 g_free (entry->name);
1216 g_list_free (deleted_elem);
1222 * cr_sel_eng_unregister_all_pseudo_class_sel_handlers:
1223 *@a_this: the current instance of #CRSelEng .
1225 *Unregisters all the pseudo class sel handlers
1226 *and frees all the associated allocated datastructures.
1228 *Returns CR_OK upon succesful completion, an error code
1232 cr_sel_eng_unregister_all_pseudo_class_sel_handlers (CRSelEng * a_this)
1235 struct CRPseudoClassSelHandlerEntry *entry = NULL;
1237 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1239 if (!PRIVATE (a_this)->pcs_handlers)
1241 for (elem = PRIVATE (a_this)->pcs_handlers;
1242 elem; elem = g_list_next (elem)) {
1247 g_free (entry->name);
1253 g_list_free (PRIVATE (a_this)->pcs_handlers);
1254 PRIVATE (a_this)->pcs_handlers = NULL;
1259 cr_sel_eng_get_pseudo_class_selector_handler (CRSelEng * a_this,
1261 enum CRPseudoType a_type,
1262 CRPseudoClassSelectorHandler *
1266 struct CRPseudoClassSelHandlerEntry *entry = NULL;
1267 gboolean found = FALSE;
1269 g_return_val_if_fail (a_this && PRIVATE (a_this)
1270 && a_name, CR_BAD_PARAM_ERROR);
1272 for (elem = PRIVATE (a_this)->pcs_handlers;
1273 elem; elem = g_list_next (elem)) {
1275 if (!strcmp (a_name, entry->name)
1276 && entry->type == a_type) {
1283 return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
1284 *a_handler = entry->handler;
1289 * cr_sel_eng_matches_node:
1290 *@a_this: the selection engine.
1291 *@a_sel: the simple selector against which the xml node
1292 *is going to be matched.
1293 *@a_node: the node against which the selector is going to be matched.
1294 *@a_result: out parameter. The result of the match. Is set to
1295 *TRUE if the selector matches the node, FALSE otherwise. This value
1296 *is considered if and only if this functions returns CR_OK.
1298 *Evaluates a chained list of simple selectors (known as a css2 selector).
1299 *Says wheter if this selector matches the xml node given in parameter or
1302 *Returns the CR_OK if the selection ran correctly, an error code otherwise.
1305 cr_sel_eng_matches_node (CRSelEng * a_this, CRSimpleSel * a_sel,
1306 xmlNode * a_node, gboolean * a_result)
1308 g_return_val_if_fail (a_this && PRIVATE (a_this)
1310 && a_result, CR_BAD_PARAM_ERROR);
1312 if (a_node->type != XML_ELEMENT_NODE) {
1317 return sel_matches_node_real (a_this, a_sel,
1323 * cr_sel_eng_get_matched_rulesets:
1324 *@a_this: the current instance of the selection engine.
1325 *@a_sheet: the stylesheet that holds the selectors.
1326 *@a_node: the xml node to consider during the walk thru
1328 *@a_rulesets: out parameter. A pointer to an array of
1329 *rulesets statement pointers. *a_rulesets is allocated by
1330 *this function and must be freed by the caller. However, the caller
1331 *must not alter the rulesets statements pointer because they
1332 *point to statements that are still in the css stylesheet.
1333 *@a_len: the length of *a_ruleset.
1335 *Returns an array of pointers to selectors that matches
1336 *the xml node given in parameter.
1338 *Returns CR_OK upon sucessfull completion, an error code otherwise.
1341 cr_sel_eng_get_matched_rulesets (CRSelEng * a_this,
1342 CRStyleSheet * a_sheet,
1344 CRStatement *** a_rulesets, gulong * a_len)
1346 CRStatement **stmts_tab = NULL;
1347 enum CRStatus status = CR_OK;
1348 gulong tab_size = 0,
1351 gushort stmts_chunck_size = 8;
1353 g_return_val_if_fail (a_this
1356 && a_rulesets && *a_rulesets == NULL
1357 && a_len, CR_BAD_PARAM_ERROR);
1359 stmts_tab = g_try_malloc (stmts_chunck_size * sizeof (CRStatement *));
1362 cr_utils_trace_info ("Out of memory");
1366 memset (stmts_tab, 0, stmts_chunck_size * sizeof (CRStatement *));
1368 tab_size = stmts_chunck_size;
1371 while ((status = cr_sel_eng_get_matched_rulesets_real
1372 (a_this, a_sheet, a_node, stmts_tab + index, &tab_len))
1373 == CR_OUTPUT_TOO_SHORT_ERROR) {
1374 stmts_tab = g_try_realloc (stmts_tab,
1375 (tab_size + stmts_chunck_size)
1376 * sizeof (CRStatement *));
1378 cr_utils_trace_info ("Out of memory");
1382 tab_size += stmts_chunck_size;
1384 tab_len = tab_size - index;
1387 tab_len = tab_size - stmts_chunck_size + tab_len;
1388 *a_rulesets = stmts_tab;
1407 cr_sel_eng_get_matched_properties_from_cascade (CRSelEng * a_this,
1408 CRCascade * a_cascade,
1410 CRPropList ** a_props)
1412 CRStatement **stmts_tab = NULL;
1413 enum CRStatus status = CR_OK;
1414 gulong tab_size = 0,
1418 enum CRStyleOrigin origin = 0;
1419 gushort stmts_chunck_size = 8;
1420 CRStyleSheet *sheet = NULL;
1422 g_return_val_if_fail (a_this
1424 && a_node && a_props, CR_BAD_PARAM_ERROR);
1426 for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++) {
1427 sheet = cr_cascade_get_sheet (a_cascade, origin);
1430 if (tab_size - index < 1) {
1431 stmts_tab = g_try_realloc
1432 (stmts_tab, (tab_size + stmts_chunck_size)
1433 * sizeof (CRStatement *));
1435 cr_utils_trace_info ("Out of memory");
1439 tab_size += stmts_chunck_size;
1441 *compute the max size left for
1442 *cr_sel_eng_get_matched_rulesets_real()'s output tab
1444 tab_len = tab_size - index;
1446 while ((status = cr_sel_eng_get_matched_rulesets_real
1447 (a_this, sheet, a_node, stmts_tab + index, &tab_len))
1448 == CR_OUTPUT_TOO_SHORT_ERROR) {
1449 stmts_tab = g_try_realloc
1450 (stmts_tab, (tab_size + stmts_chunck_size)
1451 * sizeof (CRStatement *));
1453 cr_utils_trace_info ("Out of memory");
1457 tab_size += stmts_chunck_size;
1460 *compute the max size left for
1461 *cr_sel_eng_get_matched_rulesets_real()'s output tab
1463 tab_len = tab_size - index;
1465 if (status != CR_OK) {
1466 cr_utils_trace_info ("Error while running "
1471 tab_len = tab_size - index;
1475 *TODO, walk down the stmts_tab and build the
1476 *property_name/declaration hashtable.
1477 *Make sure one can walk from the declaration to
1480 for (i = 0; i < index; i++) {
1481 CRStatement *stmt = stmts_tab[i];
1485 switch (stmt->type) {
1487 if (!stmt->parent_sheet)
1489 status = put_css_properties_in_props_list
1508 cr_sel_eng_get_matched_style (CRSelEng * a_this,
1509 CRCascade * a_cascade,
1511 CRStyle * a_parent_style,
1513 gboolean a_set_props_to_initial_values)
1515 enum CRStatus status = CR_OK;
1517 CRPropList *props = NULL;
1519 g_return_val_if_fail (a_this && a_cascade
1520 && a_node && a_style, CR_BAD_PARAM_ERROR);
1522 status = cr_sel_eng_get_matched_properties_from_cascade
1523 (a_this, a_cascade, a_node, &props);
1525 g_return_val_if_fail (status == CR_OK, status);
1528 *a_style = cr_style_new (a_set_props_to_initial_values) ;
1529 g_return_val_if_fail (*a_style, CR_ERROR);
1531 if (a_set_props_to_initial_values == TRUE) {
1532 cr_style_set_props_to_initial_values (*a_style) ;
1534 cr_style_set_props_to_default_values (*a_style);
1537 (*a_style)->parent_style = a_parent_style;
1539 set_style_from_props (*a_style, props);
1541 cr_prop_list_destroy (props);
1549 * cr_sel_eng_destroy:
1550 *@a_this: the current instance of the selection engine.
1552 *The destructor of #CRSelEng
1555 cr_sel_eng_destroy (CRSelEng * a_this)
1557 g_return_if_fail (a_this);
1559 if (!PRIVATE (a_this))
1561 if (PRIVATE (a_this)->pcs_handlers) {
1562 cr_sel_eng_unregister_all_pseudo_class_sel_handlers
1564 PRIVATE (a_this)->pcs_handlers = NULL ;
1566 g_free (PRIVATE (a_this));
1567 PRIVATE (a_this) = NULL;