• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/gettext-0.17/gettext-tools/gnulib-lib/libcroco/
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 2.1 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
39struct CRPseudoClassSelHandlerEntry {
40        guchar *name;
41        enum CRPseudoType type;
42        CRPseudoClassSelectorHandler handler;
43};
44
45struct _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
60static gboolean class_add_sel_matches_node (CRAdditionalSel * a_add_sel,
61                                            xmlNode * a_node);
62
63static gboolean id_add_sel_matches_node (CRAdditionalSel * a_add_sel,
64                                         xmlNode * a_node);
65
66static gboolean attr_add_sel_matches_node (CRAdditionalSel * a_add_sel,
67                                           xmlNode * a_node);
68
69static 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
76static 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
84static enum CRStatus put_css_properties_in_props_list (CRPropList ** a_props,
85                                                       CRStatement *
86                                                       a_ruleset);
87
88static gboolean pseudo_class_add_sel_matches_node (CRSelEng * a_this,
89                                                   CRAdditionalSel *
90                                                   a_add_sel,
91                                                   xmlNode * a_node);
92
93static gboolean lang_pseudo_class_handler (CRSelEng * a_this,
94                                           CRAdditionalSel * a_sel,
95                                           xmlNode * a_node);
96
97static gboolean first_child_pseudo_class_handler (CRSelEng * a_this,
98                                                  CRAdditionalSel * a_sel,
99                                                  xmlNode * a_node);
100
101static xmlNode *get_next_element_node (xmlNode * a_node);
102
103static xmlNode *get_next_child_element_node (xmlNode * a_node);
104
105static xmlNode *get_prev_element_node (xmlNode * a_node);
106
107static xmlNode *get_next_parent_element_node (xmlNode * a_node);
108
109static gboolean
110lang_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
152static gboolean
153first_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
179static gboolean
180pseudo_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 */
210static gboolean
211class_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 */
258static gboolean
259id_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 */
297static gboolean
298attr_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 */
476static gboolean
477additional_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
542static xmlNode *
543get_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
556static xmlNode *
557get_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
571static xmlNode *
572get_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
585static xmlNode *
586get_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 */
617static enum CRStatus
618sel_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 */
811static enum CRStatus
812cr_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
958static enum CRStatus
959put_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
1089static void
1090set_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 */
1113CRSelEng *
1114cr_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 */
1156enum CRStatus
1157cr_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
1187enum CRStatus
1188cr_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 */
1231enum CRStatus
1232cr_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
1258enum CRStatus
1259cr_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 */
1304enum CRStatus
1305cr_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 */
1340enum CRStatus
1341cr_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
1406enum CRStatus
1407cr_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
1507enum CRStatus
1508cr_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 */
1554void
1555cr_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}
1573