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