1 /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
3 /*
4 * This file is part of The Croco Library
5 *
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.
9 *
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.
14 *
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
19 * USA
20 *
21 * See COPYRIGHTS file for copyright informations.
22 */
24 #include <string.h>
25 #include "cr-sel-eng.h"
26 #include "cr-node-iface.h"
28 /**
29 *@file:
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.
34 */
36 #define PRIVATE(a_this) (a_this)->priv
38 struct CRPseudoClassSelHandlerEntry {
39 char *name;
40 enum CRPseudoType type;
41 CRPseudoClassSelectorHandler handler;
42 };
44 struct _CRSelEngPriv {
45 /*not used yet */
46 gboolean case_sensitive;
48 CRNodeIface const *node_iface;
49 CRStyleSheet *sheet;
50 /**
51 *where to store the next statement
52 *to be visited so that we can remember
53 *it from one method call to another.
54 */
55 CRStatement *cur_stmt;
56 GList *pcs_handlers;
57 gint pcs_handlers_size;
58 } ;
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,
70 CRSimpleSel * a_sel,
71 CRXMLNodePtr a_node,
72 gboolean * a_result,
73 gboolean a_eval_sel_list_from_end,
74 gboolean a_recurse);
76 static enum CRStatus cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
77 CRStyleSheet *
78 a_stylesheet,
79 CRXMLNodePtr a_node,
80 CRStatement **
81 a_rulesets,
82 gulong * a_len);
84 static enum CRStatus put_css_properties_in_props_list (CRPropList ** a_props,
85 CRStatement *
86 a_ruleset);
88 static gboolean pseudo_class_add_sel_matches_node (CRSelEng * a_this,
89 CRAdditionalSel *
90 a_add_sel,
91 CRXMLNodePtr a_node);
93 static gboolean lang_pseudo_class_handler (CRSelEng * a_this,
94 CRAdditionalSel * a_sel,
95 CRXMLNodePtr a_node);
97 static gboolean first_child_pseudo_class_handler (CRSelEng * a_this,
98 CRAdditionalSel * a_sel,
99 CRXMLNodePtr a_node);
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);
109 void
110 cr_sel_eng_set_node_iface (CRSelEng *const a_this, CRNodeIface const *const a_node_iface)
111 {
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;
115 }
117 static gboolean
118 lang_pseudo_class_handler (CRSelEng *const a_this,
119 CRAdditionalSel * a_sel, CRXMLNodePtr a_node)
120 {
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
130 && a_node, FALSE);
132 node_iface = PRIVATE(a_this)->node_iface;
134 if (strncmp (a_sel->content.pseudo->name->stryng->str,
135 "lang", 4)
136 || !a_sel->content.pseudo->type == FUNCTION_PSEUDO) {
137 cr_utils_trace_info ("This handler is for :lang only");
138 return FALSE;
139 }
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)
144 return FALSE;
145 for (; node; node = get_next_parent_element_node (node_iface, node)) {
146 char *val = node_iface->getProp (node, "lang");
147 if (val) {
148 if (!strncmp (val,
149 a_sel->content.pseudo->extra->stryng->str,
150 a_sel->content.pseudo->extra->stryng->len)) {
151 result = TRUE;
152 break;
153 }
154 node_iface->freePropVal (val);
155 val = NULL;
156 }
157 }
159 return result;
160 }
162 static gboolean
163 first_child_pseudo_class_handler (CRSelEng *const a_this,
164 CRAdditionalSel * a_sel, CRXMLNodePtr const a_node)
165 {
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
174 && a_node, FALSE);
176 if (strcmp (a_sel->content.pseudo->name->stryng->str,
177 "first-child")
178 || !a_sel->content.pseudo->type == IDENT_PSEUDO) {
179 cr_utils_trace_info ("This handler is for :first-child only");
180 return FALSE;
181 }
182 node_iface = PRIVATE(a_this)->node_iface;
183 parent = node_iface->getParentNode (a_node);
184 if (!parent)
185 return FALSE;
186 node = get_next_child_element_node (node_iface, parent);
187 return (node == a_node);
188 }
190 static gboolean
191 pseudo_class_add_sel_matches_node (CRSelEng * a_this,
192 CRAdditionalSel * a_add_sel,
193 CRXMLNodePtr a_node)
194 {
195 enum CRStatus status = CR_OK;
196 CRPseudoClassSelectorHandler handler = NULL;
198 g_return_val_if_fail (a_this && PRIVATE (a_this)
199 && a_add_sel
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
204 && a_node, FALSE);
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)
210 return FALSE;
212 return handler (a_this, a_add_sel, a_node);
213 }
215 /**
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.
220 */
221 static gboolean
222 class_add_sel_matches_node (CRAdditionalSel * a_add_sel,
223 CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
224 {
225 gboolean result = FALSE;
226 char *klass = NULL;
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
233 && a_node, FALSE);
235 klass = a_node_iface->getProp (a_node, "class");
236 if (klass) {
237 char const *cur;
238 for (cur = klass; cur && *cur; cur++) {
239 while (cur && *cur
240 && cr_utils_is_white_space (*cur)
241 == TRUE)
242 cur++;
244 if (!strncmp (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;
248 if ((cur && !*cur)
249 || cr_utils_is_white_space (*cur) == TRUE)
250 result = TRUE;
251 }
252 if (cur && !*cur)
253 break ;
254 }
255 a_node_iface->freePropVal (klass);
256 klass = NULL;
257 }
258 return result;
260 }
262 /**
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.
267 */
268 static gboolean
269 id_add_sel_matches_node (CRAdditionalSel * a_add_sel,
270 CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
271 {
272 gboolean result = FALSE;
273 char *id = NULL;
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
280 && a_node, FALSE);
281 g_return_val_if_fail (a_add_sel
282 && a_add_sel->type == ID_ADD_SELECTOR
283 && a_node, FALSE);
285 id = a_node_iface->getProp (a_node, "id");
286 if (id) {
287 if (!strncmp (id, a_add_sel->content.id_name->stryng->str,
288 a_add_sel->content.id_name->stryng->len)) {
289 result = TRUE;
290 }
291 a_node_iface->freePropVal (id);
292 id = NULL;
293 }
294 return result;
295 }
297 /**
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
302 *be evaluated
303 *return TRUE if the additional selector matches the current xml node
304 *FALSE otherwise.
305 */
306 static gboolean
307 attr_add_sel_matches_node (CRAdditionalSel * a_add_sel,
308 CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
309 {
310 CRAttrSel *cur_sel = NULL;
312 g_return_val_if_fail (a_add_sel
313 && a_add_sel->type == ATTRIBUTE_ADD_SELECTOR
314 && a_node, FALSE);
316 for (cur_sel = a_add_sel->content.attr_sel;
317 cur_sel; cur_sel = cur_sel->next) {
318 if (!cur_sel->name
319 || !cur_sel->name->stryng
320 || !cur_sel->name->stryng->str)
321 return FALSE;
323 char *const value = a_node_iface->getProp (a_node, cur_sel->name->stryng->str);
324 if (!value)
325 goto free_and_return_false;
327 switch (cur_sel->match_way) {
328 case SET:
329 break;
331 case EQUALS:
332 if (!cur_sel->value
333 || !cur_sel->value->stryng
334 || !cur_sel->value->stryng->str) {
335 goto free_and_return_false;
336 }
337 if (strcmp
338 (value,
339 cur_sel->value->stryng->str)) {
340 goto free_and_return_false;
341 }
342 break;
344 case INCLUDES:
345 {
346 char const *ptr1 = NULL,
347 *ptr2 = NULL,
348 *cur = NULL;
349 gboolean found = FALSE;
351 /*
352 *here, make sure value is a space
353 *separated list of "words", where one
354 *value is exactly cur_sel->value->str
355 */
356 for (cur = value; *cur; cur++) {
357 /*
358 *set ptr1 to the first non white space
359 *char addr.
360 */
361 while (cr_utils_is_white_space (*cur)
362 && *cur)
363 cur++;
364 if (!*cur)
365 break;
366 ptr1 = cur;
368 /*
369 *set ptr2 to the end the word.
370 */
371 while (!cr_utils_is_white_space (*cur)
372 && *cur)
373 cur++;
374 cur--;
375 ptr2 = cur;
377 if (!strncmp
378 (ptr1,
379 cur_sel->value->stryng->str,
380 ptr2 - ptr1 + 1)) {
381 found = TRUE;
382 break;
383 }
384 ptr1 = ptr2 = NULL;
385 }
387 if (!found) {
388 goto free_and_return_false;
389 }
390 }
391 break;
393 case DASHMATCH:
394 {
395 char const *ptr1 = NULL,
396 *ptr2 = NULL,
397 *cur = NULL;
398 gboolean found = FALSE;
400 /*
401 *here, make sure value is an hyphen
402 *separated list of "words", each of which
403 *starting with "cur_sel->value->str"
404 */
405 for (cur = value; *cur; cur++) {
406 if (*cur == '-')
407 cur++;
408 ptr1 = cur;
410 while (*cur != '-' && *cur)
411 cur++;
412 cur--;
413 ptr2 = cur;
415 if (g_strstr_len
416 (ptr1, ptr2 - ptr1 + 1,
417 cur_sel->value->stryng->str)
418 == ptr1) {
419 found = TRUE;
420 break;
421 }
422 }
424 if (!found) {
425 goto free_and_return_false;
426 }
427 }
428 break;
429 default:
430 goto free_and_return_false;
431 }
433 a_node_iface->freePropVal (value);
434 continue;
436 free_and_return_false:
437 a_node_iface->freePropVal (value);
438 return FALSE;
439 }
441 return TRUE;
442 }
444 /**
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.
449 */
450 static gboolean
451 additional_selector_matches_node (CRSelEng * a_this,
452 CRAdditionalSel * a_add_sel,
453 CRXMLNodePtr a_node)
454 {
455 CRAdditionalSel *cur_add_sel = NULL, *tail = NULL ;
456 gboolean evaluated = FALSE ;
458 for (tail = a_add_sel ;
459 tail && tail->next;
460 tail = tail->next) ;
462 g_return_val_if_fail (tail, FALSE) ;
464 for (cur_add_sel = tail ;
465 cur_add_sel ;
466 cur_add_sel = cur_add_sel->prev) {
468 evaluated = TRUE ;
469 if (cur_add_sel->type == NO_ADD_SELECTOR) {
470 return FALSE;
471 }
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,
479 a_node)) {
480 return FALSE;
481 }
482 continue ;
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,
489 a_node)) {
490 return FALSE;
491 }
492 continue ;
493 } else if (cur_add_sel->type == ATTRIBUTE_ADD_SELECTOR
494 && cur_add_sel->content.attr_sel) {
495 /*
496 *here, call a function that does the match
497 *against an attribute additionnal selector
498 *and an xml node.
499 */
500 if (!attr_add_sel_matches_node (cur_add_sel,
501 PRIVATE(a_this)->node_iface,
502 a_node)) {
503 return FALSE;
504 }
505 continue ;
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)) {
510 return TRUE;
511 }
512 return FALSE;
513 }
514 }
515 if (evaluated == TRUE)
516 return TRUE;
517 return FALSE ;
518 }
520 static CRXMLNodePtr
521 get_next_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
522 {
523 CRXMLNodePtr cur_node = a_node;
525 g_return_val_if_fail (a_node, NULL);
527 do {
528 cur_node = a_node_iface->getNextSibling (cur_node);
529 } while (cur_node && !a_node_iface->isElementNode(cur_node));
530 return cur_node;
531 }
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). */
536 static CRXMLNodePtr
537 get_next_child_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
538 {
539 CRXMLNodePtr cur_node = NULL;
541 g_return_val_if_fail (a_node, NULL);
543 cur_node = a_node_iface->getFirstChild (a_node);
544 if (!cur_node)
545 return cur_node;
546 if (a_node_iface->isElementNode (cur_node))
547 return cur_node;
548 return get_next_element_node (a_node_iface, cur_node);
549 }
551 static CRXMLNodePtr
552 get_prev_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
553 {
554 CRXMLNodePtr cur_node = a_node;
556 g_return_val_if_fail (a_node, NULL);
558 do {
559 cur_node = a_node_iface->getPrevSibling (cur_node);
560 } while (cur_node && !a_node_iface->isElementNode(cur_node));
561 return cur_node;
562 }
564 static CRXMLNodePtr
565 get_next_parent_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node)
566 {
567 CRXMLNodePtr cur_node = a_node;
569 g_return_val_if_fail (a_node, NULL);
571 do {
572 cur_node = a_node_iface->getParentNode (cur_node);
573 } while (cur_node && !a_node_iface->isElementNode (cur_node));
574 return cur_node;
575 }
577 /**
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.
584 *
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.
594 */
595 static enum CRStatus
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,
599 gboolean a_recurse)
600 {
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)
606 && a_this && a_node
607 && a_result, CR_BAD_PARAM_ERROR);
609 node_iface = PRIVATE(a_this)->node_iface;
610 *a_result = FALSE;
612 if (!node_iface->isElementNode(a_node))
613 return CR_OK;
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) ;
619 } else {
620 cur_sel = a_sel;
621 }
623 for (cur_node = a_node; cur_sel; cur_sel = cur_sel->prev) {
624 if (((cur_sel->type_mask & TYPE_SELECTOR)
625 && (cur_sel->name
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)) {
631 /*
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.
637 */
638 if (cur_sel->add_sel) {
639 if (additional_selector_matches_node (a_this, cur_sel->add_sel,
640 cur_node) == TRUE) {
641 goto walk_a_step_in_expr;
642 } else {
643 goto done;
644 }
645 } else {
646 goto walk_a_step_in_expr;
647 }
648 }
649 if (!(cur_sel->type_mask & TYPE_SELECTOR)
650 && !(cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
651 if (!cur_sel->add_sel) {
652 goto done;
653 }
654 if (additional_selector_matches_node
655 (a_this, cur_sel->add_sel, cur_node)
656 == TRUE) {
657 goto walk_a_step_in_expr;
658 } else {
659 goto done;
660 }
661 } else {
662 goto done ;
663 }
665 walk_a_step_in_expr:
666 if (a_recurse == FALSE) {
667 *a_result = TRUE;
668 goto done;
669 }
671 /*
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.
675 */
676 if (!cur_sel->prev)
677 break;
679 switch (cur_sel->combinator) {
680 case NO_COMBINATOR:
681 break;
683 case COMB_WS: /*descendant selector */
684 {
685 CRXMLNodePtr n = NULL;
686 enum CRStatus status = CR_OK;
687 gboolean matches = FALSE;
689 /*
690 *walk the xml tree upward looking for a parent
691 *node that matches the preceding selector.
692 */
693 for (n = node_iface->getParentNode (cur_node);
694 n;
695 n = node_iface->getParentNode (n)) {
696 status = sel_matches_node_real
697 (a_this, cur_sel->prev,
698 n, &matches, FALSE, TRUE);
700 if (status != CR_OK)
701 goto done;
703 if (matches == TRUE) {
704 cur_node = n ;
705 break;
706 }
707 }
709 if (!n) {
710 /*
711 *didn't find any ancestor that matches
712 *the previous simple selector.
713 */
714 goto done;
715 }
716 /*
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.
723 */
724 break;
725 }
727 case COMB_PLUS:
728 cur_node = get_prev_element_node (node_iface, cur_node);
729 if (!cur_node)
730 goto done;
731 break;
733 case COMB_GT:
734 cur_node = get_next_parent_element_node (node_iface, cur_node);
735 if (!cur_node)
736 goto done;
737 break;
739 default:
740 goto done;
741 }
742 continue;
743 }
745 /*
746 *if we reached this point, it means the selector matches
747 *the xml node.
748 */
749 *a_result = TRUE;
751 done:
752 return CR_OK;
753 }
756 /**
757 *Returns array of the ruleset statements that matches the
758 *given xml node.
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.
768 *
769 *@param a_sel_eng the current selection engine
770 *@param a_node the xml node for which the request
771 *is being made.
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
776 *function.
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.
792 */
793 static enum CRStatus
794 cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
795 CRStyleSheet * a_stylesheet,
796 CRXMLNodePtr a_node,
797 CRStatement ** a_rulesets,
798 gulong * a_len)
799 {
800 CRStatement *cur_stmt = NULL;
801 CRSelector *sel_list = NULL,
802 *cur_sel = NULL;
803 gboolean matches = FALSE;
804 enum CRStatus status = CR_OK;
805 gulong i = 0;
807 g_return_val_if_fail (a_this
808 && a_stylesheet
809 && a_node && a_rulesets, CR_BAD_PARAM_ERROR);
811 if (!a_stylesheet->statements) {
812 *a_rulesets = NULL;
813 *a_len = 0;
814 return CR_OK;
815 }
817 /*
818 *if this stylesheet is "new one"
819 *let's remember it for subsequent calls.
820 */
821 if (PRIVATE (a_this)->sheet != a_stylesheet) {
822 PRIVATE (a_this)->sheet = a_stylesheet;
823 PRIVATE (a_this)->cur_stmt = a_stylesheet->statements;
824 }
826 /*
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
830 *selectors lists.
831 */
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) {
835 /*
836 *initialyze the selector list in which we will
837 *really perform the search.
838 */
839 sel_list = NULL;
841 /*
842 *get the the damn selector list in
843 *which we have to look
844 */
845 switch (cur_stmt->type) {
846 case RULESET_STMT:
847 if (cur_stmt->kind.ruleset
848 && cur_stmt->kind.ruleset->sel_list) {
849 sel_list = cur_stmt->kind.ruleset->sel_list;
850 }
851 break;
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->
857 kind.ruleset
858 && cur_stmt->kind.media_rule->rulesets->
859 kind.ruleset->sel_list) {
860 sel_list =
861 cur_stmt->kind.media_rule->
862 rulesets->kind.ruleset->sel_list;
863 }
864 break;
866 case AT_IMPORT_RULE_STMT:
867 /*
868 *some recursivity may be needed here.
869 *I don't like this :(
870 */
871 break;
872 default:
873 break;
874 }
876 if (!sel_list)
877 continue;
879 /*
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.
883 */
884 for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next) {
885 if (!cur_sel->simple_sel)
886 continue;
888 status = cr_sel_eng_matches_node
889 (a_this, cur_sel->simple_sel,
890 a_node, &matches);
892 if (status == CR_OK && matches == TRUE) {
893 /*
894 *bingo!!! we found one ruleset that
895 *matches that fucking node.
896 *lets put it in the out array.
897 */
899 if (i < *a_len) {
900 a_rulesets[i] = cur_stmt;
901 i++;
903 /*
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).
911 */
912 status = cr_simple_sel_compute_specificity (cur_sel->simple_sel);
914 g_return_val_if_fail (status == CR_OK,
915 CR_ERROR);
916 cur_stmt->specificity =
917 cur_sel->simple_sel->
918 specificity;
919 } else
920 {
921 *a_len = i;
922 return CR_OUTPUT_TOO_SHORT_ERROR;
923 }
924 }
925 }
926 }
928 /*
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
932 *anymore.
933 */
934 g_return_val_if_fail (!PRIVATE (a_this)->cur_stmt, CR_ERROR);
935 PRIVATE (a_this)->sheet = NULL;
936 *a_len = i;
937 return CR_OK;
938 }
940 static enum CRStatus
941 put_css_properties_in_props_list (CRPropList ** a_props, CRStatement * a_stmt)
942 {
943 CRPropList *props = NULL,
944 *pair = NULL,
945 *tmp_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);
952 props = *a_props;
954 for (cur_decl = a_stmt->kind.ruleset->decl_list;
955 cur_decl; cur_decl = cur_decl->next) {
956 CRDeclaration *decl;
958 decl = NULL;
959 pair = NULL;
961 if (!cur_decl->property
962 || !cur_decl->property->stryng
963 || !cur_decl->property->stryng->str)
964 continue;
965 /*
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
971 */
972 cr_prop_list_lookup_prop (props,
973 cur_decl->property,
974 &pair);
976 if (!pair) {
977 tmp_props = cr_prop_list_append2
978 (props, cur_decl->property, cur_decl);
979 if (tmp_props) {
980 props = tmp_props;
981 tmp_props = NULL;
982 }
983 continue;
984 }
986 /*
987 *A property with the same name already exists.
988 *We must apply here
989 *some cascading rules
990 *to compute the precedence.
991 */
992 cr_prop_list_get_decl (pair, &decl);
993 g_return_val_if_fail (decl, CR_ERROR);
995 /*
996 *first, look at the origin.
997 *6.4.1 says:
998 *"for normal declarations,
999 *author style sheets override user
1000 *style sheets which override
1001 *the default style sheet."
1002 */
1003 if (decl->parent_statement
1004 && decl->parent_statement->parent_sheet
1005 && (decl->parent_statement->parent_sheet->origin
1006 < a_stmt->parent_sheet->origin)) {
1007 /*
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
1012 *has an UA origin)
1013 */
1014 if (decl->important == TRUE
1015 && decl->parent_statement->parent_sheet->origin
1016 != ORIGIN_UA) {
1017 continue;
1018 }
1019 tmp_props = cr_prop_list_unlink (props, pair);
1020 if (props) {
1021 cr_prop_list_destroy (pair);
1022 }
1023 props = tmp_props;
1024 tmp_props = NULL;
1025 props = cr_prop_list_append2
1026 (props, cur_decl->property, cur_decl);
1028 continue;
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)) {
1034 cr_utils_trace_info
1035 ("We should not reach this line\n");
1036 continue;
1037 }
1039 /*
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
1045 *more general ones"
1046 *and
1047 *"if two rules have the same weight,
1048 *origin and specificity,
1049 *the later specified wins"
1050 */
1051 if (a_stmt->specificity
1052 >= decl->parent_statement->specificity) {
1053 if (decl->important == TRUE)
1054 continue;
1055 props = cr_prop_list_unlink (props, pair);
1056 if (pair) {
1057 cr_prop_list_destroy (pair);
1058 pair = NULL;
1059 }
1060 props = cr_prop_list_append2 (props,
1061 cur_decl->property,
1062 cur_decl);
1063 }
1064 }
1065 /*TODO: this may leak. Check this out */
1066 *a_props = props;
1068 return CR_OK;
1069 }
1071 static void
1072 set_style_from_props (CRStyle * a_style, CRPropList * a_props)
1073 {
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);
1080 decl = NULL;
1081 }
1082 }
1084 /****************************************
1085 *PUBLIC METHODS
1086 ****************************************/
1088 /**
1089 *Creates a new instance of #CRSelEng.
1090 *@return the newly built instance of #CRSelEng of
1091 *NULL if an error occurs.
1092 */
1093 CRSelEng *
1094 cr_sel_eng_new (void)
1095 {
1096 CRSelEng *result = NULL;
1098 result = (CRSelEng *) g_try_malloc (sizeof (CRSelEng));
1099 if (!result) {
1100 cr_utils_trace_info ("Out of memory");
1101 return NULL;
1102 }
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");
1108 g_free (result);
1109 return NULL;
1110 }
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
1117 (result, "lang",
1118 FUNCTION_PSEUDO, /*(CRPseudoClassSelectorHandler)*/
1119 lang_pseudo_class_handler);
1121 return result;
1122 }
1124 /**
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.
1132 */
1133 enum CRStatus
1134 cr_sel_eng_register_pseudo_class_sel_handler (CRSelEng * a_this,
1135 char * a_name,
1136 enum CRPseudoType a_type,
1137 CRPseudoClassSelectorHandler
1138 a_handler)
1139 {
1140 struct CRPseudoClassSelHandlerEntry *handler_entry = NULL;
1141 GList *list = 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;
1150 }
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);
1157 if (!list) {
1158 return CR_OUT_OF_MEMORY_ERROR;
1159 }
1160 PRIVATE (a_this)->pcs_handlers = list;
1161 return CR_OK;
1162 }
1164 enum CRStatus
1165 cr_sel_eng_unregister_pseudo_class_sel_handler (CRSelEng * a_this,
1166 char * a_name,
1167 enum CRPseudoType a_type)
1168 {
1169 GList *elem = NULL,
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) {
1181 found = TRUE;
1182 break;
1183 }
1184 }
1185 if (found == FALSE)
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;
1190 if (entry->name) {
1191 g_free (entry->name);
1192 entry->name = NULL;
1193 }
1194 g_free (elem);
1195 g_list_free (deleted_elem);
1197 return CR_OK;
1198 }
1200 /**
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
1205 *otherwise.
1206 */
1207 enum CRStatus
1208 cr_sel_eng_unregister_all_pseudo_class_sel_handlers (CRSelEng * a_this)
1209 {
1210 GList *elem = NULL;
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)
1216 return CR_OK;
1217 for (elem = PRIVATE (a_this)->pcs_handlers;
1218 elem; elem = g_list_next (elem)) {
1219 entry = (struct CRPseudoClassSelHandlerEntry *) elem->data;
1220 if (!entry)
1221 continue;
1222 if (entry->name) {
1223 g_free (entry->name);
1224 entry->name = NULL;
1225 }
1226 g_free (entry);
1227 elem->data = NULL;
1228 }
1229 g_list_free (PRIVATE (a_this)->pcs_handlers);
1230 PRIVATE (a_this)->pcs_handlers = NULL;
1231 return CR_OK;
1232 }
1234 enum CRStatus
1235 cr_sel_eng_get_pseudo_class_selector_handler (CRSelEng * a_this,
1236 char * a_name,
1237 enum CRPseudoType a_type,
1238 CRPseudoClassSelectorHandler *
1239 a_handler)
1240 {
1241 GList *elem = NULL;
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) {
1253 found = TRUE;
1254 break;
1255 }
1256 }
1258 if (found == FALSE)
1259 return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
1260 *a_handler = entry->handler;
1261 return CR_OK;
1262 }
1264 /**
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
1267 *not.
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.
1276 */
1277 enum CRStatus
1278 cr_sel_eng_matches_node (CRSelEng * a_this, CRSimpleSel * a_sel,
1279 CRXMLNodePtr a_node, gboolean * a_result)
1280 {
1281 g_return_val_if_fail (a_this && PRIVATE (a_this)
1282 && a_this && a_node
1283 && a_result, CR_BAD_PARAM_ERROR);
1285 if (!PRIVATE(a_this)->node_iface->isElementNode (a_node)) {
1286 *a_result = FALSE;
1287 return CR_OK;
1288 }
1290 return sel_matches_node_real (a_this, a_sel,
1291 a_node, a_result,
1292 TRUE, TRUE);
1293 }
1295 /**
1296 *Returns an array of pointers to selectors that matches
1297 *the xml node given in parameter.
1298 *
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
1302 *the stylesheet.
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.
1310 */
1311 enum CRStatus
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)
1316 {
1317 CRStatement **stmts_tab = NULL;
1318 enum CRStatus status = CR_OK;
1319 gulong tab_size = 0,
1320 tab_len = 0,
1321 index = 0;
1322 gushort stmts_chunck_size = 8;
1324 g_return_val_if_fail (a_this
1325 && a_sheet
1326 && a_node
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 *));
1332 if (!stmts_tab) {
1333 cr_utils_trace_info ("Out of memory");
1334 status = CR_ERROR;
1335 goto error;
1336 }
1337 memset (stmts_tab, 0, stmts_chunck_size * sizeof (CRStatement *));
1339 tab_size = stmts_chunck_size;
1340 tab_len = tab_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 *));
1348 if (!stmts_tab) {
1349 cr_utils_trace_info ("Out of memory");
1350 status = CR_ERROR;
1351 goto error;
1352 }
1353 tab_size += stmts_chunck_size;
1354 index += tab_len;
1355 tab_len = tab_size - index;
1356 }
1358 tab_len = tab_size - stmts_chunck_size + tab_len;
1359 *a_rulesets = stmts_tab;
1360 *a_len = tab_len;
1362 return CR_OK;
1364 error:
1366 if (stmts_tab) {
1367 g_free (stmts_tab);
1368 stmts_tab = NULL;
1370 }
1372 *a_len = 0;
1373 return status;
1374 }
1377 enum CRStatus
1378 cr_sel_eng_get_matched_properties_from_cascade (CRSelEng * a_this,
1379 CRCascade * a_cascade,
1380 CRXMLNodePtr a_node,
1381 CRPropList ** a_props)
1382 {
1383 CRStatement **stmts_tab = NULL;
1384 enum CRStatus status = CR_OK;
1385 gulong tab_size = 0,
1386 tab_len = 0,
1387 i = 0,
1388 index = 0;
1389 enum CRStyleOrigin origin;
1390 gushort stmts_chunck_size = 8;
1391 CRStyleSheet *sheet = NULL;
1393 g_return_val_if_fail (a_this
1394 && a_cascade
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);
1399 if (!sheet)
1400 continue;
1401 if (tab_size - index < 1) {
1402 stmts_tab = (CRStatement **) g_try_realloc
1403 (stmts_tab, (tab_size + stmts_chunck_size)
1404 * sizeof (CRStatement *));
1405 if (!stmts_tab) {
1406 cr_utils_trace_info ("Out of memory");
1407 status = CR_ERROR;
1408 goto cleanup;
1409 }
1410 tab_size += stmts_chunck_size;
1411 /*
1412 *compute the max size left for
1413 *cr_sel_eng_get_matched_rulesets_real()'s output tab
1414 */
1415 tab_len = tab_size - index;
1416 }
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 *));
1423 if (!stmts_tab) {
1424 cr_utils_trace_info ("Out of memory");
1425 status = CR_ERROR;
1426 goto cleanup;
1427 }
1428 tab_size += stmts_chunck_size;
1429 index += tab_len;
1430 /*
1431 *compute the max size left for
1432 *cr_sel_eng_get_matched_rulesets_real()'s output tab
1433 */
1434 tab_len = tab_size - index;
1435 }
1436 if (status != CR_OK) {
1437 cr_utils_trace_info ("Error while running "
1438 "selector engine");
1439 goto cleanup;
1440 }
1441 index += tab_len;
1442 tab_len = tab_size - index;
1443 }
1445 /*
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
1449 *the stylesheet.
1450 */
1451 for (i = 0; i < index; i++) {
1452 CRStatement *stmt = stmts_tab[i];
1454 if (!stmt)
1455 continue;
1456 switch (stmt->type) {
1457 case RULESET_STMT:
1458 if (!stmt->parent_sheet)
1459 continue;
1460 status = put_css_properties_in_props_list
1461 (a_props, stmt);
1462 break;
1463 default:
1464 break;
1465 }
1467 }
1468 status = CR_OK ;
1469 cleanup:
1470 if (stmts_tab) {
1471 g_free (stmts_tab);
1472 stmts_tab = NULL;
1473 }
1475 return status;
1476 }
1478 enum CRStatus
1479 cr_sel_eng_get_matched_style (CRSelEng * a_this,
1480 CRCascade * a_cascade,
1481 CRXMLNodePtr a_node,
1482 CRStyle * a_parent_style,
1483 CRStyle ** a_style,
1484 gboolean a_set_props_to_initial_values)
1485 {
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);
1497 if (props) {
1498 if (!*a_style) {
1499 *a_style = cr_style_new (a_set_props_to_initial_values) ;
1500 g_return_val_if_fail (*a_style, CR_ERROR);
1501 } else {
1502 if (a_set_props_to_initial_values == TRUE) {
1503 cr_style_set_props_to_initial_values (*a_style) ;
1504 } else {
1505 cr_style_set_props_to_default_values (*a_style);
1506 }
1507 }
1508 (*a_style)->parent_style = a_parent_style;
1510 set_style_from_props (*a_style, props);
1511 if (props) {
1512 cr_prop_list_destroy (props);
1513 props = NULL;
1514 }
1515 }
1516 return CR_OK;
1517 }
1519 /**
1520 *The destructor of #CRSelEng
1521 *@param a_this the current instance of the selection engine.
1522 */
1523 void
1524 cr_sel_eng_destroy (CRSelEng * a_this)
1525 {
1526 g_return_if_fail (a_this);
1528 if (!PRIVATE (a_this))
1529 goto end ;
1530 if (PRIVATE (a_this)->pcs_handlers) {
1531 cr_sel_eng_unregister_all_pseudo_class_sel_handlers
1532 (a_this) ;
1533 PRIVATE (a_this)->pcs_handlers = NULL ;
1534 }
1535 g_free (PRIVATE (a_this));
1536 PRIVATE (a_this) = NULL;
1537 end:
1538 if (a_this) {
1539 g_free (a_this);
1540 }
1541 }