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

cr-sel-eng.c

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

Generated on Sat Mar 20 02:38:43 2004 for Libcroco by doxygen 1.3.5