Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

cr-sel-eng.c

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

Generated on Fri Oct 29 08:29:12 2004 for Libcroco by  doxygen 1.3.9.1