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  *@CRSelEng:
00029  *
00030  *The definition of the  #CRSelEng class.
00031  *The #CRSelEng is actually the "Selection Engine"
00032  *class. This is highly experimental for at the moment and
00033  *its api is very likely to change in a near future.
00034  */
00035 
00036 #define PRIVATE(a_this) (a_this)->priv
00037 
00038 struct CRPseudoClassSelHandlerEntry {
00039         guchar *name;
00040         enum CRPseudoType type;
00041         CRPseudoClassSelectorHandler handler;
00042 };
00043 
00044 struct _CRSelEngPriv {
00045         /*not used yet */
00046         gboolean case_sensitive;
00047 
00048         CRStyleSheet *sheet;
00049         /**
00050          *where to store the next statement
00051          *to be visited so that we can remember
00052          *it from one method call to another.
00053          */
00054         CRStatement *cur_stmt;
00055         GList *pcs_handlers;
00056         gint pcs_handlers_size;
00057 } ;
00058 
00059 static gboolean class_add_sel_matches_node (CRAdditionalSel * a_add_sel,
00060                                             xmlNode * a_node);
00061 
00062 static gboolean id_add_sel_matches_node (CRAdditionalSel * a_add_sel,
00063                                          xmlNode * a_node);
00064 
00065 static gboolean attr_add_sel_matches_node (CRAdditionalSel * a_add_sel,
00066                                            xmlNode * a_node);
00067 
00068 static enum CRStatus sel_matches_node_real (CRSelEng * a_this,
00069                                             CRSimpleSel * a_sel,
00070                                             xmlNode * a_node,
00071                                             gboolean * a_result,
00072                                             gboolean a_eval_sel_list_from_end,
00073                                             gboolean a_recurse);
00074 
00075 static enum CRStatus cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
00076                                                            CRStyleSheet *
00077                                                            a_stylesheet,
00078                                                            xmlNode * a_node,
00079                                                            CRStatement **
00080                                                            a_rulesets,
00081                                                            gulong * a_len);
00082 
00083 static enum CRStatus put_css_properties_in_props_list (CRPropList ** a_props,
00084                                                        CRStatement *
00085                                                        a_ruleset);
00086 
00087 static gboolean pseudo_class_add_sel_matches_node (CRSelEng * a_this,
00088                                                    CRAdditionalSel *
00089                                                    a_add_sel,
00090                                                    xmlNode * a_node);
00091 
00092 static gboolean lang_pseudo_class_handler (CRSelEng * a_this,
00093                                            CRAdditionalSel * a_sel,
00094                                            xmlNode * a_node);
00095 
00096 static gboolean first_child_pseudo_class_handler (CRSelEng * a_this,
00097                                                   CRAdditionalSel * a_sel,
00098                                                   xmlNode * a_node);
00099 
00100 static xmlNode *get_next_element_node (xmlNode * a_node);
00101 
00102 static xmlNode *get_next_child_element_node (xmlNode * a_node);
00103 
00104 static xmlNode *get_prev_element_node (xmlNode * a_node);
00105 
00106 static xmlNode *get_next_parent_element_node (xmlNode * a_node);
00107 
00108 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         /*lang code should exist and be at least of length 2 */
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  *@param a_add_sel the class additional selector to consider.
00205  *@param a_node the xml node to consider.
00206  *@return TRUE if the class additional selector matches
00207  *the xml node given in argument, FALSE otherwise.
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  *@return TRUE if the additional attribute selector matches
00253  *the current xml node given in argument, FALSE otherwise.
00254  *@param a_add_sel the additional attribute selector to consider.
00255  *@param a_node the xml node to consider.
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  *Returns TRUE if the instance of #CRAdditional selector matches
00289  *the node given in parameter, FALSE otherwise.
00290  *@param a_add_sel the additional selector to evaluate.
00291  *@param a_node the xml node against whitch the selector is to
00292  *be evaluated
00293  *return TRUE if the additional selector matches the current xml node
00294  *FALSE otherwise.
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                                  *here, make sure value is a space
00372                                  *separated list of "words", where one
00373                                  *value is exactly cur_sel->value->str
00374                                  */
00375                                 for (cur = value; *cur; cur++) {
00376                                         /*
00377                                          *set ptr1 to the first non white space
00378                                          *char addr.
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                                          *set ptr2 to the end the word.
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                                  *here, make sure value is an hyphen
00432                                  *separated list of "words", each of which
00433                                  *starting with "cur_sel->value->str"
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  *Evaluates if a given additional selector matches an xml node.
00471  *@param a_add_sel the additional selector to consider.
00472  *@param a_node the xml node to consider.
00473  *@return TRUE is a_add_sel matches a_node, FALSE otherwise.
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                          *here, call a function that does the match
00519                          *against an attribute additionnal selector
00520                          *and an xml node.
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  *Evaluate a selector (a simple selectors list) and says
00600  *if it matches the xml node given in parameter.
00601  *The algorithm used here is the following:
00602  *Walk the combinator separated list of simple selectors backward, starting
00603  *from the end of the list. For each simple selector, looks if
00604  *if matches the current node.
00605  *
00606  *@param a_this the selection engine.
00607  *@param a_sel the simple selection list.
00608  *@param a_node the xml node.
00609  *@param a_result out parameter. Set to true if the
00610  *selector matches the xml node, FALSE otherwise.
00611  *@param a_recurse if set to TRUE, the function will walk to
00612  *the next simple selector (after the evaluation of the current one) 
00613  *and recursively evaluate it. Must be usually set to TRUE unless you
00614  *know what you are doing.
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                 /*go and get the last simple selector of the list */
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                          *this simple selector
00652                          *matches the current xml node
00653                          *Let's see if the preceding
00654                          *simple selectors also match
00655                          *their xml node counterpart.
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                  *here, depending on the combinator of cur_sel
00692                  *choose the axis of the xml tree traversal
00693                  *and walk one step in the xml tree.
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:  /*descendant selector */
00703                 {
00704                         xmlNode *n = NULL;
00705                         enum CRStatus status = CR_OK;
00706                         gboolean matches = FALSE;
00707 
00708                         /*
00709                          *walk the xml tree upward looking for a parent
00710                          *node that matches the preceding selector.
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                                  *didn't find any ancestor that matches
00729                                  *the previous simple selector.
00730                                  */
00731                                 goto done;
00732                         }
00733                         /*
00734                          *in this case, the preceding simple sel
00735                          *will have been interpreted twice, which
00736                          *is a cpu and mem waste ... I need to find
00737                          *another way to do this. Anyway, this is
00738                          *my first attempt to write this function and
00739                          *I am a bit clueless.
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          *if we reached this point, it means the selector matches
00764          *the xml node.
00765          */
00766         *a_result = TRUE;
00767 
00768  done:
00769         return CR_OK;
00770 }
00771 
00772 
00773 /**
00774  *Returns  array of the ruleset statements that matches the
00775  *given xml node.
00776  *The engine keeps in memory the last statement he
00777  *visited during the match. So, the next call
00778  *to this function will eventually return a rulesets list starting
00779  *from the last ruleset statement visited during the previous call.
00780  *The enable users to get matching rulesets in an incremental way.
00781  *Note that for each statement returned, 
00782  *the engine calculates the specificity of the selector
00783  *that matched the xml node and stores it in the "specifity" field
00784  *of the statement structure.
00785  *
00786  *@param a_sel_eng the current selection engine
00787  *@param a_node the xml node for which the request
00788  *is being made.
00789  *@param a_sel_list the list of selectors to perform the search in.
00790  *@param a_rulesets in/out parameter. A pointer to the
00791  *returned array of rulesets statements that match the xml node
00792  *given in parameter. The caller allocates the array before calling this
00793  *function.
00794  *@param a_len in/out parameter the length (in sizeof (#CRStatement*)) 
00795  *of the returned array.
00796  *(the length of a_rulesets, more precisely).
00797  *The caller must set it to the length of a_ruleset prior to calling this
00798  *function. In return, the function sets it to the length 
00799  *(in sizeof (#CRStatement)) of the actually returned CRStatement array.
00800  *@return CR_OUTPUT_TOO_SHORT_ERROR if found more rulesets than the size
00801  *of the a_rulesets array. In this case, the first *a_len rulesets found
00802  *are put in a_rulesets, and a further call will return the following
00803  *ruleset(s) following the same principle.
00804  *@return CR_OK if all the rulesets found have been returned. In this
00805  *case, *a_len is set to the actual number of ruleset found.
00806  *@return CR_BAD_PARAM_ERROR in case any of the given parameter are
00807  *bad (e.g null pointer).
00808  *@return CR_ERROR if any other error occured.
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          *if this stylesheet is "new one"
00836          *let's remember it for subsequent calls.
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          *walk through the list of statements and,
00845          *get the selectors list inside the statements that
00846          *contain some, and try to match our xml node in these
00847          *selectors lists.
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                  *initialyze the selector list in which we will
00854                  *really perform the search.
00855                  */
00856                 sel_list = NULL;
00857 
00858                 /*
00859                  *get the the damn selector list in 
00860                  *which we have to look
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                          *some recursivity may be needed here.
00886                          *I don't like this :(
00887                          */
00888                         break;
00889                 default:
00890                         break;
00891                 }
00892 
00893                 if (!sel_list)
00894                         continue;
00895 
00896                 /*
00897                  *now, we have a comma separated selector list to look in.
00898                  *let's walk it and try to match the xml_node
00899                  *on each item of the list.
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                                  *bingo!!! we found one ruleset that
00912                                  *matches that fucking node.
00913                                  *lets put it in the out array.
00914                                  */
00915 
00916                                 if (i < *a_len) {
00917                                         a_rulesets[i] = cur_stmt;
00918                                         i++;
00919 
00920                                         /*
00921                                          *For the cascade computing algorithm
00922                                          *(which is gonna take place later)
00923                                          *we must compute the specificity
00924                                          *(css2 spec chap 6.4.1) of the selector
00925                                          *that matched the current xml node
00926                                          *and store it in the css2 statement
00927                                          *(statement == ruleset here).
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          *if we reached this point, it means
00947          *we reached the end of stylesheet.
00948          *no need to store any info about the stylesheet
00949          *anymore.
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                  *First, test if the property is not
00984                  *already present in our properties list
00985                  *If yes, apply the cascading rules to
00986                  *compute the precedence. If not, insert
00987                  *the property into the list
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                  *A property with the same name already exists.
01005                  *We must apply here 
01006                  *some cascading rules
01007                  *to compute the precedence.
01008                  */
01009                 cr_prop_list_get_decl (pair, &decl);
01010                 g_return_val_if_fail (decl, CR_ERROR);
01011 
01012                 /*
01013                  *first, look at the origin.
01014                  *6.4.1 says: 
01015                  *"for normal declarations, 
01016                  *author style sheets override user 
01017                  *style sheets which override 
01018                  *the default style sheet."
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                          *if the already selected declaration
01026                          *is marked as being !important the current
01027                          *declaration must not overide it 
01028                          *(unless the already selected declaration 
01029                          *has an UA origin)
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                  *A property with the same
01058                  *name and the same origin already exists.
01059                  *shit. This is lasting longer than expected ...
01060                  *Luckily, the spec says in 6.4.1:
01061                  *"more specific selectors will override 
01062                  *more general ones"
01063                  *and
01064                  *"if two rules have the same weight, 
01065                  *origin and specificity, 
01066                  *the later specified wins"
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         /*TODO: this may leak. Check this out */
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  *PUBLIC METHODS
01103  ****************************************/
01104 
01105 /**
01106  * cr_sel_eng_new:
01107  *Creates a new instance of #CRSelEng.
01108  *
01109  *Returns the newly built instance of #CRSelEng of
01110  *NULL if an error occurs.
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  * cr_sel_eng_register_pseudo_class_sel_handler:
01145  *@a_this: the current instance of #CRSelEng
01146  *@a_pseudo_class_sel_name: the name of the pseudo class selector.
01147  *@a_pseudo_class_type: the type of the pseudo class selector.
01148  *@a_handler: the actual handler or callback to be called during
01149  *the selector evaluation process.
01150  *
01151  *Adds a new handler entry in the handlers entry table.
01152  *
01153  *Returns CR_OK, upon successful completion, an error code otherwise.
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  * cr_sel_eng_unregister_all_pseudo_class_sel_handlers:
01222  *@a_this: the current instance of #CRSelEng .
01223  *
01224  *Unregisters all the pseudo class sel handlers
01225  *and frees all the associated allocated datastructures.
01226  *
01227  *Returns CR_OK upon succesful completion, an error code
01228  *otherwise.
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  * cr_sel_eng_matches_node:
01289  *@a_this: the selection engine.
01290  *@a_sel: the simple selector against which the xml node 
01291  *is going to be matched.
01292  *@a_node: the node against which the selector is going to be matched.
01293  *@a_result: out parameter. The result of the match. Is set to
01294  *TRUE if the selector matches the node, FALSE otherwise. This value
01295  *is considered if and only if this functions returns CR_OK.
01296  *
01297  *Evaluates a chained list of simple selectors (known as a css2 selector).
01298  *Says wheter if this selector matches the xml node given in parameter or
01299  *not.
01300  *
01301  *Returns the CR_OK if the selection ran correctly, an error code otherwise.
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  * cr_sel_eng_get_matched_rulesets:
01323  *@a_this: the current instance of the selection engine.
01324  *@a_sheet: the stylesheet that holds the selectors.
01325  *@a_node: the xml node to consider during the walk thru
01326  *the stylesheet.
01327  *@a_rulesets: out parameter. A pointer to an array of
01328  *rulesets statement pointers. *a_rulesets is allocated by
01329  *this function and must be freed by the caller. However, the caller
01330  *must not alter the rulesets statements pointer because they
01331  *point to statements that are still in the css stylesheet.
01332  *@a_len: the length of *a_ruleset.
01333  *
01334  *Returns an array of pointers to selectors that matches
01335  *the xml node given in parameter.
01336  *
01337  *Returns CR_OK upon sucessfull completion, an error code otherwise.
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                          *compute the max size left for
01441                          *cr_sel_eng_get_matched_rulesets_real()'s output tab 
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                          *compute the max size left for
01460                          *cr_sel_eng_get_matched_rulesets_real()'s output tab 
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          *TODO, walk down the stmts_tab and build the
01475          *property_name/declaration hashtable.
01476          *Make sure one can walk from the declaration to
01477          *the stylesheet.
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  * cr_sel_eng_destroy:
01549  *@a_this: the current instance of the selection engine.
01550  *
01551  *The destructor of #CRSelEng
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 }

Generated on Thu Mar 9 19:19:08 2006 for Libcroco by  doxygen 1.4.6