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