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