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