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