Code

Extensions. Add option to choose dxf output units
[inkscape.git] / src / libcroco / cr-sel-eng.c
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)
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;
117 static gboolean
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
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;
162 static gboolean
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
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);
190 static gboolean
191 pseudo_class_add_sel_matches_node (CRSelEng * a_this,
192                                    CRAdditionalSel * a_add_sel,
193                                    CRXMLNodePtr a_node)
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);
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)
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;
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)
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;
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)
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;
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)
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 ;
520 static CRXMLNodePtr
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);
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;
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)
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);
551 static CRXMLNodePtr
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);
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;
564 static CRXMLNodePtr
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);
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;
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)
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;
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)
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;
940 static enum CRStatus
941 put_css_properties_in_props_list (CRPropList ** a_props, CRStatement * a_stmt)
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;
1071 static void
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);
1080                 decl = NULL;
1081         }
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)
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;
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)
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;
1164 enum CRStatus
1165 cr_sel_eng_unregister_pseudo_class_sel_handler (CRSelEng * a_this,
1166                                                 char * a_name,
1167                                                 enum CRPseudoType a_type)
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;
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)
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;
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)
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;
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)
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);
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)
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;
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)
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;
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)
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;
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)
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         }