00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <string.h>
00025 #include "cr-sel-eng.h"
00026
00027
00028
00029
00030
00031
00032
00033
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
00046 gboolean case_sensitive;
00047
00048 CRStyleSheet *sheet;
00049
00050
00051
00052
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 static gboolean
00109 lang_pseudo_class_handler (CRSelEng * a_this,
00110 CRAdditionalSel * a_sel, xmlNode * a_node)
00111 {
00112 xmlNode *node = a_node;
00113 xmlChar *val = NULL;
00114 gboolean result = FALSE;
00115
00116 g_return_val_if_fail (a_this && PRIVATE (a_this)
00117 && a_sel && a_sel->content.pseudo
00118 && a_sel->content.pseudo
00119 && a_sel->content.pseudo->name
00120 && a_sel->content.pseudo->name->stryng
00121 && a_node, CR_BAD_PARAM_ERROR);
00122
00123 if (strncmp (a_sel->content.pseudo->name->stryng->str,
00124 "lang", 4)
00125 || !a_sel->content.pseudo->type == FUNCTION_PSEUDO) {
00126 cr_utils_trace_info ("This handler is for :lang only");
00127 return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
00128 }
00129
00130 if (!a_sel->content.pseudo->extra
00131 || !a_sel->content.pseudo->extra->stryng
00132 || a_sel->content.pseudo->extra->stryng->len < 2)
00133 return FALSE;
00134 for (; node; node = get_next_parent_element_node (node)) {
00135 val = xmlGetProp (node, "lang");
00136 if (val
00137 && !strncmp (val,
00138 a_sel->content.pseudo->extra->stryng->str,
00139 a_sel->content.pseudo->extra->stryng->len)) {
00140 result = TRUE;
00141 }
00142 if (val) {
00143 xmlFree (val);
00144 val = NULL;
00145 }
00146 }
00147
00148 return result;
00149 }
00150
00151 static gboolean
00152 first_child_pseudo_class_handler (CRSelEng * a_this,
00153 CRAdditionalSel * a_sel, xmlNode * a_node)
00154 {
00155 xmlNode *node = NULL;
00156
00157 g_return_val_if_fail (a_this && PRIVATE (a_this)
00158 && a_sel && a_sel->content.pseudo
00159 && a_sel->content.pseudo
00160 && a_sel->content.pseudo->name
00161 && a_sel->content.pseudo->name->stryng
00162 && a_node, CR_BAD_PARAM_ERROR);
00163
00164 if (strcmp (a_sel->content.pseudo->name->stryng->str,
00165 "first-child")
00166 || !a_sel->content.pseudo->type == IDENT_PSEUDO) {
00167 cr_utils_trace_info ("This handler is for :first-child only");
00168 return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
00169 }
00170 if (!a_node->parent)
00171 return FALSE;
00172 node = get_next_child_element_node (a_node->parent);
00173 if (node == a_node)
00174 return TRUE;
00175 return FALSE;
00176 }
00177
00178 static gboolean
00179 pseudo_class_add_sel_matches_node (CRSelEng * a_this,
00180 CRAdditionalSel * a_add_sel,
00181 xmlNode * a_node)
00182 {
00183 enum CRStatus status = CR_OK;
00184 CRPseudoClassSelectorHandler handler = NULL;
00185
00186 g_return_val_if_fail (a_this && PRIVATE (a_this)
00187 && a_add_sel
00188 && a_add_sel->content.pseudo
00189 && a_add_sel->content.pseudo->name
00190 && a_add_sel->content.pseudo->name->stryng
00191 && a_add_sel->content.pseudo->name->stryng->str
00192 && a_node, CR_BAD_PARAM_ERROR);
00193
00194 status = cr_sel_eng_get_pseudo_class_selector_handler
00195 (a_this, a_add_sel->content.pseudo->name->stryng->str,
00196 a_add_sel->content.pseudo->type, &handler);
00197 if (status != CR_OK || !handler)
00198 return FALSE;
00199
00200 return handler (a_this, a_add_sel, a_node);
00201 }
00202
00203
00204
00205
00206
00207
00208
00209 static gboolean
00210 class_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
00211 {
00212 gboolean result = FALSE;
00213 xmlChar *klass = NULL,
00214 *cur = NULL;
00215
00216 g_return_val_if_fail (a_add_sel
00217 && a_add_sel->type == CLASS_ADD_SELECTOR
00218 && a_add_sel->content.class_name
00219 && a_add_sel->content.class_name->stryng
00220 && a_add_sel->content.class_name->stryng->str
00221 && a_node, FALSE);
00222
00223 if (xmlHasProp (a_node, "class")) {
00224 klass = xmlGetProp (a_node, "class");
00225 for (cur = klass; cur && *cur; cur++) {
00226 while (cur && *cur
00227 && cr_utils_is_white_space (*cur)
00228 == TRUE)
00229 cur++;
00230
00231 if (!strncmp (cur,
00232 a_add_sel->content.class_name->stryng->str,
00233 a_add_sel->content.class_name->stryng->len)) {
00234 cur += a_add_sel->content.class_name->stryng->len;
00235 if ((cur && !*cur)
00236 || cr_utils_is_white_space (*cur) == TRUE)
00237 result = TRUE;
00238 }
00239 if (cur && !*cur)
00240 break ;
00241 }
00242 }
00243 if (klass) {
00244 xmlFree (klass);
00245 klass = NULL;
00246 }
00247 return result;
00248
00249 }
00250
00251
00252
00253
00254
00255
00256
00257 static gboolean
00258 id_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
00259 {
00260 gboolean result = FALSE;
00261 xmlChar *id = NULL;
00262
00263 g_return_val_if_fail (a_add_sel
00264 && a_add_sel->type == ID_ADD_SELECTOR
00265 && a_add_sel->content.id_name
00266 && a_add_sel->content.id_name->stryng
00267 && a_add_sel->content.id_name->stryng->str
00268 && a_node, FALSE);
00269 g_return_val_if_fail (a_add_sel
00270 && a_add_sel->type == ID_ADD_SELECTOR
00271 && a_node, FALSE);
00272
00273 if (xmlHasProp (a_node, "id")) {
00274 id = xmlGetProp (a_node, "id");
00275 if (!strncmp (id, a_add_sel->content.id_name->stryng->str,
00276 a_add_sel->content.id_name->stryng->len)) {
00277 result = TRUE;
00278 }
00279 }
00280 if (id) {
00281 xmlFree (id);
00282 id = NULL;
00283 }
00284 return result;
00285 }
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296 static gboolean
00297 attr_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
00298 {
00299 CRAttrSel *cur_sel = NULL;
00300
00301 g_return_val_if_fail (a_add_sel
00302 && a_add_sel->type == ATTRIBUTE_ADD_SELECTOR
00303 && a_node, FALSE);
00304
00305 for (cur_sel = a_add_sel->content.attr_sel;
00306 cur_sel; cur_sel = cur_sel->next) {
00307 switch (cur_sel->match_way) {
00308 case SET:
00309 if (!cur_sel->name
00310 || !cur_sel->name->stryng
00311 || !cur_sel->name->stryng->str)
00312 return FALSE;
00313
00314 if (!xmlHasProp (a_node,
00315 cur_sel->name->stryng->str))
00316 return FALSE;
00317 break;
00318
00319 case EQUALS:
00320 {
00321 xmlChar *value = NULL;
00322
00323 if (!cur_sel->name
00324 || !cur_sel->name->stryng
00325 || !cur_sel->name->stryng->str
00326 || !cur_sel->value
00327 || !cur_sel->value->stryng
00328 || !cur_sel->value->stryng->str)
00329 return FALSE;
00330
00331 if (!xmlHasProp
00332 (a_node,
00333 cur_sel->name->stryng->str))
00334 return FALSE;
00335
00336 value = xmlGetProp
00337 (a_node,
00338 cur_sel->name->stryng->str);
00339
00340 if (value
00341 && strcmp
00342 (value,
00343 cur_sel->value->stryng->str)) {
00344 xmlFree (value);
00345 return FALSE;
00346 }
00347 xmlFree (value);
00348 }
00349 break;
00350
00351 case INCLUDES:
00352 {
00353 xmlChar *value = NULL,
00354 *ptr1 = NULL,
00355 *ptr2 = NULL,
00356 *cur = NULL;
00357 gboolean found = FALSE;
00358
00359 if (!xmlHasProp
00360 (a_node,
00361 cur_sel->name->stryng->str))
00362 return FALSE;
00363 value = xmlGetProp
00364 (a_node,
00365 cur_sel->name->stryng->str);
00366
00367 if (!value)
00368 return FALSE;
00369
00370
00371
00372
00373
00374
00375 for (cur = value; *cur; cur++) {
00376
00377
00378
00379
00380 while (cr_utils_is_white_space
00381 (*cur) == TRUE && *cur)
00382 cur++;
00383 if (!*cur)
00384 break;
00385 ptr1 = cur;
00386
00387
00388
00389
00390 while (cr_utils_is_white_space
00391 (*cur) == FALSE && *cur)
00392 cur++;
00393 cur--;
00394 ptr2 = cur;
00395
00396 if (!strncmp
00397 (ptr1,
00398 cur_sel->value->stryng->str,
00399 ptr2 - ptr1 + 1)) {
00400 found = TRUE;
00401 break;
00402 }
00403 ptr1 = ptr2 = NULL;
00404 }
00405
00406 if (found == FALSE) {
00407 xmlFree (value);
00408 return FALSE;
00409 }
00410 xmlFree (value);
00411 }
00412 break;
00413
00414 case DASHMATCH:
00415 {
00416 xmlChar *value = NULL,
00417 *ptr1 = NULL,
00418 *ptr2 = NULL,
00419 *cur = NULL;
00420 gboolean found = FALSE;
00421
00422 if (!xmlHasProp
00423 (a_node,
00424 cur_sel->name->stryng->str))
00425 return FALSE;
00426 value = xmlGetProp
00427 (a_node,
00428 cur_sel->name->stryng->str);
00429
00430
00431
00432
00433
00434
00435 for (cur = value; *cur; cur++) {
00436 if (*cur == '-')
00437 cur++;
00438 ptr1 = cur;
00439
00440 while (*cur != '-' && *cur)
00441 cur++;
00442 cur--;
00443 ptr2 = cur;
00444
00445 if (g_strstr_len
00446 (ptr1, ptr2 - ptr1 + 1,
00447 cur_sel->value->stryng->str)
00448 == (gchar *) ptr1) {
00449 found = TRUE;
00450 break;
00451 }
00452 }
00453
00454 if (found == FALSE) {
00455 xmlFree (value);
00456 return FALSE;
00457 }
00458 xmlFree (value);
00459 }
00460 break;
00461 default:
00462 return FALSE;
00463 }
00464 }
00465
00466 return TRUE;
00467 }
00468
00469
00470
00471
00472
00473
00474
00475 static gboolean
00476 additional_selector_matches_node (CRSelEng * a_this,
00477 CRAdditionalSel * a_add_sel,
00478 xmlNode * a_node)
00479 {
00480 CRAdditionalSel *cur_add_sel = NULL, *tail = NULL ;
00481 gboolean evaluated = FALSE ;
00482
00483 for (tail = a_add_sel ;
00484 tail && tail->next;
00485 tail = tail->next) ;
00486
00487 g_return_val_if_fail (tail, FALSE) ;
00488
00489 for (cur_add_sel = tail ;
00490 cur_add_sel ;
00491 cur_add_sel = cur_add_sel->prev) {
00492
00493 evaluated = TRUE ;
00494 if (cur_add_sel->type == NO_ADD_SELECTOR) {
00495 return FALSE;
00496 }
00497
00498 if (cur_add_sel->type == CLASS_ADD_SELECTOR
00499 && cur_add_sel->content.class_name
00500 && cur_add_sel->content.class_name->stryng
00501 && cur_add_sel->content.class_name->stryng->str) {
00502 if (class_add_sel_matches_node (cur_add_sel,
00503 a_node) == FALSE) {
00504 return FALSE;
00505 }
00506 continue ;
00507 } else if (cur_add_sel->type == ID_ADD_SELECTOR
00508 && cur_add_sel->content.id_name
00509 && cur_add_sel->content.id_name->stryng
00510 && cur_add_sel->content.id_name->stryng->str) {
00511 if (id_add_sel_matches_node (cur_add_sel, a_node) == FALSE) {
00512 return FALSE;
00513 }
00514 continue ;
00515 } else if (cur_add_sel->type == ATTRIBUTE_ADD_SELECTOR
00516 && cur_add_sel->content.attr_sel) {
00517
00518
00519
00520
00521
00522 if (attr_add_sel_matches_node (cur_add_sel, a_node)
00523 == FALSE) {
00524 return FALSE;
00525 }
00526 continue ;
00527 } else if (cur_add_sel->type == PSEUDO_CLASS_ADD_SELECTOR
00528 && cur_add_sel->content.pseudo) {
00529 if (pseudo_class_add_sel_matches_node
00530 (a_this, cur_add_sel, a_node) == TRUE) {
00531 return TRUE;
00532 }
00533 return FALSE;
00534 }
00535 }
00536 if (evaluated == TRUE)
00537 return TRUE;
00538 return FALSE ;
00539 }
00540
00541 static xmlNode *
00542 get_next_element_node (xmlNode * a_node)
00543 {
00544 xmlNode *cur_node = NULL;
00545
00546 g_return_val_if_fail (a_node, NULL);
00547
00548 cur_node = a_node->next;
00549 while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
00550 cur_node = cur_node->next;
00551 }
00552 return cur_node;
00553 }
00554
00555 static xmlNode *
00556 get_next_child_element_node (xmlNode * a_node)
00557 {
00558 xmlNode *cur_node = NULL;
00559
00560 g_return_val_if_fail (a_node, NULL);
00561
00562 cur_node = a_node->children;
00563 if (!cur_node)
00564 return cur_node;
00565 if (a_node->children->type == XML_ELEMENT_NODE)
00566 return a_node->children;
00567 return get_next_element_node (a_node->children);
00568 }
00569
00570 static xmlNode *
00571 get_prev_element_node (xmlNode * a_node)
00572 {
00573 xmlNode *cur_node = NULL;
00574
00575 g_return_val_if_fail (a_node, NULL);
00576
00577 cur_node = a_node->prev;
00578 while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
00579 cur_node = cur_node->prev;
00580 }
00581 return cur_node;
00582 }
00583
00584 static xmlNode *
00585 get_next_parent_element_node (xmlNode * a_node)
00586 {
00587 xmlNode *cur_node = NULL;
00588
00589 g_return_val_if_fail (a_node, NULL);
00590
00591 cur_node = a_node->parent;
00592 while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
00593 cur_node = cur_node->parent;
00594 }
00595 return cur_node;
00596 }
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616 static enum CRStatus
00617 sel_matches_node_real (CRSelEng * a_this, CRSimpleSel * a_sel,
00618 xmlNode * a_node, gboolean * a_result,
00619 gboolean a_eval_sel_list_from_end,
00620 gboolean a_recurse)
00621 {
00622 CRSimpleSel *cur_sel = NULL;
00623 xmlNode *cur_node = NULL;
00624
00625 g_return_val_if_fail (a_this && PRIVATE (a_this)
00626 && a_this && a_node
00627 && a_result, CR_BAD_PARAM_ERROR);
00628
00629 *a_result = FALSE;
00630
00631 if (a_node->type != XML_ELEMENT_NODE)
00632 return CR_OK;
00633
00634 if (a_eval_sel_list_from_end == TRUE) {
00635
00636 for (cur_sel = a_sel;
00637 cur_sel && cur_sel->next; cur_sel = cur_sel->next) ;
00638 } else {
00639 cur_sel = a_sel;
00640 }
00641
00642 for (cur_node = a_node; cur_sel; cur_sel = cur_sel->prev) {
00643 if (((cur_sel->type_mask & TYPE_SELECTOR)
00644 && (cur_sel->name
00645 && cur_sel->name->stryng
00646 && cur_sel->name->stryng->str)
00647 && (!strcmp (cur_sel->name->stryng->str,
00648 cur_node->name)))
00649 || (cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
00650
00651
00652
00653
00654
00655
00656
00657 if (cur_sel->add_sel) {
00658 if (additional_selector_matches_node (a_this, cur_sel->add_sel,
00659 cur_node) == TRUE) {
00660 goto walk_a_step_in_expr;
00661 } else {
00662 goto done;
00663 }
00664 } else {
00665 goto walk_a_step_in_expr;
00666 }
00667 }
00668 if (!(cur_sel->type_mask & TYPE_SELECTOR)
00669 && !(cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
00670 if (!cur_sel->add_sel) {
00671 goto done;
00672 }
00673 if (additional_selector_matches_node
00674 (a_this, cur_sel->add_sel, cur_node)
00675 == TRUE) {
00676 goto walk_a_step_in_expr;
00677 } else {
00678 goto done;
00679 }
00680 } else {
00681 goto done ;
00682 }
00683
00684 walk_a_step_in_expr:
00685 if (a_recurse == FALSE) {
00686 *a_result = TRUE;
00687 goto done;
00688 }
00689
00690
00691
00692
00693
00694
00695 if (!cur_sel->prev)
00696 break;
00697
00698 switch (cur_sel->combinator) {
00699 case NO_COMBINATOR:
00700 break;
00701
00702 case COMB_WS:
00703 {
00704 xmlNode *n = NULL;
00705 enum CRStatus status = CR_OK;
00706 gboolean matches = FALSE;
00707
00708
00709
00710
00711
00712 for (n = cur_node->parent; n; n = n->parent) {
00713 status = sel_matches_node_real
00714 (a_this, cur_sel->prev,
00715 n, &matches, FALSE, TRUE);
00716
00717 if (status != CR_OK)
00718 goto done;
00719
00720 if (matches == TRUE) {
00721 cur_node = n ;
00722 break;
00723 }
00724 }
00725
00726 if (!n) {
00727
00728
00729
00730
00731 goto done;
00732 }
00733
00734
00735
00736
00737
00738
00739
00740
00741 break;
00742 }
00743
00744 case COMB_PLUS:
00745 cur_node = get_prev_element_node (cur_node);
00746 if (!cur_node)
00747 goto done;
00748 break;
00749
00750 case COMB_GT:
00751 cur_node = get_next_parent_element_node (cur_node);
00752 if (!cur_node)
00753 goto done;
00754 break;
00755
00756 default:
00757 goto done;
00758 }
00759 continue;
00760 }
00761
00762
00763
00764
00765
00766 *a_result = TRUE;
00767
00768 done:
00769 return CR_OK;
00770 }
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810 static enum CRStatus
00811 cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
00812 CRStyleSheet * a_stylesheet,
00813 xmlNode * a_node,
00814 CRStatement ** a_rulesets,
00815 gulong * a_len)
00816 {
00817 CRStatement *cur_stmt = NULL;
00818 CRSelector *sel_list = NULL,
00819 *cur_sel = NULL;
00820 gboolean matches = FALSE;
00821 enum CRStatus status = CR_OK;
00822 gulong i = 0;
00823
00824 g_return_val_if_fail (a_this
00825 && a_stylesheet
00826 && a_node && a_rulesets, CR_BAD_PARAM_ERROR);
00827
00828 if (!a_stylesheet->statements) {
00829 *a_rulesets = NULL;
00830 *a_len = 0;
00831 return CR_OK;
00832 }
00833
00834
00835
00836
00837
00838 if (PRIVATE (a_this)->sheet != a_stylesheet) {
00839 PRIVATE (a_this)->sheet = a_stylesheet;
00840 PRIVATE (a_this)->cur_stmt = a_stylesheet->statements;
00841 }
00842
00843
00844
00845
00846
00847
00848
00849 for (cur_stmt = PRIVATE (a_this)->cur_stmt, i = 0;
00850 (PRIVATE (a_this)->cur_stmt = cur_stmt);
00851 cur_stmt = cur_stmt->next) {
00852
00853
00854
00855
00856 sel_list = NULL;
00857
00858
00859
00860
00861
00862 switch (cur_stmt->type) {
00863 case RULESET_STMT:
00864 if (cur_stmt->kind.ruleset
00865 && cur_stmt->kind.ruleset->sel_list) {
00866 sel_list = cur_stmt->kind.ruleset->sel_list;
00867 }
00868 break;
00869
00870 case AT_MEDIA_RULE_STMT:
00871 if (cur_stmt->kind.media_rule
00872 && cur_stmt->kind.media_rule->rulesets
00873 && cur_stmt->kind.media_rule->rulesets->
00874 kind.ruleset
00875 && cur_stmt->kind.media_rule->rulesets->
00876 kind.ruleset->sel_list) {
00877 sel_list =
00878 cur_stmt->kind.media_rule->
00879 rulesets->kind.ruleset->sel_list;
00880 }
00881 break;
00882
00883 case AT_IMPORT_RULE_STMT:
00884
00885
00886
00887
00888 break;
00889 default:
00890 break;
00891 }
00892
00893 if (!sel_list)
00894 continue;
00895
00896
00897
00898
00899
00900
00901 for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next) {
00902 if (!cur_sel->simple_sel)
00903 continue;
00904
00905 status = cr_sel_eng_matches_node
00906 (a_this, cur_sel->simple_sel,
00907 a_node, &matches);
00908
00909 if (status == CR_OK && matches == TRUE) {
00910
00911
00912
00913
00914
00915
00916 if (i < *a_len) {
00917 a_rulesets[i] = cur_stmt;
00918 i++;
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929 status = cr_simple_sel_compute_specificity (cur_sel->simple_sel);
00930
00931 g_return_val_if_fail (status == CR_OK,
00932 CR_ERROR);
00933 cur_stmt->specificity =
00934 cur_sel->simple_sel->
00935 specificity;
00936 } else
00937 {
00938 *a_len = i;
00939 return CR_OUTPUT_TOO_SHORT_ERROR;
00940 }
00941 }
00942 }
00943 }
00944
00945
00946
00947
00948
00949
00950
00951 g_return_val_if_fail (!PRIVATE (a_this)->cur_stmt, CR_ERROR);
00952 PRIVATE (a_this)->sheet = NULL;
00953 *a_len = i;
00954 return CR_OK;
00955 }
00956
00957 static enum CRStatus
00958 put_css_properties_in_props_list (CRPropList ** a_props, CRStatement * a_stmt)
00959 {
00960 CRPropList *props = NULL,
00961 *pair = NULL,
00962 *tmp_props = NULL;
00963 CRDeclaration *cur_decl = NULL;
00964
00965 g_return_val_if_fail (a_props && a_stmt
00966 && a_stmt->type == RULESET_STMT
00967 && a_stmt->kind.ruleset, CR_BAD_PARAM_ERROR);
00968
00969 props = *a_props;
00970
00971 for (cur_decl = a_stmt->kind.ruleset->decl_list;
00972 cur_decl; cur_decl = cur_decl->next) {
00973 CRDeclaration *decl;
00974
00975 decl = NULL;
00976 pair = NULL;
00977
00978 if (!cur_decl->property
00979 || !cur_decl->property->stryng
00980 || !cur_decl->property->stryng->str)
00981 continue;
00982
00983
00984
00985
00986
00987
00988
00989 cr_prop_list_lookup_prop (props,
00990 cur_decl->property,
00991 &pair);
00992
00993 if (!pair) {
00994 tmp_props = cr_prop_list_append2
00995 (props, cur_decl->property, cur_decl);
00996 if (tmp_props) {
00997 props = tmp_props;
00998 tmp_props = NULL;
00999 }
01000 continue;
01001 }
01002
01003
01004
01005
01006
01007
01008
01009 cr_prop_list_get_decl (pair, &decl);
01010 g_return_val_if_fail (decl, CR_ERROR);
01011
01012
01013
01014
01015
01016
01017
01018
01019
01020 if (decl->parent_statement
01021 && decl->parent_statement->parent_sheet
01022 && (decl->parent_statement->parent_sheet->origin
01023 < a_stmt->parent_sheet->origin)) {
01024
01025
01026
01027
01028
01029
01030
01031 if (decl->important == TRUE
01032 && decl->parent_statement->parent_sheet->origin
01033 != ORIGIN_UA) {
01034 continue;
01035 }
01036 tmp_props = cr_prop_list_unlink (props, pair);
01037 if (props) {
01038 cr_prop_list_destroy (pair);
01039 }
01040 props = tmp_props;
01041 tmp_props = NULL;
01042 props = cr_prop_list_append2
01043 (props, cur_decl->property, cur_decl);
01044
01045 continue;
01046 } else if (decl->parent_statement
01047 && decl->parent_statement->parent_sheet
01048 && (decl->parent_statement->
01049 parent_sheet->origin
01050 > a_stmt->parent_sheet->origin)) {
01051 cr_utils_trace_info
01052 ("We should not reach this line\n");
01053 continue;
01054 }
01055
01056
01057
01058
01059
01060
01061
01062
01063
01064
01065
01066
01067
01068 if (a_stmt->specificity
01069 >= decl->parent_statement->specificity) {
01070 if (decl->important == TRUE)
01071 continue;
01072 props = cr_prop_list_unlink (props, pair);
01073 if (pair) {
01074 cr_prop_list_destroy (pair);
01075 pair = NULL;
01076 }
01077 props = cr_prop_list_append2 (props,
01078 cur_decl->property,
01079 cur_decl);
01080 }
01081 }
01082
01083 *a_props = props;
01084
01085 return CR_OK;
01086 }
01087
01088 static void
01089 set_style_from_props (CRStyle * a_style, CRPropList * a_props)
01090 {
01091 CRPropList *cur = NULL;
01092 CRDeclaration *decl = NULL;
01093
01094 for (cur = a_props; cur; cur = cr_prop_list_get_next (cur)) {
01095 cr_prop_list_get_decl (cur, &decl);
01096 cr_style_set_style_from_decl (a_style, decl);
01097 decl = NULL;
01098 }
01099 }
01100
01101
01102
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112 CRSelEng *
01113 cr_sel_eng_new (void)
01114 {
01115 CRSelEng *result = NULL;
01116
01117 result = g_try_malloc (sizeof (CRSelEng));
01118 if (!result) {
01119 cr_utils_trace_info ("Out of memory");
01120 return NULL;
01121 }
01122 memset (result, 0, sizeof (CRSelEng));
01123
01124 PRIVATE (result) = g_try_malloc (sizeof (CRSelEngPriv));
01125 if (!PRIVATE (result)) {
01126 cr_utils_trace_info ("Out of memory");
01127 g_free (result);
01128 return NULL;
01129 }
01130 memset (PRIVATE (result), 0, sizeof (CRSelEngPriv));
01131 cr_sel_eng_register_pseudo_class_sel_handler
01132 (result, (guchar *) "first-child",
01133 IDENT_PSEUDO, (CRPseudoClassSelectorHandler)
01134 first_child_pseudo_class_handler);
01135 cr_sel_eng_register_pseudo_class_sel_handler
01136 (result, (guchar *) "lang",
01137 FUNCTION_PSEUDO, (CRPseudoClassSelectorHandler)
01138 lang_pseudo_class_handler);
01139
01140 return result;
01141 }
01142
01143
01144
01145
01146
01147
01148
01149
01150
01151
01152
01153
01154
01155 enum CRStatus
01156 cr_sel_eng_register_pseudo_class_sel_handler (CRSelEng * a_this,
01157 guchar * a_name,
01158 enum CRPseudoType a_type,
01159 CRPseudoClassSelectorHandler
01160 a_handler)
01161 {
01162 struct CRPseudoClassSelHandlerEntry *handler_entry = NULL;
01163 GList *list = NULL;
01164
01165 g_return_val_if_fail (a_this && PRIVATE (a_this)
01166 && a_handler && a_name, CR_BAD_PARAM_ERROR);
01167
01168 handler_entry = g_try_malloc
01169 (sizeof (struct CRPseudoClassSelHandlerEntry));
01170 if (!handler_entry) {
01171 return CR_OUT_OF_MEMORY_ERROR;
01172 }
01173 memset (handler_entry, 0,
01174 sizeof (struct CRPseudoClassSelHandlerEntry));
01175 handler_entry->name = g_strdup (a_name);
01176 handler_entry->type = a_type;
01177 handler_entry->handler = a_handler;
01178 list = g_list_append (PRIVATE (a_this)->pcs_handlers, handler_entry);
01179 if (!list) {
01180 return CR_OUT_OF_MEMORY_ERROR;
01181 }
01182 PRIVATE (a_this)->pcs_handlers = list;
01183 return CR_OK;
01184 }
01185
01186 enum CRStatus
01187 cr_sel_eng_unregister_pseudo_class_sel_handler (CRSelEng * a_this,
01188 guchar * a_name,
01189 enum CRPseudoType a_type)
01190 {
01191 GList *elem = NULL,
01192 *deleted_elem = NULL;
01193 gboolean found = FALSE;
01194 struct CRPseudoClassSelHandlerEntry *entry = NULL;
01195
01196 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01197
01198 for (elem = PRIVATE (a_this)->pcs_handlers;
01199 elem; elem = g_list_next (elem)) {
01200 entry = elem->data;
01201 if (!strcmp (entry->name, a_name)
01202 && entry->type == a_type) {
01203 found = TRUE;
01204 break;
01205 }
01206 }
01207 if (found == FALSE)
01208 return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
01209 PRIVATE (a_this)->pcs_handlers = g_list_delete_link
01210 (PRIVATE (a_this)->pcs_handlers, elem);
01211 entry = elem->data;
01212 if (entry->name)
01213 g_free (entry->name);
01214 g_free (elem);
01215 g_list_free (deleted_elem);
01216
01217 return CR_OK;
01218 }
01219
01220
01221
01222
01223
01224
01225
01226
01227
01228
01229
01230 enum CRStatus
01231 cr_sel_eng_unregister_all_pseudo_class_sel_handlers (CRSelEng * a_this)
01232 {
01233 GList *elem = NULL;
01234 struct CRPseudoClassSelHandlerEntry *entry = NULL;
01235
01236 g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
01237
01238 if (!PRIVATE (a_this)->pcs_handlers)
01239 return CR_OK;
01240 for (elem = PRIVATE (a_this)->pcs_handlers;
01241 elem; elem = g_list_next (elem)) {
01242 entry = elem->data;
01243 if (!entry)
01244 continue;
01245 if (entry->name) {
01246 g_free (entry->name);
01247 entry->name = NULL;
01248 }
01249 g_free (entry);
01250 elem->data = NULL;
01251 }
01252 g_list_free (PRIVATE (a_this)->pcs_handlers);
01253 PRIVATE (a_this)->pcs_handlers = NULL;
01254 return CR_OK;
01255 }
01256
01257 enum CRStatus
01258 cr_sel_eng_get_pseudo_class_selector_handler (CRSelEng * a_this,
01259 guchar * a_name,
01260 enum CRPseudoType a_type,
01261 CRPseudoClassSelectorHandler *
01262 a_handler)
01263 {
01264 GList *elem = NULL;
01265 struct CRPseudoClassSelHandlerEntry *entry = NULL;
01266 gboolean found = FALSE;
01267
01268 g_return_val_if_fail (a_this && PRIVATE (a_this)
01269 && a_name, CR_BAD_PARAM_ERROR);
01270
01271 for (elem = PRIVATE (a_this)->pcs_handlers;
01272 elem; elem = g_list_next (elem)) {
01273 entry = elem->data;
01274 if (!strcmp (a_name, entry->name)
01275 && entry->type == a_type) {
01276 found = TRUE;
01277 break;
01278 }
01279 }
01280
01281 if (found == FALSE)
01282 return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
01283 *a_handler = entry->handler;
01284 return CR_OK;
01285 }
01286
01287
01288
01289
01290
01291
01292
01293
01294
01295
01296
01297
01298
01299
01300
01301
01302
01303 enum CRStatus
01304 cr_sel_eng_matches_node (CRSelEng * a_this, CRSimpleSel * a_sel,
01305 xmlNode * a_node, gboolean * a_result)
01306 {
01307 g_return_val_if_fail (a_this && PRIVATE (a_this)
01308 && a_this && a_node
01309 && a_result, CR_BAD_PARAM_ERROR);
01310
01311 if (a_node->type != XML_ELEMENT_NODE) {
01312 *a_result = FALSE;
01313 return CR_OK;
01314 }
01315
01316 return sel_matches_node_real (a_this, a_sel,
01317 a_node, a_result,
01318 TRUE, TRUE);
01319 }
01320
01321
01322
01323
01324
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337
01338
01339 enum CRStatus
01340 cr_sel_eng_get_matched_rulesets (CRSelEng * a_this,
01341 CRStyleSheet * a_sheet,
01342 xmlNode * a_node,
01343 CRStatement *** a_rulesets, gulong * a_len)
01344 {
01345 CRStatement **stmts_tab = NULL;
01346 enum CRStatus status = CR_OK;
01347 gulong tab_size = 0,
01348 tab_len = 0,
01349 index = 0;
01350 gushort stmts_chunck_size = 8;
01351
01352 g_return_val_if_fail (a_this
01353 && a_sheet
01354 && a_node
01355 && a_rulesets && *a_rulesets == NULL
01356 && a_len, CR_BAD_PARAM_ERROR);
01357
01358 stmts_tab = g_try_malloc (stmts_chunck_size * sizeof (CRStatement *));
01359
01360 if (!stmts_tab) {
01361 cr_utils_trace_info ("Out of memory");
01362 status = CR_ERROR;
01363 goto error;
01364 }
01365 memset (stmts_tab, 0, stmts_chunck_size * sizeof (CRStatement *));
01366
01367 tab_size = stmts_chunck_size;
01368 tab_len = tab_size;
01369
01370 while ((status = cr_sel_eng_get_matched_rulesets_real
01371 (a_this, a_sheet, a_node, stmts_tab + index, &tab_len))
01372 == CR_OUTPUT_TOO_SHORT_ERROR) {
01373 stmts_tab = g_try_realloc (stmts_tab,
01374 (tab_size + stmts_chunck_size)
01375 * sizeof (CRStatement *));
01376 if (!stmts_tab) {
01377 cr_utils_trace_info ("Out of memory");
01378 status = CR_ERROR;
01379 goto error;
01380 }
01381 tab_size += stmts_chunck_size;
01382 index += tab_len;
01383 tab_len = tab_size - index;
01384 }
01385
01386 tab_len = tab_size - stmts_chunck_size + tab_len;
01387 *a_rulesets = stmts_tab;
01388 *a_len = tab_len;
01389
01390 return CR_OK;
01391
01392 error:
01393
01394 if (stmts_tab) {
01395 g_free (stmts_tab);
01396 stmts_tab = NULL;
01397
01398 }
01399
01400 *a_len = 0;
01401 return status;
01402 }
01403
01404
01405 enum CRStatus
01406 cr_sel_eng_get_matched_properties_from_cascade (CRSelEng * a_this,
01407 CRCascade * a_cascade,
01408 xmlNode * a_node,
01409 CRPropList ** a_props)
01410 {
01411 CRStatement **stmts_tab = NULL;
01412 enum CRStatus status = CR_OK;
01413 gulong tab_size = 0,
01414 tab_len = 0,
01415 i = 0,
01416 index = 0;
01417 enum CRStyleOrigin origin = 0;
01418 gushort stmts_chunck_size = 8;
01419 CRStyleSheet *sheet = NULL;
01420
01421 g_return_val_if_fail (a_this
01422 && a_cascade
01423 && a_node && a_props, CR_BAD_PARAM_ERROR);
01424
01425 for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++) {
01426 sheet = cr_cascade_get_sheet (a_cascade, origin);
01427 if (!sheet)
01428 continue;
01429 if (tab_size - index < 1) {
01430 stmts_tab = g_try_realloc
01431 (stmts_tab, (tab_size + stmts_chunck_size)
01432 * sizeof (CRStatement *));
01433 if (!stmts_tab) {
01434 cr_utils_trace_info ("Out of memory");
01435 status = CR_ERROR;
01436 goto cleanup;
01437 }
01438 tab_size += stmts_chunck_size;
01439
01440
01441
01442
01443 tab_len = tab_size - index;
01444 }
01445 while ((status = cr_sel_eng_get_matched_rulesets_real
01446 (a_this, sheet, a_node, stmts_tab + index, &tab_len))
01447 == CR_OUTPUT_TOO_SHORT_ERROR) {
01448 stmts_tab = g_try_realloc
01449 (stmts_tab, (tab_size + stmts_chunck_size)
01450 * sizeof (CRStatement *));
01451 if (!stmts_tab) {
01452 cr_utils_trace_info ("Out of memory");
01453 status = CR_ERROR;
01454 goto cleanup;
01455 }
01456 tab_size += stmts_chunck_size;
01457 index += tab_len;
01458
01459
01460
01461
01462 tab_len = tab_size - index;
01463 }
01464 if (status != CR_OK) {
01465 cr_utils_trace_info ("Error while running "
01466 "selector engine");
01467 goto cleanup;
01468 }
01469 index += tab_len;
01470 tab_len = tab_size - index;
01471 }
01472
01473
01474
01475
01476
01477
01478
01479 for (i = 0; i < index; i++) {
01480 CRStatement *stmt = stmts_tab[i];
01481
01482 if (!stmt)
01483 continue;
01484 switch (stmt->type) {
01485 case RULESET_STMT:
01486 if (!stmt->parent_sheet)
01487 continue;
01488 status = put_css_properties_in_props_list
01489 (a_props, stmt);
01490 break;
01491 default:
01492 break;
01493 }
01494
01495 }
01496 status = CR_OK ;
01497 cleanup:
01498 if (stmts_tab) {
01499 g_free (stmts_tab);
01500 stmts_tab = NULL;
01501 }
01502
01503 return status;
01504 }
01505
01506 enum CRStatus
01507 cr_sel_eng_get_matched_style (CRSelEng * a_this,
01508 CRCascade * a_cascade,
01509 xmlNode * a_node,
01510 CRStyle * a_parent_style,
01511 CRStyle ** a_style,
01512 gboolean a_set_props_to_initial_values)
01513 {
01514 enum CRStatus status = CR_OK;
01515
01516 CRPropList *props = NULL;
01517
01518 g_return_val_if_fail (a_this && a_cascade
01519 && a_node && a_style, CR_BAD_PARAM_ERROR);
01520
01521 status = cr_sel_eng_get_matched_properties_from_cascade
01522 (a_this, a_cascade, a_node, &props);
01523
01524 g_return_val_if_fail (status == CR_OK, status);
01525 if (props) {
01526 if (!*a_style) {
01527 *a_style = cr_style_new (a_set_props_to_initial_values) ;
01528 g_return_val_if_fail (*a_style, CR_ERROR);
01529 } else {
01530 if (a_set_props_to_initial_values == TRUE) {
01531 cr_style_set_props_to_initial_values (*a_style) ;
01532 } else {
01533 cr_style_set_props_to_default_values (*a_style);
01534 }
01535 }
01536 (*a_style)->parent_style = a_parent_style;
01537
01538 set_style_from_props (*a_style, props);
01539 if (props) {
01540 cr_prop_list_destroy (props);
01541 props = NULL;
01542 }
01543 }
01544 return CR_OK;
01545 }
01546
01547
01548
01549
01550
01551
01552
01553 void
01554 cr_sel_eng_destroy (CRSelEng * a_this)
01555 {
01556 g_return_if_fail (a_this);
01557
01558 if (!PRIVATE (a_this))
01559 goto end ;
01560 if (PRIVATE (a_this)->pcs_handlers) {
01561 cr_sel_eng_unregister_all_pseudo_class_sel_handlers
01562 (a_this) ;
01563 PRIVATE (a_this)->pcs_handlers = NULL ;
01564 }
01565 g_free (PRIVATE (a_this));
01566 PRIVATE (a_this) = NULL;
01567 end:
01568 if (a_this) {
01569 g_free (a_this);
01570 }
01571 }