Libcroco
cr-sel-eng.c
Go to the documentation of this file.
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 
39  guchar *name;
42 };
43 
44 struct _CRSelEngPriv {
45  /*not used yet */
46  gboolean case_sensitive;
47 
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  */
55  GList *pcs_handlers;
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,
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
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");
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
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");
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
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 
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
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
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
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  */
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  */
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
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 *
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 *
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 *
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 *
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
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
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;
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
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  */
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
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
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)) {
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
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 *
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));
1140  (result, (guchar *) "first-child",
1142  first_child_pseudo_class_handler);
1144  (result, (guchar *) "lang",
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
1165  guchar * a_name,
1166  enum CRPseudoType a_type,
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
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)
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
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
1267  guchar * a_name,
1268  enum CRPseudoType a_type,
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)
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
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
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))
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
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))
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
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 
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) {
1540  } else {
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
1563 {
1564  g_return_if_fail (a_this);
1565 
1566  if (!PRIVATE (a_this))
1567  goto end ;
1568  if (PRIVATE (a_this)->pcs_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 }
CRSelector * sel_list
A list of instances of #CRSimpeSel.
Definition: cr-statement.h:66
enum CRStatus cr_sel_eng_matches_node(CRSelEng *a_this, CRSimpleSel *a_sel, xmlNode *a_node, gboolean *a_result)
cr_sel_eng_matches_node: @a_this: the selection engine.
Definition: cr-sel-eng.c:1312
A css2 media rule.
Definition: cr-statement.h:162
CRDeclaration * decl_list
A list of instances of CRDeclaration.
Definition: cr-statement.h:69
CRAtMediaRule * media_rule
Definition: cr-statement.h:193
union _CRStatement::@1 kind
CRStyleSheet * cr_cascade_get_sheet(CRCascade *a_this, enum CRStyleOrigin a_origin)
cr_cascade_get_sheet: @a_this: the current instance of CRCascade.
Definition: cr-cascade.c:114
enum CRStatus cr_style_set_style_from_decl(CRStyle *a_this, CRDeclaration *a_decl)
Walks through a css2 property declaration, and populated the according field(s) in the CRStyle struct...
Definition: cr-style.c:2012
CRPropList * cr_prop_list_append2(CRPropList *a_this, CRString *a_prop, CRDeclaration *a_decl)
cr_prop_list_append2: Appends a pair of prop/declaration to the current prop list.
Definition: cr-prop-list.c:108
enum AttrMatchWay match_way
Definition: cr-attr-sel.h:52
enum AddSelectorType type
enum Combinator combinator
The combinator that separates this simple selector from the previous one.
Definition: cr-simple-sel.h:84
CRStatement * statements
The css statements list.
Definition: cr-stylesheet.h:60
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)
Definition: cr-sel-eng.c:1515
An abstraction of a css stylesheet as defined by the css2 spec in chapter 4.
Definition: cr-stylesheet.h:57
CRSelEng * cr_sel_eng_new(void)
cr_sel_eng_new: Creates a new instance of CRSelEng.
Definition: cr-sel-eng.c:1121
enum CRPseudoType type
Definition: cr-pseudo.h:48
enum CRStatus cr_prop_list_lookup_prop(CRPropList *a_this, CRString *a_prop, CRPropList **a_pair)
cr_prop_list_lookup_prop: @a_this: the current instance of CRPropList @a_prop: the property to lookup...
Definition: cr-prop-list.c:267
CRSimpleSel * prev
typedefG_BEGIN_DECLS struct _CRSelector CRSelector
Definition: cr-selector.h:40
CRStatus
The status type returned by the methods of the croco library.
Definition: cr-utils.h:43
enum CRStatus cr_sel_eng_get_matched_properties_from_cascade(CRSelEng *a_this, CRCascade *a_cascade, xmlNode *a_node, CRPropList **a_props)
Definition: cr-sel-eng.c:1414
CRAdditionalSel * add_sel
The additional selector list of the current simple selector.
Definition: cr-simple-sel.h:94
enum CRStatementType type
The type of the statement.
Definition: cr-statement.h:187
enum CRStatus cr_sel_eng_get_matched_rulesets(CRSelEng *a_this, CRStyleSheet *a_sheet, xmlNode *a_node, CRStatement ***a_rulesets, gulong *a_len)
cr_sel_eng_get_matched_rulesets: @a_this: the current instance of the selection engine.
Definition: cr-sel-eng.c:1348
enum CRStyleOrigin origin
Definition: cr-stylesheet.h:62
CRString * value
Definition: cr-attr-sel.h:51
typedefG_BEGIN_DECLS struct _CRPropList CRPropList
Definition: cr-prop-list.h:31
CRSimpleSel * next
void cr_prop_list_destroy(CRPropList *a_this)
cr_prop_list_destroy: @a_this: the current instance of CRPropList
Definition: cr-prop-list.c:380
gboolean case_sensitive
Definition: cr-sel-eng.c:46
CRStyleOrigin
Definition: cr-stylesheet.h:38
The abstraction of a css2 simple selection list as defined by the right part of the "selector" produc...
Definition: cr-simple-sel.h:74
Definition: cr-utils.h:44
CRStatement * cur_stmt
where to store the next statement to be visited so that we can remember it from one method call to an...
Definition: cr-sel-eng.c:54
The abstraction of css statement as defined in the chapter 4 and appendix D.1 of the css2 spec.
Definition: cr-statement.h:182
CRPropList * cr_prop_list_get_next(CRPropList *a_this)
cr_prop_list_get_next: @a_this: the current instance of CRPropList
Definition: cr-prop-list.c:308
void cr_sel_eng_destroy(CRSelEng *a_this)
cr_sel_eng_destroy: @a_this: the current instance of the selection engine.
Definition: cr-sel-eng.c:1562
CRStyle * cr_style_new(gboolean a_set_props_to_initial_values)
Default constructor of CRStyle.
Definition: cr-style.c:1712
CRString * name
Definition: cr-pseudo.h:49
union CRAdditionalSelectorContent content
CRString * property
The property.
CRPropList * cr_prop_list_unlink(CRPropList *a_this, CRPropList *a_pair)
cr_prop_list_unlink: @a_this: the current list of prop/decl pairs @a_pair: the prop/decl pair to unli...
Definition: cr-prop-list.c:342
CRAttrSel * next
Definition: cr-attr-sel.h:53
enum CRStatus cr_sel_eng_get_pseudo_class_selector_handler(CRSelEng *a_this, guchar *a_name, enum CRPseudoType a_type, CRPseudoClassSelectorHandler *a_handler)
Definition: cr-sel-eng.c:1266
GList * pcs_handlers
Definition: cr-sel-eng.c:55
CRRuleSet * ruleset
Definition: cr-statement.h:191
CRStatement * rulesets
Definition: cr-statement.h:109
#define PRIVATE(a_this)
@CRSelEng:
Definition: cr-sel-eng.c:36
CRStatement * parent_statement
enum CRStatus cr_sel_eng_unregister_pseudo_class_sel_handler(CRSelEng *a_this, guchar *a_name, enum CRPseudoType a_type)
Definition: cr-sel-eng.c:1195
CRDeclaration * next
CRStyleSheet * sheet
Definition: cr-sel-eng.c:48
enum CRStatus cr_prop_list_get_decl(CRPropList const *a_this, CRDeclaration **a_decl)
cr_prop_list_get_decl: @a_this: the current instance of CRPropList @a_decl: out parameter.
Definition: cr-prop-list.c:244
enum SimpleSelectorType type_mask
Definition: cr-simple-sel.h:76
CRPseudoType
Definition: cr-pseudo.h:33
enum CRPseudoType type
Definition: cr-sel-eng.c:40
The declaration of the CRSelEng class.
gboolean(* CRPseudoClassSelectorHandler)(CRSelEng *a_this, CRAdditionalSel *a_add_sel, xmlNode *a_node)
Definition: cr-sel-eng.h:60
CRStyleSheet * parent_sheet
Definition: cr-statement.h:211
CRString * extra
Definition: cr-pseudo.h:50
gboolean important
gulong specificity
Definition: cr-statement.h:205
enum CRStatus cr_style_set_props_to_initial_values(CRStyle *a_this)
Sets the style properties to their initial value according to the css2 spec.
Definition: cr-style.c:1845
CRAdditionalSel * prev
gint pcs_handlers_size
Definition: cr-sel-eng.c:56
CRStatement * next
Definition: cr-statement.h:212
enum CRStatus cr_sel_eng_unregister_all_pseudo_class_sel_handlers(CRSelEng *a_this)
cr_sel_eng_unregister_all_pseudo_class_sel_handlers: @a_this: the current instance of CRSelEng .
Definition: cr-sel-eng.c:1239
enum CRStatus cr_style_set_props_to_default_values(CRStyle *a_this)
Sets the style properties to their default values according to the css2 spec i.e inherit if the prope...
Definition: cr-style.c:1740
A css2 import rule.
Definition: cr-statement.h:159
enum CRStatus cr_sel_eng_register_pseudo_class_sel_handler(CRSelEng *a_this, guchar *a_name, enum CRPseudoType a_type, CRPseudoClassSelectorHandler a_handler)
cr_sel_eng_register_pseudo_class_sel_handler: @a_this: the current instance of CRSelEng @a_pseudo_cla...
Definition: cr-sel-eng.c:1164
gboolean cr_utils_is_white_space(guint32 a_char)
Returns TRUE if a_char is a white space as defined in the css spec in chap 4.1.1.
Definition: cr-utils.c:1181
CRPseudoClassSelectorHandler handler
Definition: cr-sel-eng.c:41
#define strqcmp(str, lit, lit_len)
Definition: cr-sel-eng.c:109
CRString * name
Definition: cr-attr-sel.h:50
CRString * name
Definition: cr-simple-sel.h:78
enum CRStatus cr_simple_sel_compute_specificity(CRSimpleSel *a_this)
cr_simple_sel_compute_specificity:
#define cr_utils_trace_info(a_msg)
Traces an info message.
Definition: cr-utils.h:127
typedefG_BEGIN_DECLS struct _CRSelEng CRSelEng
Definition: cr-sel-eng.h:44
typedefG_BEGIN_DECLS struct _CRStyle CRStyle
Definition: cr-style.h:38