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