Libcroco
|
00001 /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */ 00002 00003 /* 00004 * This file is part of The Croco Library 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of version 2.1 of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser 00016 * General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 00019 * USA 00020 * 00021 * See COPYRIGHTS file for copyright informations. 00022 */ 00023 00024 #include <string.h> 00025 #include "cr-sel-eng.h" 00026 00027 /** 00028 *@CRSelEng: 00029 * 00030 *The definition of the #CRSelEng class. 00031 *The #CRSelEng is actually the "Selection Engine" 00032 *class. This is highly experimental for at the moment and 00033 *its api is very likely to change in a near future. 00034 */ 00035 00036 #define PRIVATE(a_this) (a_this)->priv 00037 00038 struct CRPseudoClassSelHandlerEntry { 00039 guchar *name; 00040 enum CRPseudoType type; 00041 CRPseudoClassSelectorHandler handler; 00042 }; 00043 00044 struct _CRSelEngPriv { 00045 /*not used yet */ 00046 gboolean case_sensitive; 00047 00048 CRStyleSheet *sheet; 00049 /** 00050 *where to store the next statement 00051 *to be visited so that we can remember 00052 *it from one method call to another. 00053 */ 00054 CRStatement *cur_stmt; 00055 GList *pcs_handlers; 00056 gint pcs_handlers_size; 00057 } ; 00058 00059 static gboolean class_add_sel_matches_node (CRAdditionalSel * a_add_sel, 00060 xmlNode * a_node); 00061 00062 static gboolean id_add_sel_matches_node (CRAdditionalSel * a_add_sel, 00063 xmlNode * a_node); 00064 00065 static gboolean attr_add_sel_matches_node (CRAdditionalSel * a_add_sel, 00066 xmlNode * a_node); 00067 00068 static enum CRStatus sel_matches_node_real (CRSelEng * a_this, 00069 CRSimpleSel * a_sel, 00070 xmlNode * a_node, 00071 gboolean * a_result, 00072 gboolean a_eval_sel_list_from_end, 00073 gboolean a_recurse); 00074 00075 static enum CRStatus cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this, 00076 CRStyleSheet * 00077 a_stylesheet, 00078 xmlNode * a_node, 00079 CRStatement ** 00080 a_rulesets, 00081 gulong * a_len); 00082 00083 static enum CRStatus put_css_properties_in_props_list (CRPropList ** a_props, 00084 CRStatement * 00085 a_ruleset); 00086 00087 static gboolean pseudo_class_add_sel_matches_node (CRSelEng * a_this, 00088 CRAdditionalSel * 00089 a_add_sel, 00090 xmlNode * a_node); 00091 00092 static gboolean lang_pseudo_class_handler (CRSelEng * a_this, 00093 CRAdditionalSel * a_sel, 00094 xmlNode * a_node); 00095 00096 static gboolean first_child_pseudo_class_handler (CRSelEng * a_this, 00097 CRAdditionalSel * a_sel, 00098 xmlNode * a_node); 00099 00100 static xmlNode *get_next_element_node (xmlNode * a_node); 00101 00102 static xmlNode *get_next_child_element_node (xmlNode * a_node); 00103 00104 static xmlNode *get_prev_element_node (xmlNode * a_node); 00105 00106 static xmlNode *get_next_parent_element_node (xmlNode * a_node); 00107 00108 /* Quick strcmp. Test only for == 0 or != 0, not < 0 or > 0. */ 00109 #define strqcmp(str,lit,lit_len) \ 00110 (strlen (str) != (lit_len) || memcmp (str, lit, lit_len)) 00111 00112 static gboolean 00113 lang_pseudo_class_handler (CRSelEng * a_this, 00114 CRAdditionalSel * a_sel, xmlNode * a_node) 00115 { 00116 xmlNode *node = a_node; 00117 xmlChar *val = NULL; 00118 gboolean result = FALSE; 00119 00120 g_return_val_if_fail (a_this && PRIVATE (a_this) 00121 && a_sel && a_sel->content.pseudo 00122 && a_sel->content.pseudo 00123 && a_sel->content.pseudo->name 00124 && a_sel->content.pseudo->name->stryng 00125 && a_node, CR_BAD_PARAM_ERROR); 00126 00127 if (strqcmp (a_sel->content.pseudo->name->stryng->str, 00128 "lang", 4) 00129 || a_sel->content.pseudo->type != FUNCTION_PSEUDO) { 00130 cr_utils_trace_info ("This handler is for :lang only"); 00131 return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR; 00132 } 00133 /*lang code should exist and be at least of length 2 */ 00134 if (!a_sel->content.pseudo->extra 00135 || !a_sel->content.pseudo->extra->stryng 00136 || a_sel->content.pseudo->extra->stryng->len < 2) 00137 return FALSE; 00138 for (; node; node = get_next_parent_element_node (node)) { 00139 val = xmlGetProp (node, (const xmlChar *) "lang"); 00140 if (val 00141 && !strqcmp ((const char *) val, 00142 a_sel->content.pseudo->extra->stryng->str, 00143 a_sel->content.pseudo->extra->stryng->len)) { 00144 result = TRUE; 00145 } 00146 if (val) { 00147 xmlFree (val); 00148 val = NULL; 00149 } 00150 } 00151 00152 return result; 00153 } 00154 00155 static gboolean 00156 first_child_pseudo_class_handler (CRSelEng * a_this, 00157 CRAdditionalSel * a_sel, xmlNode * a_node) 00158 { 00159 xmlNode *node = NULL; 00160 00161 g_return_val_if_fail (a_this && PRIVATE (a_this) 00162 && a_sel && a_sel->content.pseudo 00163 && a_sel->content.pseudo 00164 && a_sel->content.pseudo->name 00165 && a_sel->content.pseudo->name->stryng 00166 && a_node, CR_BAD_PARAM_ERROR); 00167 00168 if (strcmp (a_sel->content.pseudo->name->stryng->str, 00169 "first-child") 00170 || a_sel->content.pseudo->type != IDENT_PSEUDO) { 00171 cr_utils_trace_info ("This handler is for :first-child only"); 00172 return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR; 00173 } 00174 if (!a_node->parent) 00175 return FALSE; 00176 node = get_next_child_element_node (a_node->parent); 00177 if (node == a_node) 00178 return TRUE; 00179 return FALSE; 00180 } 00181 00182 static gboolean 00183 pseudo_class_add_sel_matches_node (CRSelEng * a_this, 00184 CRAdditionalSel * a_add_sel, 00185 xmlNode * a_node) 00186 { 00187 enum CRStatus status = CR_OK; 00188 CRPseudoClassSelectorHandler handler = NULL; 00189 00190 g_return_val_if_fail (a_this && PRIVATE (a_this) 00191 && a_add_sel 00192 && a_add_sel->content.pseudo 00193 && a_add_sel->content.pseudo->name 00194 && a_add_sel->content.pseudo->name->stryng 00195 && a_add_sel->content.pseudo->name->stryng->str 00196 && a_node, CR_BAD_PARAM_ERROR); 00197 00198 status = cr_sel_eng_get_pseudo_class_selector_handler 00199 (a_this, (guchar *) a_add_sel->content.pseudo->name->stryng->str, 00200 a_add_sel->content.pseudo->type, &handler); 00201 if (status != CR_OK || !handler) 00202 return FALSE; 00203 00204 return handler (a_this, a_add_sel, a_node); 00205 } 00206 00207 /** 00208 *@param a_add_sel the class additional selector to consider. 00209 *@param a_node the xml node to consider. 00210 *@return TRUE if the class additional selector matches 00211 *the xml node given in argument, FALSE otherwise. 00212 */ 00213 static gboolean 00214 class_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node) 00215 { 00216 gboolean result = FALSE; 00217 xmlChar *klass = NULL, 00218 *cur = NULL; 00219 00220 g_return_val_if_fail (a_add_sel 00221 && a_add_sel->type == CLASS_ADD_SELECTOR 00222 && a_add_sel->content.class_name 00223 && a_add_sel->content.class_name->stryng 00224 && a_add_sel->content.class_name->stryng->str 00225 && a_node, FALSE); 00226 00227 if (xmlHasProp (a_node, (const xmlChar *) "class")) { 00228 klass = xmlGetProp (a_node, (const xmlChar *) "class"); 00229 for (cur = klass; cur && *cur; cur++) { 00230 while (cur && *cur 00231 && cr_utils_is_white_space (*cur) 00232 == TRUE) 00233 cur++; 00234 00235 if (!strncmp ((const char *) cur, 00236 a_add_sel->content.class_name->stryng->str, 00237 a_add_sel->content.class_name->stryng->len)) { 00238 cur += a_add_sel->content.class_name->stryng->len; 00239 if ((cur && !*cur) 00240 || cr_utils_is_white_space (*cur) == TRUE) 00241 result = TRUE; 00242 } else { /* if it doesn't match, */ 00243 /* then skip to next whitespace character to try again */ 00244 while (cur && *cur && !(cr_utils_is_white_space(*cur) == TRUE)) 00245 cur++; 00246 } 00247 if (cur && !*cur) 00248 break ; 00249 } 00250 } 00251 if (klass) { 00252 xmlFree (klass); 00253 klass = NULL; 00254 } 00255 return result; 00256 00257 } 00258 00259 /** 00260 *@return TRUE if the additional attribute selector matches 00261 *the current xml node given in argument, FALSE otherwise. 00262 *@param a_add_sel the additional attribute selector to consider. 00263 *@param a_node the xml node to consider. 00264 */ 00265 static gboolean 00266 id_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node) 00267 { 00268 gboolean result = FALSE; 00269 xmlChar *id = NULL; 00270 00271 g_return_val_if_fail (a_add_sel 00272 && a_add_sel->type == ID_ADD_SELECTOR 00273 && a_add_sel->content.id_name 00274 && a_add_sel->content.id_name->stryng 00275 && a_add_sel->content.id_name->stryng->str 00276 && a_node, FALSE); 00277 g_return_val_if_fail (a_add_sel 00278 && a_add_sel->type == ID_ADD_SELECTOR 00279 && a_node, FALSE); 00280 00281 if (xmlHasProp (a_node, (const xmlChar *) "id")) { 00282 id = xmlGetProp (a_node, (const xmlChar *) "id"); 00283 if (!strqcmp ((const char *) id, a_add_sel->content.id_name->stryng->str, 00284 a_add_sel->content.id_name->stryng->len)) { 00285 result = TRUE; 00286 } 00287 } 00288 if (id) { 00289 xmlFree (id); 00290 id = NULL; 00291 } 00292 return result; 00293 } 00294 00295 /** 00296 *Returns TRUE if the instance of #CRAdditional selector matches 00297 *the node given in parameter, FALSE otherwise. 00298 *@param a_add_sel the additional selector to evaluate. 00299 *@param a_node the xml node against whitch the selector is to 00300 *be evaluated 00301 *return TRUE if the additional selector matches the current xml node 00302 *FALSE otherwise. 00303 */ 00304 static gboolean 00305 attr_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node) 00306 { 00307 CRAttrSel *cur_sel = NULL; 00308 00309 g_return_val_if_fail (a_add_sel 00310 && a_add_sel->type == ATTRIBUTE_ADD_SELECTOR 00311 && a_node, FALSE); 00312 00313 for (cur_sel = a_add_sel->content.attr_sel; 00314 cur_sel; cur_sel = cur_sel->next) { 00315 switch (cur_sel->match_way) { 00316 case SET: 00317 if (!cur_sel->name 00318 || !cur_sel->name->stryng 00319 || !cur_sel->name->stryng->str) 00320 return FALSE; 00321 00322 if (!xmlHasProp (a_node, 00323 (const xmlChar *) cur_sel->name->stryng->str)) 00324 return FALSE; 00325 break; 00326 00327 case EQUALS: 00328 { 00329 xmlChar *value = NULL; 00330 00331 if (!cur_sel->name 00332 || !cur_sel->name->stryng 00333 || !cur_sel->name->stryng->str 00334 || !cur_sel->value 00335 || !cur_sel->value->stryng 00336 || !cur_sel->value->stryng->str) 00337 return FALSE; 00338 00339 if (!xmlHasProp 00340 (a_node, 00341 (const xmlChar *) cur_sel->name->stryng->str)) 00342 return FALSE; 00343 00344 value = xmlGetProp 00345 (a_node, 00346 (const xmlChar *) cur_sel->name->stryng->str); 00347 00348 if (value 00349 && strcmp 00350 ((const char *) value, 00351 cur_sel->value->stryng->str)) { 00352 xmlFree (value); 00353 return FALSE; 00354 } 00355 xmlFree (value); 00356 } 00357 break; 00358 00359 case INCLUDES: 00360 { 00361 xmlChar *value = NULL, 00362 *ptr1 = NULL, 00363 *ptr2 = NULL, 00364 *cur = NULL; 00365 gboolean found = FALSE; 00366 00367 if (!xmlHasProp 00368 (a_node, 00369 (const xmlChar *) cur_sel->name->stryng->str)) 00370 return FALSE; 00371 value = xmlGetProp 00372 (a_node, 00373 (const xmlChar *) cur_sel->name->stryng->str); 00374 00375 if (!value) 00376 return FALSE; 00377 00378 /* 00379 *here, make sure value is a space 00380 *separated list of "words", where one 00381 *value is exactly cur_sel->value->str 00382 */ 00383 for (cur = value; *cur; cur++) { 00384 /* 00385 *set ptr1 to the first non white space 00386 *char addr. 00387 */ 00388 while (cr_utils_is_white_space 00389 (*cur) == TRUE && *cur) 00390 cur++; 00391 if (!*cur) 00392 break; 00393 ptr1 = cur; 00394 00395 /* 00396 *set ptr2 to the end the word. 00397 */ 00398 while (cr_utils_is_white_space 00399 (*cur) == FALSE && *cur) 00400 cur++; 00401 cur--; 00402 ptr2 = cur; 00403 00404 if (!strncmp 00405 ((const char *) ptr1, 00406 cur_sel->value->stryng->str, 00407 ptr2 - ptr1 + 1)) { 00408 found = TRUE; 00409 break; 00410 } 00411 ptr1 = ptr2 = NULL; 00412 } 00413 00414 if (found == FALSE) { 00415 xmlFree (value); 00416 return FALSE; 00417 } 00418 xmlFree (value); 00419 } 00420 break; 00421 00422 case DASHMATCH: 00423 { 00424 xmlChar *value = NULL, 00425 *ptr1 = NULL, 00426 *ptr2 = NULL, 00427 *cur = NULL; 00428 gboolean found = FALSE; 00429 00430 if (!xmlHasProp 00431 (a_node, 00432 (const xmlChar *) cur_sel->name->stryng->str)) 00433 return FALSE; 00434 value = xmlGetProp 00435 (a_node, 00436 (const xmlChar *) cur_sel->name->stryng->str); 00437 00438 /* 00439 *here, make sure value is an hyphen 00440 *separated list of "words", each of which 00441 *starting with "cur_sel->value->str" 00442 */ 00443 for (cur = value; *cur; cur++) { 00444 if (*cur == '-') 00445 cur++; 00446 ptr1 = cur; 00447 00448 while (*cur != '-' && *cur) 00449 cur++; 00450 cur--; 00451 ptr2 = cur; 00452 00453 if (g_strstr_len 00454 ((const gchar *) ptr1, ptr2 - ptr1 + 1, 00455 cur_sel->value->stryng->str) 00456 == (gchar *) ptr1) { 00457 found = TRUE; 00458 break; 00459 } 00460 } 00461 00462 if (found == FALSE) { 00463 xmlFree (value); 00464 return FALSE; 00465 } 00466 xmlFree (value); 00467 } 00468 break; 00469 default: 00470 return FALSE; 00471 } 00472 } 00473 00474 return TRUE; 00475 } 00476 00477 /** 00478 *Evaluates if a given additional selector matches an xml node. 00479 *@param a_add_sel the additional selector to consider. 00480 *@param a_node the xml node to consider. 00481 *@return TRUE is a_add_sel matches a_node, FALSE otherwise. 00482 */ 00483 static gboolean 00484 additional_selector_matches_node (CRSelEng * a_this, 00485 CRAdditionalSel * a_add_sel, 00486 xmlNode * a_node) 00487 { 00488 CRAdditionalSel *cur_add_sel = NULL, *tail = NULL ; 00489 gboolean evaluated = FALSE ; 00490 00491 for (tail = a_add_sel ; 00492 tail && tail->next; 00493 tail = tail->next) ; 00494 00495 g_return_val_if_fail (tail, FALSE) ; 00496 00497 for (cur_add_sel = tail ; 00498 cur_add_sel ; 00499 cur_add_sel = cur_add_sel->prev) { 00500 00501 evaluated = TRUE ; 00502 if (cur_add_sel->type == NO_ADD_SELECTOR) { 00503 return FALSE; 00504 } 00505 00506 if (cur_add_sel->type == CLASS_ADD_SELECTOR 00507 && cur_add_sel->content.class_name 00508 && cur_add_sel->content.class_name->stryng 00509 && cur_add_sel->content.class_name->stryng->str) { 00510 if (class_add_sel_matches_node (cur_add_sel, 00511 a_node) == FALSE) { 00512 return FALSE; 00513 } 00514 continue ; 00515 } else if (cur_add_sel->type == ID_ADD_SELECTOR 00516 && cur_add_sel->content.id_name 00517 && cur_add_sel->content.id_name->stryng 00518 && cur_add_sel->content.id_name->stryng->str) { 00519 if (id_add_sel_matches_node (cur_add_sel, a_node) == FALSE) { 00520 return FALSE; 00521 } 00522 continue ; 00523 } else if (cur_add_sel->type == ATTRIBUTE_ADD_SELECTOR 00524 && cur_add_sel->content.attr_sel) { 00525 /* 00526 *here, call a function that does the match 00527 *against an attribute additionnal selector 00528 *and an xml node. 00529 */ 00530 if (attr_add_sel_matches_node (cur_add_sel, a_node) 00531 == FALSE) { 00532 return FALSE; 00533 } 00534 continue ; 00535 } else if (cur_add_sel->type == PSEUDO_CLASS_ADD_SELECTOR 00536 && cur_add_sel->content.pseudo) { 00537 if (pseudo_class_add_sel_matches_node 00538 (a_this, cur_add_sel, a_node) == TRUE) { 00539 return TRUE; 00540 } 00541 return FALSE; 00542 } 00543 } 00544 if (evaluated == TRUE) 00545 return TRUE; 00546 return FALSE ; 00547 } 00548 00549 static xmlNode * 00550 get_next_element_node (xmlNode * a_node) 00551 { 00552 xmlNode *cur_node = NULL; 00553 00554 g_return_val_if_fail (a_node, NULL); 00555 00556 cur_node = a_node->next; 00557 while (cur_node && cur_node->type != XML_ELEMENT_NODE) { 00558 cur_node = cur_node->next; 00559 } 00560 return cur_node; 00561 } 00562 00563 static xmlNode * 00564 get_next_child_element_node (xmlNode * a_node) 00565 { 00566 xmlNode *cur_node = NULL; 00567 00568 g_return_val_if_fail (a_node, NULL); 00569 00570 cur_node = a_node->children; 00571 if (!cur_node) 00572 return cur_node; 00573 if (a_node->children->type == XML_ELEMENT_NODE) 00574 return a_node->children; 00575 return get_next_element_node (a_node->children); 00576 } 00577 00578 static xmlNode * 00579 get_prev_element_node (xmlNode * a_node) 00580 { 00581 xmlNode *cur_node = NULL; 00582 00583 g_return_val_if_fail (a_node, NULL); 00584 00585 cur_node = a_node->prev; 00586 while (cur_node && cur_node->type != XML_ELEMENT_NODE) { 00587 cur_node = cur_node->prev; 00588 } 00589 return cur_node; 00590 } 00591 00592 static xmlNode * 00593 get_next_parent_element_node (xmlNode * a_node) 00594 { 00595 xmlNode *cur_node = NULL; 00596 00597 g_return_val_if_fail (a_node, NULL); 00598 00599 cur_node = a_node->parent; 00600 while (cur_node && cur_node->type != XML_ELEMENT_NODE) { 00601 cur_node = cur_node->parent; 00602 } 00603 return cur_node; 00604 } 00605 00606 /** 00607 *Evaluate a selector (a simple selectors list) and says 00608 *if it matches the xml node given in parameter. 00609 *The algorithm used here is the following: 00610 *Walk the combinator separated list of simple selectors backward, starting 00611 *from the end of the list. For each simple selector, looks if 00612 *if matches the current node. 00613 * 00614 *@param a_this the selection engine. 00615 *@param a_sel the simple selection list. 00616 *@param a_node the xml node. 00617 *@param a_result out parameter. Set to true if the 00618 *selector matches the xml node, FALSE otherwise. 00619 *@param a_recurse if set to TRUE, the function will walk to 00620 *the next simple selector (after the evaluation of the current one) 00621 *and recursively evaluate it. Must be usually set to TRUE unless you 00622 *know what you are doing. 00623 */ 00624 static enum CRStatus 00625 sel_matches_node_real (CRSelEng * a_this, CRSimpleSel * a_sel, 00626 xmlNode * a_node, gboolean * a_result, 00627 gboolean a_eval_sel_list_from_end, 00628 gboolean a_recurse) 00629 { 00630 CRSimpleSel *cur_sel = NULL; 00631 xmlNode *cur_node = NULL; 00632 00633 g_return_val_if_fail (a_this && PRIVATE (a_this) 00634 && a_this && a_node 00635 && a_result, CR_BAD_PARAM_ERROR); 00636 00637 *a_result = FALSE; 00638 00639 if (a_node->type != XML_ELEMENT_NODE) 00640 return CR_OK; 00641 00642 if (a_eval_sel_list_from_end == TRUE) { 00643 /*go and get the last simple selector of the list */ 00644 for (cur_sel = a_sel; 00645 cur_sel && cur_sel->next; cur_sel = cur_sel->next) ; 00646 } else { 00647 cur_sel = a_sel; 00648 } 00649 00650 for (cur_node = a_node; cur_sel; cur_sel = cur_sel->prev) { 00651 if (((cur_sel->type_mask & TYPE_SELECTOR) 00652 && (cur_sel->name 00653 && cur_sel->name->stryng 00654 && cur_sel->name->stryng->str) 00655 && (!strcmp (cur_sel->name->stryng->str, 00656 (const char *) cur_node->name))) 00657 || (cur_sel->type_mask & UNIVERSAL_SELECTOR)) { 00658 /* 00659 *this simple selector 00660 *matches the current xml node 00661 *Let's see if the preceding 00662 *simple selectors also match 00663 *their xml node counterpart. 00664 */ 00665 if (cur_sel->add_sel) { 00666 if (additional_selector_matches_node (a_this, cur_sel->add_sel, 00667 cur_node) == TRUE) { 00668 goto walk_a_step_in_expr; 00669 } else { 00670 goto done; 00671 } 00672 } else { 00673 goto walk_a_step_in_expr; 00674 } 00675 } 00676 if (!(cur_sel->type_mask & TYPE_SELECTOR) 00677 && !(cur_sel->type_mask & UNIVERSAL_SELECTOR)) { 00678 if (!cur_sel->add_sel) { 00679 goto done; 00680 } 00681 if (additional_selector_matches_node 00682 (a_this, cur_sel->add_sel, cur_node) 00683 == TRUE) { 00684 goto walk_a_step_in_expr; 00685 } else { 00686 goto done; 00687 } 00688 } else { 00689 goto done ; 00690 } 00691 00692 walk_a_step_in_expr: 00693 if (a_recurse == FALSE) { 00694 *a_result = TRUE; 00695 goto done; 00696 } 00697 00698 /* 00699 *here, depending on the combinator of cur_sel 00700 *choose the axis of the xml tree traversal 00701 *and walk one step in the xml tree. 00702 */ 00703 if (!cur_sel->prev) 00704 break; 00705 00706 switch (cur_sel->combinator) { 00707 case NO_COMBINATOR: 00708 break; 00709 00710 case COMB_WS: /*descendant selector */ 00711 { 00712 xmlNode *n = NULL; 00713 enum CRStatus status = CR_OK; 00714 gboolean matches = FALSE; 00715 00716 /* 00717 *walk the xml tree upward looking for a parent 00718 *node that matches the preceding selector. 00719 */ 00720 for (n = cur_node->parent; n; n = n->parent) { 00721 status = sel_matches_node_real 00722 (a_this, cur_sel->prev, 00723 n, &matches, FALSE, TRUE); 00724 00725 if (status != CR_OK) 00726 goto done; 00727 00728 if (matches == TRUE) { 00729 cur_node = n ; 00730 break; 00731 } 00732 } 00733 00734 if (!n) { 00735 /* 00736 *didn't find any ancestor that matches 00737 *the previous simple selector. 00738 */ 00739 goto done; 00740 } 00741 /* 00742 *in this case, the preceding simple sel 00743 *will have been interpreted twice, which 00744 *is a cpu and mem waste ... I need to find 00745 *another way to do this. Anyway, this is 00746 *my first attempt to write this function and 00747 *I am a bit clueless. 00748 */ 00749 break; 00750 } 00751 00752 case COMB_PLUS: 00753 cur_node = get_prev_element_node (cur_node); 00754 if (!cur_node) 00755 goto done; 00756 break; 00757 00758 case COMB_GT: 00759 cur_node = get_next_parent_element_node (cur_node); 00760 if (!cur_node) 00761 goto done; 00762 break; 00763 00764 default: 00765 goto done; 00766 } 00767 continue; 00768 } 00769 00770 /* 00771 *if we reached this point, it means the selector matches 00772 *the xml node. 00773 */ 00774 *a_result = TRUE; 00775 00776 done: 00777 return CR_OK; 00778 } 00779 00780 00781 /** 00782 *Returns array of the ruleset statements that matches the 00783 *given xml node. 00784 *The engine keeps in memory the last statement he 00785 *visited during the match. So, the next call 00786 *to this function will eventually return a rulesets list starting 00787 *from the last ruleset statement visited during the previous call. 00788 *The enable users to get matching rulesets in an incremental way. 00789 *Note that for each statement returned, 00790 *the engine calculates the specificity of the selector 00791 *that matched the xml node and stores it in the "specifity" field 00792 *of the statement structure. 00793 * 00794 *@param a_sel_eng the current selection engine 00795 *@param a_node the xml node for which the request 00796 *is being made. 00797 *@param a_sel_list the list of selectors to perform the search in. 00798 *@param a_rulesets in/out parameter. A pointer to the 00799 *returned array of rulesets statements that match the xml node 00800 *given in parameter. The caller allocates the array before calling this 00801 *function. 00802 *@param a_len in/out parameter the length (in sizeof (#CRStatement*)) 00803 *of the returned array. 00804 *(the length of a_rulesets, more precisely). 00805 *The caller must set it to the length of a_ruleset prior to calling this 00806 *function. In return, the function sets it to the length 00807 *(in sizeof (#CRStatement)) of the actually returned CRStatement array. 00808 *@return CR_OUTPUT_TOO_SHORT_ERROR if found more rulesets than the size 00809 *of the a_rulesets array. In this case, the first *a_len rulesets found 00810 *are put in a_rulesets, and a further call will return the following 00811 *ruleset(s) following the same principle. 00812 *@return CR_OK if all the rulesets found have been returned. In this 00813 *case, *a_len is set to the actual number of ruleset found. 00814 *@return CR_BAD_PARAM_ERROR in case any of the given parameter are 00815 *bad (e.g null pointer). 00816 *@return CR_ERROR if any other error occurred. 00817 */ 00818 static enum CRStatus 00819 cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this, 00820 CRStyleSheet * a_stylesheet, 00821 xmlNode * a_node, 00822 CRStatement ** a_rulesets, 00823 gulong * a_len) 00824 { 00825 CRStatement *cur_stmt = NULL; 00826 CRSelector *sel_list = NULL, 00827 *cur_sel = NULL; 00828 gboolean matches = FALSE; 00829 enum CRStatus status = CR_OK; 00830 gulong i = 0; 00831 00832 g_return_val_if_fail (a_this 00833 && a_stylesheet 00834 && a_node && a_rulesets, CR_BAD_PARAM_ERROR); 00835 00836 if (!a_stylesheet->statements) { 00837 *a_rulesets = NULL; 00838 *a_len = 0; 00839 return CR_OK; 00840 } 00841 00842 /* 00843 *if this stylesheet is "new one" 00844 *let's remember it for subsequent calls. 00845 */ 00846 if (PRIVATE (a_this)->sheet != a_stylesheet) { 00847 PRIVATE (a_this)->sheet = a_stylesheet; 00848 PRIVATE (a_this)->cur_stmt = a_stylesheet->statements; 00849 } 00850 00851 /* 00852 *walk through the list of statements and, 00853 *get the selectors list inside the statements that 00854 *contain some, and try to match our xml node in these 00855 *selectors lists. 00856 */ 00857 for (cur_stmt = PRIVATE (a_this)->cur_stmt, i = 0; 00858 (PRIVATE (a_this)->cur_stmt = cur_stmt); 00859 cur_stmt = cur_stmt->next) { 00860 /* 00861 *initialyze the selector list in which we will 00862 *really perform the search. 00863 */ 00864 sel_list = NULL; 00865 00866 /* 00867 *get the the damn selector list in 00868 *which we have to look 00869 */ 00870 switch (cur_stmt->type) { 00871 case RULESET_STMT: 00872 if (cur_stmt->kind.ruleset 00873 && cur_stmt->kind.ruleset->sel_list) { 00874 sel_list = cur_stmt->kind.ruleset->sel_list; 00875 } 00876 break; 00877 00878 case AT_MEDIA_RULE_STMT: 00879 if (cur_stmt->kind.media_rule 00880 && cur_stmt->kind.media_rule->rulesets 00881 && cur_stmt->kind.media_rule->rulesets-> 00882 kind.ruleset 00883 && cur_stmt->kind.media_rule->rulesets-> 00884 kind.ruleset->sel_list) { 00885 sel_list = 00886 cur_stmt->kind.media_rule-> 00887 rulesets->kind.ruleset->sel_list; 00888 } 00889 break; 00890 00891 case AT_IMPORT_RULE_STMT: 00892 /* 00893 *some recursivity may be needed here. 00894 *I don't like this :( 00895 */ 00896 break; 00897 default: 00898 break; 00899 } 00900 00901 if (!sel_list) 00902 continue; 00903 00904 /* 00905 *now, we have a comma separated selector list to look in. 00906 *let's walk it and try to match the xml_node 00907 *on each item of the list. 00908 */ 00909 for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next) { 00910 if (!cur_sel->simple_sel) 00911 continue; 00912 00913 status = cr_sel_eng_matches_node 00914 (a_this, cur_sel->simple_sel, 00915 a_node, &matches); 00916 00917 if (status == CR_OK && matches == TRUE) { 00918 /* 00919 *bingo!!! we found one ruleset that 00920 *matches that fucking node. 00921 *lets put it in the out array. 00922 */ 00923 00924 if (i < *a_len) { 00925 a_rulesets[i] = cur_stmt; 00926 i++; 00927 00928 /* 00929 *For the cascade computing algorithm 00930 *(which is gonna take place later) 00931 *we must compute the specificity 00932 *(css2 spec chap 6.4.1) of the selector 00933 *that matched the current xml node 00934 *and store it in the css2 statement 00935 *(statement == ruleset here). 00936 */ 00937 status = cr_simple_sel_compute_specificity (cur_sel->simple_sel); 00938 00939 g_return_val_if_fail (status == CR_OK, 00940 CR_ERROR); 00941 cur_stmt->specificity = 00942 cur_sel->simple_sel-> 00943 specificity; 00944 } else 00945 { 00946 *a_len = i; 00947 return CR_OUTPUT_TOO_SHORT_ERROR; 00948 } 00949 } 00950 } 00951 } 00952 00953 /* 00954 *if we reached this point, it means 00955 *we reached the end of stylesheet. 00956 *no need to store any info about the stylesheet 00957 *anymore. 00958 */ 00959 g_return_val_if_fail (!PRIVATE (a_this)->cur_stmt, CR_ERROR); 00960 PRIVATE (a_this)->sheet = NULL; 00961 *a_len = i; 00962 return CR_OK; 00963 } 00964 00965 static enum CRStatus 00966 put_css_properties_in_props_list (CRPropList ** a_props, CRStatement * a_stmt) 00967 { 00968 CRPropList *props = NULL, 00969 *pair = NULL, 00970 *tmp_props = NULL; 00971 CRDeclaration *cur_decl = NULL; 00972 00973 g_return_val_if_fail (a_props && a_stmt 00974 && a_stmt->type == RULESET_STMT 00975 && a_stmt->kind.ruleset, CR_BAD_PARAM_ERROR); 00976 00977 props = *a_props; 00978 00979 for (cur_decl = a_stmt->kind.ruleset->decl_list; 00980 cur_decl; cur_decl = cur_decl->next) { 00981 CRDeclaration *decl; 00982 00983 decl = NULL; 00984 pair = NULL; 00985 00986 if (!cur_decl->property 00987 || !cur_decl->property->stryng 00988 || !cur_decl->property->stryng->str) 00989 continue; 00990 /* 00991 *First, test if the property is not 00992 *already present in our properties list 00993 *If yes, apply the cascading rules to 00994 *compute the precedence. If not, insert 00995 *the property into the list 00996 */ 00997 cr_prop_list_lookup_prop (props, 00998 cur_decl->property, 00999 &pair); 01000 01001 if (!pair) { 01002 tmp_props = cr_prop_list_append2 01003 (props, cur_decl->property, cur_decl); 01004 if (tmp_props) { 01005 props = tmp_props; 01006 tmp_props = NULL; 01007 } 01008 continue; 01009 } 01010 01011 /* 01012 *A property with the same name already exists. 01013 *We must apply here 01014 *some cascading rules 01015 *to compute the precedence. 01016 */ 01017 cr_prop_list_get_decl (pair, &decl); 01018 g_return_val_if_fail (decl, CR_ERROR); 01019 01020 /* 01021 *first, look at the origin. 01022 *6.4.1 says: 01023 *"for normal declarations, 01024 *author style sheets override user 01025 *style sheets which override 01026 *the default style sheet." 01027 */ 01028 if (decl->parent_statement 01029 && decl->parent_statement->parent_sheet 01030 && (decl->parent_statement->parent_sheet->origin 01031 < a_stmt->parent_sheet->origin)) { 01032 /* 01033 *if the already selected declaration 01034 *is marked as being !important the current 01035 *declaration must not overide it 01036 *(unless the already selected declaration 01037 *has an UA origin) 01038 */ 01039 if (decl->important == TRUE 01040 && decl->parent_statement->parent_sheet->origin 01041 != ORIGIN_UA) { 01042 continue; 01043 } 01044 tmp_props = cr_prop_list_unlink (props, pair); 01045 if (props) { 01046 cr_prop_list_destroy (pair); 01047 } 01048 props = tmp_props; 01049 tmp_props = NULL; 01050 props = cr_prop_list_append2 01051 (props, cur_decl->property, cur_decl); 01052 01053 continue; 01054 } else if (decl->parent_statement 01055 && decl->parent_statement->parent_sheet 01056 && (decl->parent_statement-> 01057 parent_sheet->origin 01058 > a_stmt->parent_sheet->origin)) { 01059 cr_utils_trace_info 01060 ("We should not reach this line\n"); 01061 continue; 01062 } 01063 01064 /* 01065 *A property with the same 01066 *name and the same origin already exists. 01067 *shit. This is lasting longer than expected ... 01068 *Luckily, the spec says in 6.4.1: 01069 *"more specific selectors will override 01070 *more general ones" 01071 *and 01072 *"if two rules have the same weight, 01073 *origin and specificity, 01074 *the later specified wins" 01075 */ 01076 if (a_stmt->specificity 01077 >= decl->parent_statement->specificity) { 01078 if (decl->important == TRUE) 01079 continue; 01080 props = cr_prop_list_unlink (props, pair); 01081 if (pair) { 01082 cr_prop_list_destroy (pair); 01083 pair = NULL; 01084 } 01085 props = cr_prop_list_append2 (props, 01086 cur_decl->property, 01087 cur_decl); 01088 } 01089 } 01090 /*TODO: this may leak. Check this out */ 01091 *a_props = props; 01092 01093 return CR_OK; 01094 } 01095 01096 static void 01097 set_style_from_props (CRStyle * a_style, CRPropList * a_props) 01098 { 01099 CRPropList *cur = NULL; 01100 CRDeclaration *decl = NULL; 01101 01102 for (cur = a_props; cur; cur = cr_prop_list_get_next (cur)) { 01103 cr_prop_list_get_decl (cur, &decl); 01104 cr_style_set_style_from_decl (a_style, decl); 01105 decl = NULL; 01106 } 01107 } 01108 01109 /**************************************** 01110 *PUBLIC METHODS 01111 ****************************************/ 01112 01113 /** 01114 * cr_sel_eng_new: 01115 *Creates a new instance of #CRSelEng. 01116 * 01117 *Returns the newly built instance of #CRSelEng of 01118 *NULL if an error occurs. 01119 */ 01120 CRSelEng * 01121 cr_sel_eng_new (void) 01122 { 01123 CRSelEng *result = NULL; 01124 01125 result = g_try_malloc (sizeof (CRSelEng)); 01126 if (!result) { 01127 cr_utils_trace_info ("Out of memory"); 01128 return NULL; 01129 } 01130 memset (result, 0, sizeof (CRSelEng)); 01131 01132 PRIVATE (result) = g_try_malloc (sizeof (CRSelEngPriv)); 01133 if (!PRIVATE (result)) { 01134 cr_utils_trace_info ("Out of memory"); 01135 g_free (result); 01136 return NULL; 01137 } 01138 memset (PRIVATE (result), 0, sizeof (CRSelEngPriv)); 01139 cr_sel_eng_register_pseudo_class_sel_handler 01140 (result, (guchar *) "first-child", 01141 IDENT_PSEUDO, (CRPseudoClassSelectorHandler) 01142 first_child_pseudo_class_handler); 01143 cr_sel_eng_register_pseudo_class_sel_handler 01144 (result, (guchar *) "lang", 01145 FUNCTION_PSEUDO, (CRPseudoClassSelectorHandler) 01146 lang_pseudo_class_handler); 01147 01148 return result; 01149 } 01150 01151 /** 01152 * cr_sel_eng_register_pseudo_class_sel_handler: 01153 *@a_this: the current instance of #CRSelEng 01154 *@a_pseudo_class_sel_name: the name of the pseudo class selector. 01155 *@a_pseudo_class_type: the type of the pseudo class selector. 01156 *@a_handler: the actual handler or callback to be called during 01157 *the selector evaluation process. 01158 * 01159 *Adds a new handler entry in the handlers entry table. 01160 * 01161 *Returns CR_OK, upon successful completion, an error code otherwise. 01162 */ 01163 enum CRStatus 01164 cr_sel_eng_register_pseudo_class_sel_handler (CRSelEng * a_this, 01165 guchar * a_name, 01166 enum CRPseudoType a_type, 01167 CRPseudoClassSelectorHandler 01168 a_handler) 01169 { 01170 struct CRPseudoClassSelHandlerEntry *handler_entry = NULL; 01171 GList *list = NULL; 01172 01173 g_return_val_if_fail (a_this && PRIVATE (a_this) 01174 && a_handler && a_name, CR_BAD_PARAM_ERROR); 01175 01176 handler_entry = g_try_malloc 01177 (sizeof (struct CRPseudoClassSelHandlerEntry)); 01178 if (!handler_entry) { 01179 return CR_OUT_OF_MEMORY_ERROR; 01180 } 01181 memset (handler_entry, 0, 01182 sizeof (struct CRPseudoClassSelHandlerEntry)); 01183 handler_entry->name = (guchar *) g_strdup ((const gchar *) a_name); 01184 handler_entry->type = a_type; 01185 handler_entry->handler = a_handler; 01186 list = g_list_append (PRIVATE (a_this)->pcs_handlers, handler_entry); 01187 if (!list) { 01188 return CR_OUT_OF_MEMORY_ERROR; 01189 } 01190 PRIVATE (a_this)->pcs_handlers = list; 01191 return CR_OK; 01192 } 01193 01194 enum CRStatus 01195 cr_sel_eng_unregister_pseudo_class_sel_handler (CRSelEng * a_this, 01196 guchar * a_name, 01197 enum CRPseudoType a_type) 01198 { 01199 GList *elem = NULL, 01200 *deleted_elem = NULL; 01201 gboolean found = FALSE; 01202 struct CRPseudoClassSelHandlerEntry *entry = NULL; 01203 01204 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR); 01205 01206 for (elem = PRIVATE (a_this)->pcs_handlers; 01207 elem; elem = g_list_next (elem)) { 01208 entry = elem->data; 01209 if (!strcmp ((const char *) entry->name, (const char *) a_name) 01210 && entry->type == a_type) { 01211 found = TRUE; 01212 break; 01213 } 01214 } 01215 if (found == FALSE) 01216 return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR; 01217 PRIVATE (a_this)->pcs_handlers = g_list_delete_link 01218 (PRIVATE (a_this)->pcs_handlers, elem); 01219 entry = elem->data; 01220 if (entry->name) 01221 g_free (entry->name); 01222 g_free (elem); 01223 g_list_free (deleted_elem); 01224 01225 return CR_OK; 01226 } 01227 01228 /** 01229 * cr_sel_eng_unregister_all_pseudo_class_sel_handlers: 01230 *@a_this: the current instance of #CRSelEng . 01231 * 01232 *Unregisters all the pseudo class sel handlers 01233 *and frees all the associated allocated datastructures. 01234 * 01235 *Returns CR_OK upon succesful completion, an error code 01236 *otherwise. 01237 */ 01238 enum CRStatus 01239 cr_sel_eng_unregister_all_pseudo_class_sel_handlers (CRSelEng * a_this) 01240 { 01241 GList *elem = NULL; 01242 struct CRPseudoClassSelHandlerEntry *entry = NULL; 01243 01244 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR); 01245 01246 if (!PRIVATE (a_this)->pcs_handlers) 01247 return CR_OK; 01248 for (elem = PRIVATE (a_this)->pcs_handlers; 01249 elem; elem = g_list_next (elem)) { 01250 entry = elem->data; 01251 if (!entry) 01252 continue; 01253 if (entry->name) { 01254 g_free (entry->name); 01255 entry->name = NULL; 01256 } 01257 g_free (entry); 01258 elem->data = NULL; 01259 } 01260 g_list_free (PRIVATE (a_this)->pcs_handlers); 01261 PRIVATE (a_this)->pcs_handlers = NULL; 01262 return CR_OK; 01263 } 01264 01265 enum CRStatus 01266 cr_sel_eng_get_pseudo_class_selector_handler (CRSelEng * a_this, 01267 guchar * a_name, 01268 enum CRPseudoType a_type, 01269 CRPseudoClassSelectorHandler * 01270 a_handler) 01271 { 01272 GList *elem = NULL; 01273 struct CRPseudoClassSelHandlerEntry *entry = NULL; 01274 gboolean found = FALSE; 01275 01276 g_return_val_if_fail (a_this && PRIVATE (a_this) 01277 && a_name, CR_BAD_PARAM_ERROR); 01278 01279 for (elem = PRIVATE (a_this)->pcs_handlers; 01280 elem; elem = g_list_next (elem)) { 01281 entry = elem->data; 01282 if (!strcmp ((const char *) a_name, (const char *) entry->name) 01283 && entry->type == a_type) { 01284 found = TRUE; 01285 break; 01286 } 01287 } 01288 01289 if (found == FALSE) 01290 return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR; 01291 *a_handler = entry->handler; 01292 return CR_OK; 01293 } 01294 01295 /** 01296 * cr_sel_eng_matches_node: 01297 *@a_this: the selection engine. 01298 *@a_sel: the simple selector against which the xml node 01299 *is going to be matched. 01300 *@a_node: the node against which the selector is going to be matched. 01301 *@a_result: out parameter. The result of the match. Is set to 01302 *TRUE if the selector matches the node, FALSE otherwise. This value 01303 *is considered if and only if this functions returns CR_OK. 01304 * 01305 *Evaluates a chained list of simple selectors (known as a css2 selector). 01306 *Says wheter if this selector matches the xml node given in parameter or 01307 *not. 01308 * 01309 *Returns the CR_OK if the selection ran correctly, an error code otherwise. 01310 */ 01311 enum CRStatus 01312 cr_sel_eng_matches_node (CRSelEng * a_this, CRSimpleSel * a_sel, 01313 xmlNode * a_node, gboolean * a_result) 01314 { 01315 g_return_val_if_fail (a_this && PRIVATE (a_this) 01316 && a_this && a_node 01317 && a_result, CR_BAD_PARAM_ERROR); 01318 01319 if (a_node->type != XML_ELEMENT_NODE) { 01320 *a_result = FALSE; 01321 return CR_OK; 01322 } 01323 01324 return sel_matches_node_real (a_this, a_sel, 01325 a_node, a_result, 01326 TRUE, TRUE); 01327 } 01328 01329 /** 01330 * cr_sel_eng_get_matched_rulesets: 01331 *@a_this: the current instance of the selection engine. 01332 *@a_sheet: the stylesheet that holds the selectors. 01333 *@a_node: the xml node to consider during the walk thru 01334 *the stylesheet. 01335 *@a_rulesets: out parameter. A pointer to an array of 01336 *rulesets statement pointers. *a_rulesets is allocated by 01337 *this function and must be freed by the caller. However, the caller 01338 *must not alter the rulesets statements pointer because they 01339 *point to statements that are still in the css stylesheet. 01340 *@a_len: the length of *a_ruleset. 01341 * 01342 *Returns an array of pointers to selectors that matches 01343 *the xml node given in parameter. 01344 * 01345 *Returns CR_OK upon sucessfull completion, an error code otherwise. 01346 */ 01347 enum CRStatus 01348 cr_sel_eng_get_matched_rulesets (CRSelEng * a_this, 01349 CRStyleSheet * a_sheet, 01350 xmlNode * a_node, 01351 CRStatement *** a_rulesets, gulong * a_len) 01352 { 01353 CRStatement **stmts_tab = NULL; 01354 enum CRStatus status = CR_OK; 01355 gulong tab_size = 0, 01356 tab_len = 0, 01357 index = 0; 01358 gushort stmts_chunck_size = 8; 01359 01360 g_return_val_if_fail (a_this 01361 && a_sheet 01362 && a_node 01363 && a_rulesets && *a_rulesets == NULL 01364 && a_len, CR_BAD_PARAM_ERROR); 01365 01366 stmts_tab = g_try_malloc (stmts_chunck_size * sizeof (CRStatement *)); 01367 01368 if (!stmts_tab) { 01369 cr_utils_trace_info ("Out of memory"); 01370 status = CR_ERROR; 01371 goto error; 01372 } 01373 memset (stmts_tab, 0, stmts_chunck_size * sizeof (CRStatement *)); 01374 01375 tab_size = stmts_chunck_size; 01376 tab_len = tab_size; 01377 01378 while ((status = cr_sel_eng_get_matched_rulesets_real 01379 (a_this, a_sheet, a_node, stmts_tab + index, &tab_len)) 01380 == CR_OUTPUT_TOO_SHORT_ERROR) { 01381 stmts_tab = g_try_realloc (stmts_tab, 01382 (tab_size + stmts_chunck_size) 01383 * sizeof (CRStatement *)); 01384 if (!stmts_tab) { 01385 cr_utils_trace_info ("Out of memory"); 01386 status = CR_ERROR; 01387 goto error; 01388 } 01389 tab_size += stmts_chunck_size; 01390 index += tab_len; 01391 tab_len = tab_size - index; 01392 } 01393 01394 tab_len = tab_size - stmts_chunck_size + tab_len; 01395 *a_rulesets = stmts_tab; 01396 *a_len = tab_len; 01397 01398 return CR_OK; 01399 01400 error: 01401 01402 if (stmts_tab) { 01403 g_free (stmts_tab); 01404 stmts_tab = NULL; 01405 01406 } 01407 01408 *a_len = 0; 01409 return status; 01410 } 01411 01412 01413 enum CRStatus 01414 cr_sel_eng_get_matched_properties_from_cascade (CRSelEng * a_this, 01415 CRCascade * a_cascade, 01416 xmlNode * a_node, 01417 CRPropList ** a_props) 01418 { 01419 CRStatement **stmts_tab = NULL; 01420 enum CRStatus status = CR_OK; 01421 gulong tab_size = 0, 01422 tab_len = 0, 01423 i = 0, 01424 index = 0; 01425 enum CRStyleOrigin origin = 0; 01426 gushort stmts_chunck_size = 8; 01427 CRStyleSheet *sheet = NULL; 01428 01429 g_return_val_if_fail (a_this 01430 && a_cascade 01431 && a_node && a_props, CR_BAD_PARAM_ERROR); 01432 01433 for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++) { 01434 sheet = cr_cascade_get_sheet (a_cascade, origin); 01435 if (!sheet) 01436 continue; 01437 if (tab_size - index < 1) { 01438 stmts_tab = g_try_realloc 01439 (stmts_tab, (tab_size + stmts_chunck_size) 01440 * sizeof (CRStatement *)); 01441 if (!stmts_tab) { 01442 cr_utils_trace_info ("Out of memory"); 01443 status = CR_ERROR; 01444 goto cleanup; 01445 } 01446 tab_size += stmts_chunck_size; 01447 /* 01448 *compute the max size left for 01449 *cr_sel_eng_get_matched_rulesets_real()'s output tab 01450 */ 01451 tab_len = tab_size - index; 01452 } 01453 while ((status = cr_sel_eng_get_matched_rulesets_real 01454 (a_this, sheet, a_node, stmts_tab + index, &tab_len)) 01455 == CR_OUTPUT_TOO_SHORT_ERROR) { 01456 stmts_tab = g_try_realloc 01457 (stmts_tab, (tab_size + stmts_chunck_size) 01458 * sizeof (CRStatement *)); 01459 if (!stmts_tab) { 01460 cr_utils_trace_info ("Out of memory"); 01461 status = CR_ERROR; 01462 goto cleanup; 01463 } 01464 tab_size += stmts_chunck_size; 01465 index += tab_len; 01466 /* 01467 *compute the max size left for 01468 *cr_sel_eng_get_matched_rulesets_real()'s output tab 01469 */ 01470 tab_len = tab_size - index; 01471 } 01472 if (status != CR_OK) { 01473 cr_utils_trace_info ("Error while running " 01474 "selector engine"); 01475 goto cleanup; 01476 } 01477 index += tab_len; 01478 tab_len = tab_size - index; 01479 } 01480 01481 /* 01482 *TODO, walk down the stmts_tab and build the 01483 *property_name/declaration hashtable. 01484 *Make sure one can walk from the declaration to 01485 *the stylesheet. 01486 */ 01487 for (i = 0; i < index; i++) { 01488 CRStatement *stmt = stmts_tab[i]; 01489 01490 if (!stmt) 01491 continue; 01492 switch (stmt->type) { 01493 case RULESET_STMT: 01494 if (!stmt->parent_sheet) 01495 continue; 01496 status = put_css_properties_in_props_list 01497 (a_props, stmt); 01498 break; 01499 default: 01500 break; 01501 } 01502 01503 } 01504 status = CR_OK ; 01505 cleanup: 01506 if (stmts_tab) { 01507 g_free (stmts_tab); 01508 stmts_tab = NULL; 01509 } 01510 01511 return status; 01512 } 01513 01514 enum CRStatus 01515 cr_sel_eng_get_matched_style (CRSelEng * a_this, 01516 CRCascade * a_cascade, 01517 xmlNode * a_node, 01518 CRStyle * a_parent_style, 01519 CRStyle ** a_style, 01520 gboolean a_set_props_to_initial_values) 01521 { 01522 enum CRStatus status = CR_OK; 01523 01524 CRPropList *props = NULL; 01525 01526 g_return_val_if_fail (a_this && a_cascade 01527 && a_node && a_style, CR_BAD_PARAM_ERROR); 01528 01529 status = cr_sel_eng_get_matched_properties_from_cascade 01530 (a_this, a_cascade, a_node, &props); 01531 01532 g_return_val_if_fail (status == CR_OK, status); 01533 if (props) { 01534 if (!*a_style) { 01535 *a_style = cr_style_new (a_set_props_to_initial_values) ; 01536 g_return_val_if_fail (*a_style, CR_ERROR); 01537 } else { 01538 if (a_set_props_to_initial_values == TRUE) { 01539 cr_style_set_props_to_initial_values (*a_style) ; 01540 } else { 01541 cr_style_set_props_to_default_values (*a_style); 01542 } 01543 } 01544 (*a_style)->parent_style = a_parent_style; 01545 01546 set_style_from_props (*a_style, props); 01547 if (props) { 01548 cr_prop_list_destroy (props); 01549 props = NULL; 01550 } 01551 } 01552 return CR_OK; 01553 } 01554 01555 /** 01556 * cr_sel_eng_destroy: 01557 *@a_this: the current instance of the selection engine. 01558 * 01559 *The destructor of #CRSelEng 01560 */ 01561 void 01562 cr_sel_eng_destroy (CRSelEng * a_this) 01563 { 01564 g_return_if_fail (a_this); 01565 01566 if (!PRIVATE (a_this)) 01567 goto end ; 01568 if (PRIVATE (a_this)->pcs_handlers) { 01569 cr_sel_eng_unregister_all_pseudo_class_sel_handlers 01570 (a_this) ; 01571 PRIVATE (a_this)->pcs_handlers = NULL ; 01572 } 01573 g_free (PRIVATE (a_this)); 01574 PRIVATE (a_this) = NULL; 01575 end: 01576 if (a_this) { 01577 g_free (a_this); 01578 } 01579 }