Imported Upstream version 0.18.1.1
[platform/upstream/gettext.git] / gettext-tools / gnulib-lib / libcroco / cr-sel-eng.c
1 /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
2
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 3 of the GNU 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  */
23
24 #include <config.h>
25 #include <string.h>
26 #include "cr-sel-eng.h"
27
28 /**
29  *@CRSelEng:
30  *
31  *The definition of the  #CRSelEng class.
32  *The #CRSelEng is actually the "Selection Engine"
33  *class. This is highly experimental for at the moment and
34  *its api is very likely to change in a near future.
35  */
36
37 #define PRIVATE(a_this) (a_this)->priv
38
39 struct CRPseudoClassSelHandlerEntry {
40         guchar *name;
41         enum CRPseudoType type;
42         CRPseudoClassSelectorHandler handler;
43 };
44
45 struct _CRSelEngPriv {
46         /*not used yet */
47         gboolean case_sensitive;
48
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 } ;
59
60 static gboolean class_add_sel_matches_node (CRAdditionalSel * a_add_sel,
61                                             xmlNode * a_node);
62
63 static gboolean id_add_sel_matches_node (CRAdditionalSel * a_add_sel,
64                                          xmlNode * a_node);
65
66 static gboolean attr_add_sel_matches_node (CRAdditionalSel * a_add_sel,
67                                            xmlNode * a_node);
68
69 static enum CRStatus sel_matches_node_real (CRSelEng * a_this,
70                                             CRSimpleSel * a_sel,
71                                             xmlNode * a_node,
72                                             gboolean * a_result,
73                                             gboolean a_eval_sel_list_from_end,
74                                             gboolean a_recurse);
75
76 static enum CRStatus cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
77                                                            CRStyleSheet *
78                                                            a_stylesheet,
79                                                            xmlNode * a_node,
80                                                            CRStatement **
81                                                            a_rulesets,
82                                                            gulong * a_len);
83
84 static enum CRStatus put_css_properties_in_props_list (CRPropList ** a_props,
85                                                        CRStatement *
86                                                        a_ruleset);
87
88 static gboolean pseudo_class_add_sel_matches_node (CRSelEng * a_this,
89                                                    CRAdditionalSel *
90                                                    a_add_sel,
91                                                    xmlNode * a_node);
92
93 static gboolean lang_pseudo_class_handler (CRSelEng * a_this,
94                                            CRAdditionalSel * a_sel,
95                                            xmlNode * a_node);
96
97 static gboolean first_child_pseudo_class_handler (CRSelEng * a_this,
98                                                   CRAdditionalSel * a_sel,
99                                                   xmlNode * a_node);
100
101 static xmlNode *get_next_element_node (xmlNode * a_node);
102
103 static xmlNode *get_next_child_element_node (xmlNode * a_node);
104
105 static xmlNode *get_prev_element_node (xmlNode * a_node);
106
107 static xmlNode *get_next_parent_element_node (xmlNode * a_node);
108
109 static gboolean
110 lang_pseudo_class_handler (CRSelEng * a_this,
111                            CRAdditionalSel * a_sel, xmlNode * a_node)
112 {
113         xmlNode *node = a_node;
114         xmlChar *val = NULL;
115         gboolean result = FALSE;
116
117         g_return_val_if_fail (a_this && PRIVATE (a_this)
118                               && a_sel && a_sel->content.pseudo
119                               && a_sel->content.pseudo
120                               && a_sel->content.pseudo->name
121                               && a_sel->content.pseudo->name->stryng
122                               && a_node, CR_BAD_PARAM_ERROR);
123
124         if (strncmp (a_sel->content.pseudo->name->stryng->str, 
125                      "lang", 4)
126             || !a_sel->content.pseudo->type == FUNCTION_PSEUDO) {
127                 cr_utils_trace_info ("This handler is for :lang only");
128                 return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
129         }
130         /*lang code should exist and be at least of length 2 */
131         if (!a_sel->content.pseudo->extra
132             || !a_sel->content.pseudo->extra->stryng
133             || a_sel->content.pseudo->extra->stryng->len < 2)
134                 return FALSE;
135         for (; node; node = get_next_parent_element_node (node)) {
136                 val = xmlGetProp (node, "lang");
137                 if (val
138                     && !strncmp (val,
139                                  a_sel->content.pseudo->extra->stryng->str,
140                                  a_sel->content.pseudo->extra->stryng->len)) {
141                         result = TRUE;
142                 }
143                 if (val) {
144                         xmlFree (val);
145                         val = NULL;
146                 }
147         }
148
149         return result;
150 }
151
152 static gboolean
153 first_child_pseudo_class_handler (CRSelEng * a_this,
154                                   CRAdditionalSel * a_sel, xmlNode * a_node)
155 {
156         xmlNode *node = NULL;
157
158         g_return_val_if_fail (a_this && PRIVATE (a_this)
159                               && a_sel && a_sel->content.pseudo
160                               && a_sel->content.pseudo
161                               && a_sel->content.pseudo->name
162                               && a_sel->content.pseudo->name->stryng
163                               && a_node, CR_BAD_PARAM_ERROR);
164
165         if (strcmp (a_sel->content.pseudo->name->stryng->str,
166                     "first-child")
167             || !a_sel->content.pseudo->type == IDENT_PSEUDO) {
168                 cr_utils_trace_info ("This handler is for :first-child only");
169                 return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
170         }
171         if (!a_node->parent)
172                 return FALSE;
173         node = get_next_child_element_node (a_node->parent);
174         if (node == a_node)
175                 return TRUE;
176         return FALSE;
177 }
178
179 static gboolean
180 pseudo_class_add_sel_matches_node (CRSelEng * a_this,
181                                    CRAdditionalSel * a_add_sel,
182                                    xmlNode * a_node)
183 {
184         enum CRStatus status = CR_OK;
185         CRPseudoClassSelectorHandler handler = NULL;
186
187         g_return_val_if_fail (a_this && PRIVATE (a_this)
188                               && a_add_sel
189                               && a_add_sel->content.pseudo
190                               && a_add_sel->content.pseudo->name
191                               && a_add_sel->content.pseudo->name->stryng
192                               && a_add_sel->content.pseudo->name->stryng->str
193                               && a_node, CR_BAD_PARAM_ERROR);
194
195         status = cr_sel_eng_get_pseudo_class_selector_handler
196                 (a_this, a_add_sel->content.pseudo->name->stryng->str,
197                  a_add_sel->content.pseudo->type, &handler);
198         if (status != CR_OK || !handler)
199                 return FALSE;
200
201         return handler (a_this, a_add_sel, a_node);
202 }
203
204 /**
205  *@param a_add_sel the class additional selector to consider.
206  *@param a_node the xml node to consider.
207  *@return TRUE if the class additional selector matches
208  *the xml node given in argument, FALSE otherwise.
209  */
210 static gboolean
211 class_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
212 {
213         gboolean result = FALSE;
214         xmlChar *klass = NULL,
215                 *cur = NULL;
216
217         g_return_val_if_fail (a_add_sel
218                               && a_add_sel->type == CLASS_ADD_SELECTOR
219                               && a_add_sel->content.class_name
220                               && a_add_sel->content.class_name->stryng
221                               && a_add_sel->content.class_name->stryng->str
222                               && a_node, FALSE);
223
224         if (xmlHasProp (a_node, "class")) {
225                 klass = xmlGetProp (a_node, "class");
226                 for (cur = klass; cur && *cur; cur++) {
227                         while (cur && *cur
228                                && cr_utils_is_white_space (*cur) 
229                                == TRUE)
230                                 cur++;
231
232                         if (!strncmp (cur, 
233                                       a_add_sel->content.class_name->stryng->str,
234                                       a_add_sel->content.class_name->stryng->len)) {
235                                 cur += a_add_sel->content.class_name->stryng->len;
236                                 if ((cur && !*cur)
237                                     || cr_utils_is_white_space (*cur) == TRUE)
238                                         result = TRUE;
239                         }
240                         if (cur && !*cur)
241                                 break ;
242                 }
243         }
244         if (klass) {
245                 xmlFree (klass);
246                 klass = NULL;
247         }
248         return result;
249
250 }
251
252 /**
253  *@return TRUE if the additional attribute selector matches
254  *the current xml node given in argument, FALSE otherwise.
255  *@param a_add_sel the additional attribute selector to consider.
256  *@param a_node the xml node to consider.
257  */
258 static gboolean
259 id_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
260 {
261         gboolean result = FALSE;
262         xmlChar *id = NULL;
263
264         g_return_val_if_fail (a_add_sel
265                               && a_add_sel->type == ID_ADD_SELECTOR
266                               && a_add_sel->content.id_name
267                               && a_add_sel->content.id_name->stryng
268                               && a_add_sel->content.id_name->stryng->str
269                               && a_node, FALSE);
270         g_return_val_if_fail (a_add_sel
271                               && a_add_sel->type == ID_ADD_SELECTOR
272                               && a_node, FALSE);
273
274         if (xmlHasProp (a_node, "id")) {
275                 id = xmlGetProp (a_node, "id");
276                 if (!strncmp (id, a_add_sel->content.id_name->stryng->str,
277                               a_add_sel->content.id_name->stryng->len)) {
278                         result = TRUE;
279                 }
280         }
281         if (id) {
282                 xmlFree (id);
283                 id = NULL;
284         }
285         return result;
286 }
287
288 /**
289  *Returns TRUE if the instance of #CRAdditional selector matches
290  *the node given in parameter, FALSE otherwise.
291  *@param a_add_sel the additional selector to evaluate.
292  *@param a_node the xml node against whitch the selector is to
293  *be evaluated
294  *return TRUE if the additional selector matches the current xml node
295  *FALSE otherwise.
296  */
297 static gboolean
298 attr_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
299 {
300         CRAttrSel *cur_sel = NULL;
301
302         g_return_val_if_fail (a_add_sel
303                               && a_add_sel->type == ATTRIBUTE_ADD_SELECTOR
304                               && a_node, FALSE);
305
306         for (cur_sel = a_add_sel->content.attr_sel;
307              cur_sel; cur_sel = cur_sel->next) {
308                 switch (cur_sel->match_way) {
309                 case SET:
310                         if (!cur_sel->name 
311                             || !cur_sel->name->stryng
312                             || !cur_sel->name->stryng->str)
313                                 return FALSE;
314
315                         if (!xmlHasProp (a_node,
316                                          cur_sel->name->stryng->str))
317                                 return FALSE;
318                         break;
319
320                 case EQUALS:
321                         {
322                                 xmlChar *value = NULL;
323
324                                 if (!cur_sel->name 
325                                     || !cur_sel->name->stryng
326                                     || !cur_sel->name->stryng->str
327                                     || !cur_sel->value
328                                     || !cur_sel->value->stryng
329                                     || !cur_sel->value->stryng->str)
330                                         return FALSE;
331
332                                 if (!xmlHasProp 
333                                     (a_node, 
334                                      cur_sel->name->stryng->str))
335                                         return FALSE;
336
337                                 value = xmlGetProp 
338                                         (a_node,
339                                          cur_sel->name->stryng->str);
340
341                                 if (value
342                                     && strcmp 
343                                     (value, 
344                                      cur_sel->value->stryng->str)) {
345                                         xmlFree (value);
346                                         return FALSE;
347                                 }
348                                 xmlFree (value);
349                         }
350                         break;
351
352                 case INCLUDES:
353                         {
354                                 xmlChar *value = NULL,
355                                         *ptr1 = NULL,
356                                         *ptr2 = NULL,
357                                         *cur = NULL;
358                                 gboolean found = FALSE;
359
360                                 if (!xmlHasProp 
361                                     (a_node, 
362                                      cur_sel->name->stryng->str))
363                                         return FALSE;
364                                 value = xmlGetProp 
365                                         (a_node,
366                                          cur_sel->name->stryng->str);
367
368                                 if (!value)
369                                         return FALSE;
370
371                                 /*
372                                  *here, make sure value is a space
373                                  *separated list of "words", where one
374                                  *value is exactly cur_sel->value->str
375                                  */
376                                 for (cur = value; *cur; cur++) {
377                                         /*
378                                          *set ptr1 to the first non white space
379                                          *char addr.
380                                          */
381                                         while (cr_utils_is_white_space
382                                                (*cur) == TRUE && *cur)
383                                                 cur++;
384                                         if (!*cur)
385                                                 break;
386                                         ptr1 = cur;
387
388                                         /*
389                                          *set ptr2 to the end the word.
390                                          */
391                                         while (cr_utils_is_white_space
392                                                (*cur) == FALSE && *cur)
393                                                 cur++;
394                                         cur--;
395                                         ptr2 = cur;
396
397                                         if (!strncmp
398                                             (ptr1, 
399                                              cur_sel->value->stryng->str,
400                                              ptr2 - ptr1 + 1)) {
401                                                 found = TRUE;
402                                                 break;
403                                         }
404                                         ptr1 = ptr2 = NULL;
405                                 }
406
407                                 if (found == FALSE) {
408                                         xmlFree (value);
409                                         return FALSE;
410                                 }
411                                 xmlFree (value);
412                         }
413                         break;
414
415                 case DASHMATCH:
416                         {
417                                 xmlChar *value = NULL,
418                                         *ptr1 = NULL,
419                                         *ptr2 = NULL,
420                                         *cur = NULL;
421                                 gboolean found = FALSE;
422
423                                 if (!xmlHasProp 
424                                     (a_node, 
425                                      cur_sel->name->stryng->str))
426                                         return FALSE;
427                                 value = xmlGetProp 
428                                         (a_node,
429                                          cur_sel->name->stryng->str);
430
431                                 /*
432                                  *here, make sure value is an hyphen
433                                  *separated list of "words", each of which
434                                  *starting with "cur_sel->value->str"
435                                  */
436                                 for (cur = value; *cur; cur++) {
437                                         if (*cur == '-')
438                                                 cur++;
439                                         ptr1 = cur;
440
441                                         while (*cur != '-' && *cur)
442                                                 cur++;
443                                         cur--;
444                                         ptr2 = cur;
445
446                                         if (g_strstr_len
447                                             (ptr1, ptr2 - ptr1 + 1,
448                                              cur_sel->value->stryng->str)
449                                             == (gchar *) ptr1) {
450                                                 found = TRUE;
451                                                 break;
452                                         }
453                                 }
454
455                                 if (found == FALSE) {
456                                         xmlFree (value);
457                                         return FALSE;
458                                 }
459                                 xmlFree (value);
460                         }
461                         break;
462                 default:
463                         return FALSE;
464                 }
465         }
466
467         return TRUE;
468 }
469
470 /**
471  *Evaluates if a given additional selector matches an xml node.
472  *@param a_add_sel the additional selector to consider.
473  *@param a_node the xml node to consider.
474  *@return TRUE is a_add_sel matches a_node, FALSE otherwise.
475  */
476 static gboolean
477 additional_selector_matches_node (CRSelEng * a_this,
478                                   CRAdditionalSel * a_add_sel,
479                                   xmlNode * a_node)
480 {
481         CRAdditionalSel *cur_add_sel = NULL, *tail = NULL ;
482         gboolean evaluated = FALSE ;
483
484         for (tail = a_add_sel ; 
485              tail && tail->next; 
486              tail = tail->next) ;
487
488         g_return_val_if_fail (tail, FALSE) ;
489
490         for (cur_add_sel = tail ;
491              cur_add_sel ;
492              cur_add_sel = cur_add_sel->prev) {
493
494                 evaluated = TRUE ;
495                 if (cur_add_sel->type == NO_ADD_SELECTOR) {
496                         return FALSE;
497                 }
498
499                 if (cur_add_sel->type == CLASS_ADD_SELECTOR
500                     && cur_add_sel->content.class_name
501                     && cur_add_sel->content.class_name->stryng
502                     && cur_add_sel->content.class_name->stryng->str) {
503                         if (class_add_sel_matches_node (cur_add_sel,
504                                                         a_node) == FALSE) {
505                                 return FALSE;
506                         }
507                         continue ;
508                 } else if (cur_add_sel->type == ID_ADD_SELECTOR
509                            && cur_add_sel->content.id_name
510                            && cur_add_sel->content.id_name->stryng
511                            && cur_add_sel->content.id_name->stryng->str) {
512                         if (id_add_sel_matches_node (cur_add_sel, a_node) == FALSE) {
513                                 return FALSE;
514                         }
515                         continue ;
516                 } else if (cur_add_sel->type == ATTRIBUTE_ADD_SELECTOR
517                            && cur_add_sel->content.attr_sel) {
518                         /*
519                          *here, call a function that does the match
520                          *against an attribute additionnal selector
521                          *and an xml node.
522                          */
523                         if (attr_add_sel_matches_node (cur_add_sel, a_node)
524                             == FALSE) {
525                                 return FALSE;
526                         }
527                         continue ;
528                 } else if (cur_add_sel->type == PSEUDO_CLASS_ADD_SELECTOR
529                            && cur_add_sel->content.pseudo) {
530                         if (pseudo_class_add_sel_matches_node
531                             (a_this, cur_add_sel, a_node) == TRUE) {
532                                 return TRUE;
533                         }
534                         return FALSE;
535                 }
536         }
537         if (evaluated == TRUE)
538                 return TRUE;
539         return FALSE ;
540 }
541
542 static xmlNode *
543 get_next_element_node (xmlNode * a_node)
544 {
545         xmlNode *cur_node = NULL;
546
547         g_return_val_if_fail (a_node, NULL);
548
549         cur_node = a_node->next;
550         while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
551                 cur_node = cur_node->next;
552         }
553         return cur_node;
554 }
555
556 static xmlNode *
557 get_next_child_element_node (xmlNode * a_node)
558 {
559         xmlNode *cur_node = NULL;
560
561         g_return_val_if_fail (a_node, NULL);
562
563         cur_node = a_node->children;
564         if (!cur_node)
565                 return cur_node;
566         if (a_node->children->type == XML_ELEMENT_NODE)
567                 return a_node->children;
568         return get_next_element_node (a_node->children);
569 }
570
571 static xmlNode *
572 get_prev_element_node (xmlNode * a_node)
573 {
574         xmlNode *cur_node = NULL;
575
576         g_return_val_if_fail (a_node, NULL);
577
578         cur_node = a_node->prev;
579         while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
580                 cur_node = cur_node->prev;
581         }
582         return cur_node;
583 }
584
585 static xmlNode *
586 get_next_parent_element_node (xmlNode * a_node)
587 {
588         xmlNode *cur_node = NULL;
589
590         g_return_val_if_fail (a_node, NULL);
591
592         cur_node = a_node->parent;
593         while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
594                 cur_node = cur_node->parent;
595         }
596         return cur_node;
597 }
598
599 /**
600  *Evaluate a selector (a simple selectors list) and says
601  *if it matches the xml node given in parameter.
602  *The algorithm used here is the following:
603  *Walk the combinator separated list of simple selectors backward, starting
604  *from the end of the list. For each simple selector, looks if
605  *if matches the current node.
606  *
607  *@param a_this the selection engine.
608  *@param a_sel the simple selection list.
609  *@param a_node the xml node.
610  *@param a_result out parameter. Set to true if the
611  *selector matches the xml node, FALSE otherwise.
612  *@param a_recurse if set to TRUE, the function will walk to
613  *the next simple selector (after the evaluation of the current one) 
614  *and recursively evaluate it. Must be usually set to TRUE unless you
615  *know what you are doing.
616  */
617 static enum CRStatus
618 sel_matches_node_real (CRSelEng * a_this, CRSimpleSel * a_sel,
619                        xmlNode * a_node, gboolean * a_result,
620                        gboolean a_eval_sel_list_from_end,
621                        gboolean a_recurse)
622 {
623         CRSimpleSel *cur_sel = NULL;
624         xmlNode *cur_node = NULL;
625
626         g_return_val_if_fail (a_this && PRIVATE (a_this)
627                               && a_this && a_node
628                               && a_result, CR_BAD_PARAM_ERROR);
629
630         *a_result = FALSE;
631
632         if (a_node->type != XML_ELEMENT_NODE)
633                 return CR_OK;
634
635         if (a_eval_sel_list_from_end == TRUE) {
636                 /*go and get the last simple selector of the list */
637                 for (cur_sel = a_sel;
638                      cur_sel && cur_sel->next; cur_sel = cur_sel->next) ;
639         } else {
640                 cur_sel = a_sel;
641         }
642
643         for (cur_node = a_node; cur_sel; cur_sel = cur_sel->prev) {
644                 if (((cur_sel->type_mask & TYPE_SELECTOR)
645                      && (cur_sel->name 
646                          && cur_sel->name->stryng
647                          && cur_sel->name->stryng->str)
648                      && (!strcmp (cur_sel->name->stryng->str,
649                                   cur_node->name)))
650                     || (cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
651                         /*
652                          *this simple selector
653                          *matches the current xml node
654                          *Let's see if the preceding
655                          *simple selectors also match
656                          *their xml node counterpart.
657                          */
658                         if (cur_sel->add_sel) {
659                                 if (additional_selector_matches_node (a_this, cur_sel->add_sel, 
660                                                                       cur_node) == TRUE) {
661                                         goto walk_a_step_in_expr;
662                                 } else {
663                                         goto done;
664                                 }
665                         } else {
666                                 goto walk_a_step_in_expr;
667                         }                                
668                 } 
669                 if (!(cur_sel->type_mask & TYPE_SELECTOR)
670                     && !(cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
671                         if (!cur_sel->add_sel) {
672                                 goto done;
673                         }
674                         if (additional_selector_matches_node
675                             (a_this, cur_sel->add_sel, cur_node)
676                             == TRUE) {
677                                 goto walk_a_step_in_expr;
678                         } else {
679                                 goto done;
680                         }
681                 } else {
682                         goto done ;
683                 }
684
685         walk_a_step_in_expr:
686                 if (a_recurse == FALSE) {
687                         *a_result = TRUE;
688                         goto done;
689                 }
690
691                 /*
692                  *here, depending on the combinator of cur_sel
693                  *choose the axis of the xml tree traversal
694                  *and walk one step in the xml tree.
695                  */
696                 if (!cur_sel->prev)
697                         break;
698
699                 switch (cur_sel->combinator) {
700                 case NO_COMBINATOR:
701                         break;
702
703                 case COMB_WS:  /*descendant selector */
704                 {
705                         xmlNode *n = NULL;
706                         enum CRStatus status = CR_OK;
707                         gboolean matches = FALSE;
708
709                         /*
710                          *walk the xml tree upward looking for a parent
711                          *node that matches the preceding selector.
712                          */
713                         for (n = cur_node->parent; n; n = n->parent) {
714                                 status = sel_matches_node_real
715                                         (a_this, cur_sel->prev,
716                                          n, &matches, FALSE, TRUE);
717
718                                 if (status != CR_OK)
719                                         goto done;
720
721                                 if (matches == TRUE) {
722                                         cur_node = n ;
723                                         break;
724                                 }
725                         }
726
727                         if (!n) {
728                                 /*
729                                  *didn't find any ancestor that matches
730                                  *the previous simple selector.
731                                  */
732                                 goto done;
733                         }
734                         /*
735                          *in this case, the preceding simple sel
736                          *will have been interpreted twice, which
737                          *is a cpu and mem waste ... I need to find
738                          *another way to do this. Anyway, this is
739                          *my first attempt to write this function and
740                          *I am a bit clueless.
741                          */
742                         break;
743                 }
744
745                 case COMB_PLUS:
746                         cur_node = get_prev_element_node (cur_node);
747                         if (!cur_node)
748                                 goto done;
749                         break;
750
751                 case COMB_GT:
752                         cur_node = get_next_parent_element_node (cur_node);
753                         if (!cur_node)
754                                 goto done;
755                         break;
756
757                 default:
758                         goto done;
759                 }
760                 continue;
761         }
762
763         /*
764          *if we reached this point, it means the selector matches
765          *the xml node.
766          */
767         *a_result = TRUE;
768
769  done:
770         return CR_OK;
771 }
772
773
774 /**
775  *Returns  array of the ruleset statements that matches the
776  *given xml node.
777  *The engine keeps in memory the last statement he
778  *visited during the match. So, the next call
779  *to this function will eventually return a rulesets list starting
780  *from the last ruleset statement visited during the previous call.
781  *The enable users to get matching rulesets in an incremental way.
782  *Note that for each statement returned, 
783  *the engine calculates the specificity of the selector
784  *that matched the xml node and stores it in the "specifity" field
785  *of the statement structure.
786  *
787  *@param a_sel_eng the current selection engine
788  *@param a_node the xml node for which the request
789  *is being made.
790  *@param a_sel_list the list of selectors to perform the search in.
791  *@param a_rulesets in/out parameter. A pointer to the
792  *returned array of rulesets statements that match the xml node
793  *given in parameter. The caller allocates the array before calling this
794  *function.
795  *@param a_len in/out parameter the length (in sizeof (#CRStatement*)) 
796  *of the returned array.
797  *(the length of a_rulesets, more precisely).
798  *The caller must set it to the length of a_ruleset prior to calling this
799  *function. In return, the function sets it to the length 
800  *(in sizeof (#CRStatement)) of the actually returned CRStatement array.
801  *@return CR_OUTPUT_TOO_SHORT_ERROR if found more rulesets than the size
802  *of the a_rulesets array. In this case, the first *a_len rulesets found
803  *are put in a_rulesets, and a further call will return the following
804  *ruleset(s) following the same principle.
805  *@return CR_OK if all the rulesets found have been returned. In this
806  *case, *a_len is set to the actual number of ruleset found.
807  *@return CR_BAD_PARAM_ERROR in case any of the given parameter are
808  *bad (e.g null pointer).
809  *@return CR_ERROR if any other error occured.
810  */
811 static enum CRStatus
812 cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
813                                       CRStyleSheet * a_stylesheet,
814                                       xmlNode * a_node,
815                                       CRStatement ** a_rulesets,
816                                       gulong * a_len)
817 {
818         CRStatement *cur_stmt = NULL;
819         CRSelector *sel_list = NULL,
820                 *cur_sel = NULL;
821         gboolean matches = FALSE;
822         enum CRStatus status = CR_OK;
823         gulong i = 0;
824
825         g_return_val_if_fail (a_this
826                               && a_stylesheet
827                               && a_node && a_rulesets, CR_BAD_PARAM_ERROR);
828
829         if (!a_stylesheet->statements) {
830                 *a_rulesets = NULL;
831                 *a_len = 0;
832                 return CR_OK;
833         }
834
835         /*
836          *if this stylesheet is "new one"
837          *let's remember it for subsequent calls.
838          */
839         if (PRIVATE (a_this)->sheet != a_stylesheet) {
840                 PRIVATE (a_this)->sheet = a_stylesheet;
841                 PRIVATE (a_this)->cur_stmt = a_stylesheet->statements;
842         }
843
844         /*
845          *walk through the list of statements and,
846          *get the selectors list inside the statements that
847          *contain some, and try to match our xml node in these
848          *selectors lists.
849          */
850         for (cur_stmt = PRIVATE (a_this)->cur_stmt, i = 0;
851              (PRIVATE (a_this)->cur_stmt = cur_stmt);
852              cur_stmt = cur_stmt->next) {
853                 /*
854                  *initialyze the selector list in which we will
855                  *really perform the search.
856                  */
857                 sel_list = NULL;
858
859                 /*
860                  *get the the damn selector list in 
861                  *which we have to look
862                  */
863                 switch (cur_stmt->type) {
864                 case RULESET_STMT:
865                         if (cur_stmt->kind.ruleset
866                             && cur_stmt->kind.ruleset->sel_list) {
867                                 sel_list = cur_stmt->kind.ruleset->sel_list;
868                         }
869                         break;
870
871                 case AT_MEDIA_RULE_STMT:
872                         if (cur_stmt->kind.media_rule
873                             && cur_stmt->kind.media_rule->rulesets
874                             && cur_stmt->kind.media_rule->rulesets->
875                             kind.ruleset
876                             && cur_stmt->kind.media_rule->rulesets->
877                             kind.ruleset->sel_list) {
878                                 sel_list =
879                                         cur_stmt->kind.media_rule->
880                                         rulesets->kind.ruleset->sel_list;
881                         }
882                         break;
883
884                 case AT_IMPORT_RULE_STMT:
885                         /*
886                          *some recursivity may be needed here.
887                          *I don't like this :(
888                          */
889                         break;
890                 default:
891                         break;
892                 }
893
894                 if (!sel_list)
895                         continue;
896
897                 /*
898                  *now, we have a comma separated selector list to look in.
899                  *let's walk it and try to match the xml_node
900                  *on each item of the list.
901                  */
902                 for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next) {
903                         if (!cur_sel->simple_sel)
904                                 continue;
905
906                         status = cr_sel_eng_matches_node
907                                 (a_this, cur_sel->simple_sel,
908                                  a_node, &matches);
909
910                         if (status == CR_OK && matches == TRUE) {
911                                 /*
912                                  *bingo!!! we found one ruleset that
913                                  *matches that fucking node.
914                                  *lets put it in the out array.
915                                  */
916
917                                 if (i < *a_len) {
918                                         a_rulesets[i] = cur_stmt;
919                                         i++;
920
921                                         /*
922                                          *For the cascade computing algorithm
923                                          *(which is gonna take place later)
924                                          *we must compute the specificity
925                                          *(css2 spec chap 6.4.1) of the selector
926                                          *that matched the current xml node
927                                          *and store it in the css2 statement
928                                          *(statement == ruleset here).
929                                          */
930                                         status = cr_simple_sel_compute_specificity (cur_sel->simple_sel);
931
932                                         g_return_val_if_fail (status == CR_OK,
933                                                               CR_ERROR);
934                                         cur_stmt->specificity =
935                                                 cur_sel->simple_sel->
936                                                 specificity;
937                                 } else
938                                 {
939                                         *a_len = i;
940                                         return CR_OUTPUT_TOO_SHORT_ERROR;
941                                 }
942                         }
943                 }
944         }
945
946         /*
947          *if we reached this point, it means
948          *we reached the end of stylesheet.
949          *no need to store any info about the stylesheet
950          *anymore.
951          */
952         g_return_val_if_fail (!PRIVATE (a_this)->cur_stmt, CR_ERROR);
953         PRIVATE (a_this)->sheet = NULL;
954         *a_len = i;
955         return CR_OK;
956 }
957
958 static enum CRStatus
959 put_css_properties_in_props_list (CRPropList ** a_props, CRStatement * a_stmt)
960 {
961         CRPropList *props = NULL,
962                 *pair = NULL,
963                 *tmp_props = NULL;
964         CRDeclaration *cur_decl = NULL;
965
966         g_return_val_if_fail (a_props && a_stmt
967                               && a_stmt->type == RULESET_STMT
968                               && a_stmt->kind.ruleset, CR_BAD_PARAM_ERROR);
969
970         props = *a_props;
971
972         for (cur_decl = a_stmt->kind.ruleset->decl_list;
973              cur_decl; cur_decl = cur_decl->next) {
974                 CRDeclaration *decl;
975
976                 decl = NULL;
977                 pair = NULL;
978
979                 if (!cur_decl->property 
980                     || !cur_decl->property->stryng
981                     || !cur_decl->property->stryng->str)
982                         continue;
983                 /*
984                  *First, test if the property is not
985                  *already present in our properties list
986                  *If yes, apply the cascading rules to
987                  *compute the precedence. If not, insert
988                  *the property into the list
989                  */
990                 cr_prop_list_lookup_prop (props,
991                                           cur_decl->property, 
992                                           &pair);
993
994                 if (!pair) {
995                         tmp_props = cr_prop_list_append2
996                                 (props, cur_decl->property, cur_decl);
997                         if (tmp_props) {
998                                 props = tmp_props;
999                                 tmp_props = NULL;
1000                         }
1001                         continue;
1002                 }
1003
1004                 /*
1005                  *A property with the same name already exists.
1006                  *We must apply here 
1007                  *some cascading rules
1008                  *to compute the precedence.
1009                  */
1010                 cr_prop_list_get_decl (pair, &decl);
1011                 g_return_val_if_fail (decl, CR_ERROR);
1012
1013                 /*
1014                  *first, look at the origin.
1015                  *6.4.1 says: 
1016                  *"for normal declarations, 
1017                  *author style sheets override user 
1018                  *style sheets which override 
1019                  *the default style sheet."
1020                  */
1021                 if (decl->parent_statement
1022                     && decl->parent_statement->parent_sheet
1023                     && (decl->parent_statement->parent_sheet->origin
1024                         < a_stmt->parent_sheet->origin)) {
1025                         /*
1026                          *if the already selected declaration
1027                          *is marked as being !important the current
1028                          *declaration must not overide it 
1029                          *(unless the already selected declaration 
1030                          *has an UA origin)
1031                          */
1032                         if (decl->important == TRUE
1033                             && decl->parent_statement->parent_sheet->origin
1034                             != ORIGIN_UA) {
1035                                 continue;
1036                         }
1037                         tmp_props = cr_prop_list_unlink (props, pair);
1038                         if (props) {
1039                                 cr_prop_list_destroy (pair);
1040                         }
1041                         props = tmp_props;
1042                         tmp_props = NULL;
1043                         props = cr_prop_list_append2
1044                                 (props, cur_decl->property, cur_decl);
1045
1046                         continue;
1047                 } else if (decl->parent_statement
1048                            && decl->parent_statement->parent_sheet
1049                            && (decl->parent_statement->
1050                                parent_sheet->origin
1051                                > a_stmt->parent_sheet->origin)) {
1052                         cr_utils_trace_info
1053                                 ("We should not reach this line\n");
1054                         continue;
1055                 }
1056
1057                 /*
1058                  *A property with the same
1059                  *name and the same origin already exists.
1060                  *shit. This is lasting longer than expected ...
1061                  *Luckily, the spec says in 6.4.1:
1062                  *"more specific selectors will override 
1063                  *more general ones"
1064                  *and
1065                  *"if two rules have the same weight, 
1066                  *origin and specificity, 
1067                  *the later specified wins"
1068                  */
1069                 if (a_stmt->specificity
1070                     >= decl->parent_statement->specificity) {
1071                         if (decl->important == TRUE)
1072                                 continue;
1073                         props = cr_prop_list_unlink (props, pair);
1074                         if (pair) {
1075                                 cr_prop_list_destroy (pair);
1076                                 pair = NULL;
1077                         }
1078                         props = cr_prop_list_append2 (props,
1079                                                       cur_decl->property,
1080                                                       cur_decl);
1081                 }
1082         }
1083         /*TODO: this may leak. Check this out */
1084         *a_props = props;
1085
1086         return CR_OK;
1087 }
1088
1089 static void
1090 set_style_from_props (CRStyle * a_style, CRPropList * a_props)
1091 {
1092         CRPropList *cur = NULL;
1093         CRDeclaration *decl = NULL;
1094
1095         for (cur = a_props; cur; cur = cr_prop_list_get_next (cur)) {
1096                 cr_prop_list_get_decl (cur, &decl);
1097                 cr_style_set_style_from_decl (a_style, decl);
1098                 decl = NULL;
1099         }
1100 }
1101
1102 /****************************************
1103  *PUBLIC METHODS
1104  ****************************************/
1105
1106 /**
1107  * cr_sel_eng_new:
1108  *Creates a new instance of #CRSelEng.
1109  *
1110  *Returns the newly built instance of #CRSelEng of
1111  *NULL if an error occurs.
1112  */
1113 CRSelEng *
1114 cr_sel_eng_new (void)
1115 {
1116         CRSelEng *result = NULL;
1117
1118         result = g_try_malloc (sizeof (CRSelEng));
1119         if (!result) {
1120                 cr_utils_trace_info ("Out of memory");
1121                 return NULL;
1122         }
1123         memset (result, 0, sizeof (CRSelEng));
1124
1125         PRIVATE (result) = g_try_malloc (sizeof (CRSelEngPriv));
1126         if (!PRIVATE (result)) {
1127                 cr_utils_trace_info ("Out of memory");
1128                 g_free (result);
1129                 return NULL;
1130         }
1131         memset (PRIVATE (result), 0, sizeof (CRSelEngPriv));
1132         cr_sel_eng_register_pseudo_class_sel_handler
1133                 (result, (guchar *) "first-child",
1134                  IDENT_PSEUDO, (CRPseudoClassSelectorHandler)
1135                  first_child_pseudo_class_handler);
1136         cr_sel_eng_register_pseudo_class_sel_handler
1137                 (result, (guchar *) "lang",
1138                  FUNCTION_PSEUDO, (CRPseudoClassSelectorHandler)
1139                  lang_pseudo_class_handler);
1140
1141         return result;
1142 }
1143
1144 /**
1145  * cr_sel_eng_register_pseudo_class_sel_handler:
1146  *@a_this: the current instance of #CRSelEng
1147  *@a_pseudo_class_sel_name: the name of the pseudo class selector.
1148  *@a_pseudo_class_type: the type of the pseudo class selector.
1149  *@a_handler: the actual handler or callback to be called during
1150  *the selector evaluation process.
1151  *
1152  *Adds a new handler entry in the handlers entry table.
1153  *
1154  *Returns CR_OK, upon successful completion, an error code otherwise.
1155  */
1156 enum CRStatus
1157 cr_sel_eng_register_pseudo_class_sel_handler (CRSelEng * a_this,
1158                                               guchar * a_name,
1159                                               enum CRPseudoType a_type,
1160                                               CRPseudoClassSelectorHandler
1161                                               a_handler)
1162 {
1163         struct CRPseudoClassSelHandlerEntry *handler_entry = NULL;
1164         GList *list = NULL;
1165
1166         g_return_val_if_fail (a_this && PRIVATE (a_this)
1167                               && a_handler && a_name, CR_BAD_PARAM_ERROR);
1168
1169         handler_entry = g_try_malloc
1170                 (sizeof (struct CRPseudoClassSelHandlerEntry));
1171         if (!handler_entry) {
1172                 return CR_OUT_OF_MEMORY_ERROR;
1173         }
1174         memset (handler_entry, 0,
1175                 sizeof (struct CRPseudoClassSelHandlerEntry));
1176         handler_entry->name = g_strdup (a_name);
1177         handler_entry->type = a_type;
1178         handler_entry->handler = a_handler;
1179         list = g_list_append (PRIVATE (a_this)->pcs_handlers, handler_entry);
1180         if (!list) {
1181                 return CR_OUT_OF_MEMORY_ERROR;
1182         }
1183         PRIVATE (a_this)->pcs_handlers = list;
1184         return CR_OK;
1185 }
1186
1187 enum CRStatus
1188 cr_sel_eng_unregister_pseudo_class_sel_handler (CRSelEng * a_this,
1189                                                 guchar * a_name,
1190                                                 enum CRPseudoType a_type)
1191 {
1192         GList *elem = NULL,
1193                 *deleted_elem = NULL;
1194         gboolean found = FALSE;
1195         struct CRPseudoClassSelHandlerEntry *entry = NULL;
1196
1197         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1198
1199         for (elem = PRIVATE (a_this)->pcs_handlers;
1200              elem; elem = g_list_next (elem)) {
1201                 entry = elem->data;
1202                 if (!strcmp (entry->name, a_name)
1203                     && entry->type == a_type) {
1204                         found = TRUE;
1205                         break;
1206                 }
1207         }
1208         if (found == FALSE)
1209                 return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
1210         PRIVATE (a_this)->pcs_handlers = g_list_delete_link
1211                 (PRIVATE (a_this)->pcs_handlers, elem);
1212         entry = elem->data;
1213         if (entry->name)
1214                 g_free (entry->name);
1215         g_free (elem);
1216         g_list_free (deleted_elem);
1217
1218         return CR_OK;
1219 }
1220
1221 /**
1222  * cr_sel_eng_unregister_all_pseudo_class_sel_handlers:
1223  *@a_this: the current instance of #CRSelEng .
1224  *
1225  *Unregisters all the pseudo class sel handlers
1226  *and frees all the associated allocated datastructures.
1227  *
1228  *Returns CR_OK upon succesful completion, an error code
1229  *otherwise.
1230  */
1231 enum CRStatus
1232 cr_sel_eng_unregister_all_pseudo_class_sel_handlers (CRSelEng * a_this)
1233 {
1234         GList *elem = NULL;
1235         struct CRPseudoClassSelHandlerEntry *entry = NULL;
1236
1237         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1238
1239         if (!PRIVATE (a_this)->pcs_handlers)
1240                 return CR_OK;
1241         for (elem = PRIVATE (a_this)->pcs_handlers;
1242              elem; elem = g_list_next (elem)) {
1243                 entry = elem->data;
1244                 if (!entry)
1245                         continue;
1246                 if (entry->name) {
1247                         g_free (entry->name);
1248                         entry->name = NULL;
1249                 }
1250                 g_free (entry);
1251                 elem->data = NULL;
1252         }
1253         g_list_free (PRIVATE (a_this)->pcs_handlers);
1254         PRIVATE (a_this)->pcs_handlers = NULL;
1255         return CR_OK;
1256 }
1257
1258 enum CRStatus
1259 cr_sel_eng_get_pseudo_class_selector_handler (CRSelEng * a_this,
1260                                               guchar * a_name,
1261                                               enum CRPseudoType a_type,
1262                                               CRPseudoClassSelectorHandler *
1263                                               a_handler)
1264 {
1265         GList *elem = NULL;
1266         struct CRPseudoClassSelHandlerEntry *entry = NULL;
1267         gboolean found = FALSE;
1268
1269         g_return_val_if_fail (a_this && PRIVATE (a_this)
1270                               && a_name, CR_BAD_PARAM_ERROR);
1271
1272         for (elem = PRIVATE (a_this)->pcs_handlers;
1273              elem; elem = g_list_next (elem)) {
1274                 entry = elem->data;
1275                 if (!strcmp (a_name, entry->name)
1276                     && entry->type == a_type) {
1277                         found = TRUE;
1278                         break;
1279                 }
1280         }
1281
1282         if (found == FALSE)
1283                 return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
1284         *a_handler = entry->handler;
1285         return CR_OK;
1286 }
1287
1288 /**
1289  * cr_sel_eng_matches_node:
1290  *@a_this: the selection engine.
1291  *@a_sel: the simple selector against which the xml node 
1292  *is going to be matched.
1293  *@a_node: the node against which the selector is going to be matched.
1294  *@a_result: out parameter. The result of the match. Is set to
1295  *TRUE if the selector matches the node, FALSE otherwise. This value
1296  *is considered if and only if this functions returns CR_OK.
1297  *
1298  *Evaluates a chained list of simple selectors (known as a css2 selector).
1299  *Says wheter if this selector matches the xml node given in parameter or
1300  *not.
1301  *
1302  *Returns the CR_OK if the selection ran correctly, an error code otherwise.
1303  */
1304 enum CRStatus
1305 cr_sel_eng_matches_node (CRSelEng * a_this, CRSimpleSel * a_sel,
1306                          xmlNode * a_node, gboolean * a_result)
1307 {
1308         g_return_val_if_fail (a_this && PRIVATE (a_this)
1309                               && a_this && a_node
1310                               && a_result, CR_BAD_PARAM_ERROR);
1311
1312         if (a_node->type != XML_ELEMENT_NODE) {
1313                 *a_result = FALSE;
1314                 return CR_OK;
1315         }
1316
1317         return sel_matches_node_real (a_this, a_sel, 
1318                                       a_node, a_result, 
1319                                       TRUE, TRUE);
1320 }
1321
1322 /**
1323  * cr_sel_eng_get_matched_rulesets:
1324  *@a_this: the current instance of the selection engine.
1325  *@a_sheet: the stylesheet that holds the selectors.
1326  *@a_node: the xml node to consider during the walk thru
1327  *the stylesheet.
1328  *@a_rulesets: out parameter. A pointer to an array of
1329  *rulesets statement pointers. *a_rulesets is allocated by
1330  *this function and must be freed by the caller. However, the caller
1331  *must not alter the rulesets statements pointer because they
1332  *point to statements that are still in the css stylesheet.
1333  *@a_len: the length of *a_ruleset.
1334  *
1335  *Returns an array of pointers to selectors that matches
1336  *the xml node given in parameter.
1337  *
1338  *Returns CR_OK upon sucessfull completion, an error code otherwise.
1339  */
1340 enum CRStatus
1341 cr_sel_eng_get_matched_rulesets (CRSelEng * a_this,
1342                                  CRStyleSheet * a_sheet,
1343                                  xmlNode * a_node,
1344                                  CRStatement *** a_rulesets, gulong * a_len)
1345 {
1346         CRStatement **stmts_tab = NULL;
1347         enum CRStatus status = CR_OK;
1348         gulong tab_size = 0,
1349                 tab_len = 0,
1350                 index = 0;
1351         gushort stmts_chunck_size = 8;
1352
1353         g_return_val_if_fail (a_this
1354                               && a_sheet
1355                               && a_node
1356                               && a_rulesets && *a_rulesets == NULL
1357                               && a_len, CR_BAD_PARAM_ERROR);
1358
1359         stmts_tab = g_try_malloc (stmts_chunck_size * sizeof (CRStatement *));
1360
1361         if (!stmts_tab) {
1362                 cr_utils_trace_info ("Out of memory");
1363                 status = CR_ERROR;
1364                 goto error;
1365         }
1366         memset (stmts_tab, 0, stmts_chunck_size * sizeof (CRStatement *));
1367
1368         tab_size = stmts_chunck_size;
1369         tab_len = tab_size;
1370
1371         while ((status = cr_sel_eng_get_matched_rulesets_real
1372                 (a_this, a_sheet, a_node, stmts_tab + index, &tab_len))
1373                == CR_OUTPUT_TOO_SHORT_ERROR) {
1374                 stmts_tab = g_try_realloc (stmts_tab,
1375                                            (tab_size + stmts_chunck_size)
1376                                            * sizeof (CRStatement *));
1377                 if (!stmts_tab) {
1378                         cr_utils_trace_info ("Out of memory");
1379                         status = CR_ERROR;
1380                         goto error;
1381                 }
1382                 tab_size += stmts_chunck_size;
1383                 index += tab_len;
1384                 tab_len = tab_size - index;
1385         }
1386
1387         tab_len = tab_size - stmts_chunck_size + tab_len;
1388         *a_rulesets = stmts_tab;
1389         *a_len = tab_len;
1390
1391         return CR_OK;
1392
1393       error:
1394
1395         if (stmts_tab) {
1396                 g_free (stmts_tab);
1397                 stmts_tab = NULL;
1398
1399         }
1400
1401         *a_len = 0;
1402         return status;
1403 }
1404
1405
1406 enum CRStatus
1407 cr_sel_eng_get_matched_properties_from_cascade (CRSelEng * a_this,
1408                                                 CRCascade * a_cascade,
1409                                                 xmlNode * a_node,
1410                                                 CRPropList ** a_props)
1411 {
1412         CRStatement **stmts_tab = NULL;
1413         enum CRStatus status = CR_OK;
1414         gulong tab_size = 0,
1415                 tab_len = 0,
1416                 i = 0,
1417                 index = 0;
1418         enum CRStyleOrigin origin = 0;
1419         gushort stmts_chunck_size = 8;
1420         CRStyleSheet *sheet = NULL;
1421
1422         g_return_val_if_fail (a_this
1423                               && a_cascade
1424                               && a_node && a_props, CR_BAD_PARAM_ERROR);
1425
1426         for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++) {
1427                 sheet = cr_cascade_get_sheet (a_cascade, origin);
1428                 if (!sheet)
1429                         continue;
1430                 if (tab_size - index < 1) {
1431                         stmts_tab = g_try_realloc
1432                                 (stmts_tab, (tab_size + stmts_chunck_size)
1433                                  * sizeof (CRStatement *));
1434                         if (!stmts_tab) {
1435                                 cr_utils_trace_info ("Out of memory");
1436                                 status = CR_ERROR;
1437                                 goto cleanup;
1438                         }
1439                         tab_size += stmts_chunck_size;
1440                         /*
1441                          *compute the max size left for
1442                          *cr_sel_eng_get_matched_rulesets_real()'s output tab 
1443                          */
1444                         tab_len = tab_size - index;
1445                 }
1446                 while ((status = cr_sel_eng_get_matched_rulesets_real
1447                         (a_this, sheet, a_node, stmts_tab + index, &tab_len))
1448                        == CR_OUTPUT_TOO_SHORT_ERROR) {
1449                         stmts_tab = g_try_realloc
1450                                 (stmts_tab, (tab_size + stmts_chunck_size)
1451                                  * sizeof (CRStatement *));
1452                         if (!stmts_tab) {
1453                                 cr_utils_trace_info ("Out of memory");
1454                                 status = CR_ERROR;
1455                                 goto cleanup;
1456                         }
1457                         tab_size += stmts_chunck_size;
1458                         index += tab_len;
1459                         /*
1460                          *compute the max size left for
1461                          *cr_sel_eng_get_matched_rulesets_real()'s output tab 
1462                          */
1463                         tab_len = tab_size - index;
1464                 }
1465                 if (status != CR_OK) {
1466                         cr_utils_trace_info ("Error while running "
1467                                              "selector engine");
1468                         goto cleanup;
1469                 }
1470                 index += tab_len;
1471                 tab_len = tab_size - index;
1472         }
1473
1474         /*
1475          *TODO, walk down the stmts_tab and build the
1476          *property_name/declaration hashtable.
1477          *Make sure one can walk from the declaration to
1478          *the stylesheet.
1479          */
1480         for (i = 0; i < index; i++) {
1481                 CRStatement *stmt = stmts_tab[i];
1482
1483                 if (!stmt)
1484                         continue;
1485                 switch (stmt->type) {
1486                 case RULESET_STMT:
1487                         if (!stmt->parent_sheet)
1488                                 continue;
1489                         status = put_css_properties_in_props_list
1490                                 (a_props, stmt);
1491                         break;
1492                 default:
1493                         break;
1494                 }
1495
1496         }
1497         status = CR_OK ;
1498  cleanup:
1499         if (stmts_tab) {
1500                 g_free (stmts_tab);
1501                 stmts_tab = NULL;
1502         }
1503
1504         return status;
1505 }
1506
1507 enum CRStatus
1508 cr_sel_eng_get_matched_style (CRSelEng * a_this,
1509                               CRCascade * a_cascade,
1510                               xmlNode * a_node,
1511                               CRStyle * a_parent_style, 
1512                               CRStyle ** a_style,
1513                               gboolean a_set_props_to_initial_values)
1514 {
1515         enum CRStatus status = CR_OK;
1516
1517         CRPropList *props = NULL;
1518
1519         g_return_val_if_fail (a_this && a_cascade
1520                               && a_node && a_style, CR_BAD_PARAM_ERROR);
1521
1522         status = cr_sel_eng_get_matched_properties_from_cascade
1523                 (a_this, a_cascade, a_node, &props);
1524
1525         g_return_val_if_fail (status == CR_OK, status);
1526         if (props) {
1527                 if (!*a_style) {
1528                         *a_style = cr_style_new (a_set_props_to_initial_values) ;
1529                         g_return_val_if_fail (*a_style, CR_ERROR);
1530                 } else {
1531                         if (a_set_props_to_initial_values == TRUE) {
1532                                 cr_style_set_props_to_initial_values (*a_style) ;
1533                         } else {
1534                                 cr_style_set_props_to_default_values (*a_style);
1535                         }
1536                 }
1537                 (*a_style)->parent_style = a_parent_style;
1538
1539                 set_style_from_props (*a_style, props);
1540                 if (props) {
1541                         cr_prop_list_destroy (props);
1542                         props = NULL;
1543                 }
1544         }
1545         return CR_OK;
1546 }
1547
1548 /**
1549  * cr_sel_eng_destroy:
1550  *@a_this: the current instance of the selection engine.
1551  *
1552  *The destructor of #CRSelEng
1553  */
1554 void
1555 cr_sel_eng_destroy (CRSelEng * a_this)
1556 {
1557         g_return_if_fail (a_this);
1558
1559         if (!PRIVATE (a_this))
1560                 goto end ;
1561         if (PRIVATE (a_this)->pcs_handlers) {
1562                 cr_sel_eng_unregister_all_pseudo_class_sel_handlers
1563                         (a_this) ;
1564                 PRIVATE (a_this)->pcs_handlers = NULL ;
1565         }
1566         g_free (PRIVATE (a_this));
1567         PRIVATE (a_this) = NULL;
1568  end:
1569         if (a_this) {
1570                 g_free (a_this);
1571         }
1572 }