(root)/
gettext-0.22.4/
libtextstyle/
lib/
libcroco/
cr-sel-eng.c
       1  /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
       2  
       3  /* libcroco - Library for parsing and applying CSS
       4   * Copyright (C) 2006-2019 Free Software Foundation, Inc.
       5   *
       6   * This file is not part of the GNU gettext program, but is used with
       7   * GNU gettext.
       8   *
       9   * The original copyright notice is as follows:
      10   */
      11  
      12  /*
      13   * This file is part of The Croco Library
      14   *
      15   * Copyright (C) 2003-2004 Dodji Seketeli.  All Rights Reserved.
      16   *
      17   * This program is free software; you can redistribute it and/or
      18   * modify it under the terms of version 2.1 of the GNU Lesser General Public
      19   * License as published by the Free Software Foundation.
      20   *
      21   * This program is distributed in the hope that it will be useful,
      22   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      23   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      24   * GNU General Public License for more details.
      25   *
      26   * You should have received a copy of the GNU Lesser 
      27   * General Public License
      28   * along with this program; if not, write to the Free Software
      29   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
      30   * USA
      31   */
      32  
      33  #include <config.h>
      34  #include <string.h>
      35  #include "cr-sel-eng.h"
      36  
      37  /**
      38   *@CRSelEng:
      39   *
      40   *The definition of the  #CRSelEng class.
      41   *The #CRSelEng is actually the "Selection Engine"
      42   *class. This is highly experimental for at the moment and
      43   *its api is very likely to change in a near future.
      44   */
      45  
      46  #define PRIVATE(a_this) (a_this)->priv
      47  
      48  struct CRPseudoClassSelHandlerEntry {
      49          guchar *name;
      50          enum CRPseudoType type;
      51          CRPseudoClassSelectorHandler handler;
      52  };
      53  
      54  struct _CRSelEngPriv {
      55          /*not used yet */
      56          gboolean case_sensitive;
      57  
      58          CRStyleSheet *sheet;
      59          /**
      60           *where to store the next statement
      61           *to be visited so that we can remember
      62           *it from one method call to another.
      63           */
      64          CRStatement *cur_stmt;
      65          GList *pcs_handlers;
      66          gint pcs_handlers_size;
      67  } ;
      68  
      69  static gboolean class_add_sel_matches_node (CRAdditionalSel * a_add_sel,
      70                                              xmlNode * a_node);
      71  
      72  static gboolean id_add_sel_matches_node (CRAdditionalSel * a_add_sel,
      73                                           xmlNode * a_node);
      74  
      75  static gboolean attr_add_sel_matches_node (CRAdditionalSel * a_add_sel,
      76                                             xmlNode * a_node);
      77  
      78  static enum CRStatus sel_matches_node_real (CRSelEng * a_this,
      79                                              CRSimpleSel * a_sel,
      80                                              xmlNode * a_node,
      81                                              gboolean * a_result,
      82                                              gboolean a_eval_sel_list_from_end,
      83                                              gboolean a_recurse);
      84  
      85  static enum CRStatus cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
      86                                                             CRStyleSheet *
      87                                                             a_stylesheet,
      88                                                             xmlNode * a_node,
      89                                                             CRStatement **
      90                                                             a_rulesets,
      91                                                             gulong * a_len);
      92  
      93  static enum CRStatus put_css_properties_in_props_list (CRPropList ** a_props,
      94                                                         CRStatement *
      95                                                         a_ruleset);
      96  
      97  static gboolean pseudo_class_add_sel_matches_node (CRSelEng * a_this,
      98                                                     CRAdditionalSel *
      99                                                     a_add_sel,
     100                                                     xmlNode * a_node);
     101  
     102  static gboolean lang_pseudo_class_handler (CRSelEng * a_this,
     103                                             CRAdditionalSel * a_sel,
     104                                             xmlNode * a_node);
     105  
     106  static gboolean first_child_pseudo_class_handler (CRSelEng * a_this,
     107                                                    CRAdditionalSel * a_sel,
     108                                                    xmlNode * a_node);
     109  
     110  static xmlNode *get_next_element_node (xmlNode * a_node);
     111  
     112  static xmlNode *get_next_child_element_node (xmlNode * a_node);
     113  
     114  static xmlNode *get_prev_element_node (xmlNode * a_node);
     115  
     116  static xmlNode *get_next_parent_element_node (xmlNode * a_node);
     117  
     118  /* Quick strcmp.  Test only for == 0 or != 0, not < 0 or > 0.  */
     119  #define strqcmp(str,lit,lit_len) \
     120    (strlen (str) != (lit_len) || memcmp (str, lit, lit_len))
     121  
     122  static gboolean
     123  lang_pseudo_class_handler (CRSelEng * a_this,
     124                             CRAdditionalSel * a_sel, xmlNode * a_node)
     125  {
     126          xmlNode *node = a_node;
     127          xmlChar *val = NULL;
     128          gboolean result = FALSE;
     129  
     130          g_return_val_if_fail (a_this && PRIVATE (a_this)
     131                                && a_sel && a_sel->content.pseudo
     132                                && a_sel->content.pseudo
     133                                && a_sel->content.pseudo->name
     134                                && a_sel->content.pseudo->name->stryng
     135                                && a_node, CR_BAD_PARAM_ERROR);
     136  
     137          if (strqcmp (a_sel->content.pseudo->name->stryng->str, 
     138                       "lang", 4)
     139              || a_sel->content.pseudo->type != FUNCTION_PSEUDO) {
     140                  cr_utils_trace_info ("This handler is for :lang only");
     141                  return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
     142          }
     143          /*lang code should exist and be at least of length 2 */
     144          if (!a_sel->content.pseudo->extra
     145              || !a_sel->content.pseudo->extra->stryng
     146              || a_sel->content.pseudo->extra->stryng->len < 2)
     147                  return FALSE;
     148          for (; node; node = get_next_parent_element_node (node)) {
     149                  val = xmlGetProp (node, (const xmlChar *) "lang");
     150                  if (val
     151                      && !strqcmp ((const char *) val,
     152                                   a_sel->content.pseudo->extra->stryng->str,
     153                                   a_sel->content.pseudo->extra->stryng->len)) {
     154                          result = TRUE;
     155                  }
     156                  if (val) {
     157                          xmlFree (val);
     158                          val = NULL;
     159                  }
     160          }
     161  
     162          return result;
     163  }
     164  
     165  static gboolean
     166  first_child_pseudo_class_handler (CRSelEng * a_this,
     167                                    CRAdditionalSel * a_sel, xmlNode * a_node)
     168  {
     169          xmlNode *node = NULL;
     170  
     171          g_return_val_if_fail (a_this && PRIVATE (a_this)
     172                                && a_sel && a_sel->content.pseudo
     173                                && a_sel->content.pseudo
     174                                && a_sel->content.pseudo->name
     175                                && a_sel->content.pseudo->name->stryng
     176                                && a_node, CR_BAD_PARAM_ERROR);
     177  
     178          if (strcmp (a_sel->content.pseudo->name->stryng->str,
     179                      "first-child")
     180              || a_sel->content.pseudo->type != IDENT_PSEUDO) {
     181                  cr_utils_trace_info ("This handler is for :first-child only");
     182                  return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
     183          }
     184          if (!a_node->parent)
     185                  return FALSE;
     186          node = get_next_child_element_node (a_node->parent);
     187          if (node == a_node)
     188                  return TRUE;
     189          return FALSE;
     190  }
     191  
     192  static gboolean
     193  pseudo_class_add_sel_matches_node (CRSelEng * a_this,
     194                                     CRAdditionalSel * a_add_sel,
     195                                     xmlNode * a_node)
     196  {
     197          enum CRStatus status = CR_OK;
     198          CRPseudoClassSelectorHandler handler = NULL;
     199  
     200          g_return_val_if_fail (a_this && PRIVATE (a_this)
     201                                && a_add_sel
     202                                && a_add_sel->content.pseudo
     203                                && a_add_sel->content.pseudo->name
     204                                && a_add_sel->content.pseudo->name->stryng
     205                                && a_add_sel->content.pseudo->name->stryng->str
     206                                && a_node, CR_BAD_PARAM_ERROR);
     207  
     208          status = cr_sel_eng_get_pseudo_class_selector_handler
     209                  (a_this, (guchar *) a_add_sel->content.pseudo->name->stryng->str,
     210                   a_add_sel->content.pseudo->type, &handler);
     211          if (status != CR_OK || !handler)
     212                  return FALSE;
     213  
     214          return handler (a_this, a_add_sel, a_node);
     215  }
     216  
     217  /**
     218   *@param a_add_sel the class additional selector to consider.
     219   *@param a_node the xml node to consider.
     220   *@return TRUE if the class additional selector matches
     221   *the xml node given in argument, FALSE otherwise.
     222   */
     223  static gboolean
     224  class_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
     225  {
     226          gboolean result = FALSE;
     227          xmlChar *klass = NULL,
     228                  *cur = NULL;
     229  
     230          g_return_val_if_fail (a_add_sel
     231                                && a_add_sel->type == CLASS_ADD_SELECTOR
     232                                && a_add_sel->content.class_name
     233                                && a_add_sel->content.class_name->stryng
     234                                && a_add_sel->content.class_name->stryng->str
     235                                && a_node, FALSE);
     236  
     237          if (xmlHasProp (a_node, (const xmlChar *) "class")) {
     238                  klass = xmlGetProp (a_node, (const xmlChar *) "class");
     239                  for (cur = klass; cur && *cur; cur++) {
     240                          while (cur && *cur
     241                                 && cr_utils_is_white_space (*cur) 
     242                                 == TRUE)
     243                                  cur++;
     244  
     245                          if (!strncmp ((const char *) cur, 
     246                                        a_add_sel->content.class_name->stryng->str,
     247                                        a_add_sel->content.class_name->stryng->len)) {
     248                                  cur += a_add_sel->content.class_name->stryng->len;
     249                                  if ((cur && !*cur)
     250                                      || cr_utils_is_white_space (*cur) == TRUE)
     251                                          result = TRUE;
     252                          } else {  /* if it doesn't match,  */
     253                                  /*   then skip to next whitespace character to try again */
     254                                  while (cur && *cur && !(cr_utils_is_white_space(*cur) == TRUE)) 
     255                                          cur++;
     256                          }
     257                          if (cur && !*cur)
     258                                  break ;
     259                  }
     260          }
     261          if (klass) {
     262                  xmlFree (klass);
     263                  klass = NULL;
     264          }
     265          return result;
     266  
     267  }
     268  
     269  /**
     270   *@return TRUE if the additional attribute selector matches
     271   *the current xml node given in argument, FALSE otherwise.
     272   *@param a_add_sel the additional attribute selector to consider.
     273   *@param a_node the xml node to consider.
     274   */
     275  static gboolean
     276  id_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
     277  {
     278          gboolean result = FALSE;
     279          xmlChar *id = NULL;
     280  
     281          g_return_val_if_fail (a_add_sel
     282                                && a_add_sel->type == ID_ADD_SELECTOR
     283                                && a_add_sel->content.id_name
     284                                && a_add_sel->content.id_name->stryng
     285                                && a_add_sel->content.id_name->stryng->str
     286                                && a_node, FALSE);
     287          g_return_val_if_fail (a_add_sel
     288                                && a_add_sel->type == ID_ADD_SELECTOR
     289                                && a_node, FALSE);
     290  
     291          if (xmlHasProp (a_node, (const xmlChar *) "id")) {
     292                  id = xmlGetProp (a_node, (const xmlChar *) "id");
     293                  if (!strqcmp ((const char *) id, a_add_sel->content.id_name->stryng->str,
     294                                a_add_sel->content.id_name->stryng->len)) {
     295                          result = TRUE;
     296                  }
     297          }
     298          if (id) {
     299                  xmlFree (id);
     300                  id = NULL;
     301          }
     302          return result;
     303  }
     304  
     305  /**
     306   *Returns TRUE if the instance of #CRAdditional selector matches
     307   *the node given in parameter, FALSE otherwise.
     308   *@param a_add_sel the additional selector to evaluate.
     309   *@param a_node the xml node against whitch the selector is to
     310   *be evaluated
     311   *return TRUE if the additional selector matches the current xml node
     312   *FALSE otherwise.
     313   */
     314  static gboolean
     315  attr_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
     316  {
     317          CRAttrSel *cur_sel = NULL;
     318  
     319          g_return_val_if_fail (a_add_sel
     320                                && a_add_sel->type == ATTRIBUTE_ADD_SELECTOR
     321                                && a_node, FALSE);
     322  
     323          for (cur_sel = a_add_sel->content.attr_sel;
     324               cur_sel; cur_sel = cur_sel->next) {
     325                  switch (cur_sel->match_way) {
     326                  case SET:
     327                          if (!cur_sel->name 
     328                              || !cur_sel->name->stryng
     329                              || !cur_sel->name->stryng->str)
     330                                  return FALSE;
     331  
     332                          if (!xmlHasProp (a_node,
     333                                           (const xmlChar *) cur_sel->name->stryng->str))
     334                                  return FALSE;
     335                          break;
     336  
     337                  case EQUALS:
     338                          {
     339                                  xmlChar *value = NULL;
     340  
     341                                  if (!cur_sel->name 
     342                                      || !cur_sel->name->stryng
     343                                      || !cur_sel->name->stryng->str
     344                                      || !cur_sel->value
     345                                      || !cur_sel->value->stryng
     346                                      || !cur_sel->value->stryng->str)
     347                                          return FALSE;
     348  
     349                                  if (!xmlHasProp 
     350                                      (a_node, 
     351                                       (const xmlChar *) cur_sel->name->stryng->str))
     352                                          return FALSE;
     353  
     354                                  value = xmlGetProp 
     355                                          (a_node,
     356                                           (const xmlChar *) cur_sel->name->stryng->str);
     357  
     358                                  if (value
     359                                      && strcmp 
     360                                      ((const char *) value, 
     361                                       cur_sel->value->stryng->str)) {
     362                                          xmlFree (value);
     363                                          return FALSE;
     364                                  }
     365                                  xmlFree (value);
     366                          }
     367                          break;
     368  
     369                  case INCLUDES:
     370                          {
     371                                  xmlChar *value = NULL,
     372                                          *ptr1 = NULL,
     373                                          *ptr2 = NULL,
     374                                          *cur = NULL;
     375                                  gboolean found = FALSE;
     376  
     377                                  if (!xmlHasProp 
     378                                      (a_node, 
     379                                       (const xmlChar *) cur_sel->name->stryng->str))
     380                                          return FALSE;
     381                                  value = xmlGetProp 
     382                                          (a_node,
     383                                           (const xmlChar *) cur_sel->name->stryng->str);
     384  
     385                                  if (!value)
     386                                          return FALSE;
     387  
     388                                  /*
     389                                   *here, make sure value is a space
     390                                   *separated list of "words", where one
     391                                   *value is exactly cur_sel->value->str
     392                                   */
     393                                  for (cur = value; *cur; cur++) {
     394                                          /*
     395                                           *set ptr1 to the first non white space
     396                                           *char addr.
     397                                           */
     398                                          while (cr_utils_is_white_space
     399                                                 (*cur) == TRUE && *cur)
     400                                                  cur++;
     401                                          if (!*cur)
     402                                                  break;
     403                                          ptr1 = cur;
     404  
     405                                          /*
     406                                           *set ptr2 to the end the word.
     407                                           */
     408                                          while (cr_utils_is_white_space
     409                                                 (*cur) == FALSE && *cur)
     410                                                  cur++;
     411                                          cur--;
     412                                          ptr2 = cur;
     413  
     414                                          if (!strncmp
     415                                              ((const char *) ptr1, 
     416                                               cur_sel->value->stryng->str,
     417                                               ptr2 - ptr1 + 1)) {
     418                                                  found = TRUE;
     419                                                  break;
     420                                          }
     421                                          ptr1 = ptr2 = NULL;
     422                                  }
     423  
     424                                  if (found == FALSE) {
     425                                          xmlFree (value);
     426                                          return FALSE;
     427                                  }
     428                                  xmlFree (value);
     429                          }
     430                          break;
     431  
     432                  case DASHMATCH:
     433                          {
     434                                  xmlChar *value = NULL,
     435                                          *ptr1 = NULL,
     436                                          *ptr2 = NULL,
     437                                          *cur = NULL;
     438                                  gboolean found = FALSE;
     439  
     440                                  if (!xmlHasProp 
     441                                      (a_node, 
     442                                       (const xmlChar *) cur_sel->name->stryng->str))
     443                                          return FALSE;
     444                                  value = xmlGetProp 
     445                                          (a_node,
     446                                           (const xmlChar *) cur_sel->name->stryng->str);
     447  
     448                                  /*
     449                                   *here, make sure value is an hyphen
     450                                   *separated list of "words", each of which
     451                                   *starting with "cur_sel->value->str"
     452                                   */
     453                                  for (cur = value; *cur; cur++) {
     454                                          if (*cur == '-')
     455                                                  cur++;
     456                                          ptr1 = cur;
     457  
     458                                          while (*cur != '-' && *cur)
     459                                                  cur++;
     460                                          cur--;
     461                                          ptr2 = cur;
     462  
     463                                          if (g_strstr_len
     464                                              ((const gchar *) ptr1, ptr2 - ptr1 + 1,
     465                                               cur_sel->value->stryng->str)
     466                                              == (gchar *) ptr1) {
     467                                                  found = TRUE;
     468                                                  break;
     469                                          }
     470                                  }
     471  
     472                                  if (found == FALSE) {
     473                                          xmlFree (value);
     474                                          return FALSE;
     475                                  }
     476                                  xmlFree (value);
     477                          }
     478                          break;
     479                  default:
     480                          return FALSE;
     481                  }
     482          }
     483  
     484          return TRUE;
     485  }
     486  
     487  /**
     488   *Evaluates if a given additional selector matches an xml node.
     489   *@param a_add_sel the additional selector to consider.
     490   *@param a_node the xml node to consider.
     491   *@return TRUE is a_add_sel matches a_node, FALSE otherwise.
     492   */
     493  static gboolean
     494  additional_selector_matches_node (CRSelEng * a_this,
     495                                    CRAdditionalSel * a_add_sel,
     496                                    xmlNode * a_node)
     497  {
     498          CRAdditionalSel *cur_add_sel = NULL, *tail = NULL ;
     499          gboolean evaluated = FALSE ;
     500  
     501          for (tail = a_add_sel ; 
     502               tail && tail->next; 
     503               tail = tail->next) ;
     504  
     505          g_return_val_if_fail (tail, FALSE) ;
     506  
     507          for (cur_add_sel = tail ;
     508               cur_add_sel ;
     509               cur_add_sel = cur_add_sel->prev) {
     510  
     511                  evaluated = TRUE ;
     512                  if (cur_add_sel->type == NO_ADD_SELECTOR) {
     513                          return FALSE;
     514                  }
     515  
     516                  if (cur_add_sel->type == CLASS_ADD_SELECTOR
     517                      && cur_add_sel->content.class_name
     518                      && cur_add_sel->content.class_name->stryng
     519                      && cur_add_sel->content.class_name->stryng->str) {
     520                          if (class_add_sel_matches_node (cur_add_sel,
     521                                                          a_node) == FALSE) {
     522                                  return FALSE;
     523                          }
     524                          continue ;
     525                  } else if (cur_add_sel->type == ID_ADD_SELECTOR
     526                             && cur_add_sel->content.id_name
     527                             && cur_add_sel->content.id_name->stryng
     528                             && cur_add_sel->content.id_name->stryng->str) {
     529                          if (id_add_sel_matches_node (cur_add_sel, a_node) == FALSE) {
     530                                  return FALSE;
     531                          }
     532                          continue ;
     533                  } else if (cur_add_sel->type == ATTRIBUTE_ADD_SELECTOR
     534                             && cur_add_sel->content.attr_sel) {
     535                          /*
     536                           *here, call a function that does the match
     537                           *against an attribute additionnal selector
     538                           *and an xml node.
     539                           */
     540                          if (attr_add_sel_matches_node (cur_add_sel, a_node)
     541                              == FALSE) {
     542                                  return FALSE;
     543                          }
     544                          continue ;
     545                  } else if (cur_add_sel->type == PSEUDO_CLASS_ADD_SELECTOR
     546                             && cur_add_sel->content.pseudo) {
     547                          if (pseudo_class_add_sel_matches_node
     548                              (a_this, cur_add_sel, a_node) == TRUE) {
     549                                  return TRUE;
     550                          }
     551                          return FALSE;
     552                  }
     553          }
     554          if (evaluated == TRUE)
     555                  return TRUE;
     556          return FALSE ;
     557  }
     558  
     559  static xmlNode *
     560  get_next_element_node (xmlNode * a_node)
     561  {
     562          xmlNode *cur_node = NULL;
     563  
     564          g_return_val_if_fail (a_node, NULL);
     565  
     566          cur_node = a_node->next;
     567          while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
     568                  cur_node = cur_node->next;
     569          }
     570          return cur_node;
     571  }
     572  
     573  static xmlNode *
     574  get_next_child_element_node (xmlNode * a_node)
     575  {
     576          xmlNode *cur_node = NULL;
     577  
     578          g_return_val_if_fail (a_node, NULL);
     579  
     580          cur_node = a_node->children;
     581          if (!cur_node)
     582                  return cur_node;
     583          if (a_node->children->type == XML_ELEMENT_NODE)
     584                  return a_node->children;
     585          return get_next_element_node (a_node->children);
     586  }
     587  
     588  static xmlNode *
     589  get_prev_element_node (xmlNode * a_node)
     590  {
     591          xmlNode *cur_node = NULL;
     592  
     593          g_return_val_if_fail (a_node, NULL);
     594  
     595          cur_node = a_node->prev;
     596          while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
     597                  cur_node = cur_node->prev;
     598          }
     599          return cur_node;
     600  }
     601  
     602  static xmlNode *
     603  get_next_parent_element_node (xmlNode * a_node)
     604  {
     605          xmlNode *cur_node = NULL;
     606  
     607          g_return_val_if_fail (a_node, NULL);
     608  
     609          cur_node = a_node->parent;
     610          while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
     611                  cur_node = cur_node->parent;
     612          }
     613          return cur_node;
     614  }
     615  
     616  /**
     617   *Evaluate a selector (a simple selectors list) and says
     618   *if it matches the xml node given in parameter.
     619   *The algorithm used here is the following:
     620   *Walk the combinator separated list of simple selectors backward, starting
     621   *from the end of the list. For each simple selector, looks if
     622   *if matches the current node.
     623   *
     624   *@param a_this the selection engine.
     625   *@param a_sel the simple selection list.
     626   *@param a_node the xml node.
     627   *@param a_result out parameter. Set to true if the
     628   *selector matches the xml node, FALSE otherwise.
     629   *@param a_recurse if set to TRUE, the function will walk to
     630   *the next simple selector (after the evaluation of the current one) 
     631   *and recursively evaluate it. Must be usually set to TRUE unless you
     632   *know what you are doing.
     633   */
     634  static enum CRStatus
     635  sel_matches_node_real (CRSelEng * a_this, CRSimpleSel * a_sel,
     636                         xmlNode * a_node, gboolean * a_result,
     637                         gboolean a_eval_sel_list_from_end,
     638                         gboolean a_recurse)
     639  {
     640          CRSimpleSel *cur_sel = NULL;
     641          xmlNode *cur_node = NULL;
     642  
     643          g_return_val_if_fail (a_this && PRIVATE (a_this)
     644                                && a_this && a_node
     645                                && a_result, CR_BAD_PARAM_ERROR);
     646  
     647          *a_result = FALSE;
     648  
     649          if (a_node->type != XML_ELEMENT_NODE)
     650                  return CR_OK;
     651  
     652          if (a_eval_sel_list_from_end == TRUE) {
     653                  /*go and get the last simple selector of the list */
     654                  for (cur_sel = a_sel;
     655                       cur_sel && cur_sel->next; cur_sel = cur_sel->next) ;
     656          } else {
     657                  cur_sel = a_sel;
     658          }
     659  
     660          for (cur_node = a_node; cur_sel; cur_sel = cur_sel->prev) {
     661                  if (((cur_sel->type_mask & TYPE_SELECTOR)
     662                       && (cur_sel->name 
     663                           && cur_sel->name->stryng
     664                           && cur_sel->name->stryng->str)
     665                       && (!strcmp (cur_sel->name->stryng->str,
     666                                    (const char *) cur_node->name)))
     667                      || (cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
     668                          /*
     669                           *this simple selector
     670                           *matches the current xml node
     671                           *Let's see if the preceding
     672                           *simple selectors also match
     673                           *their xml node counterpart.
     674                           */
     675                          if (cur_sel->add_sel) {
     676                                  if (additional_selector_matches_node (a_this, cur_sel->add_sel, 
     677                                                                        cur_node) == TRUE) {
     678                                          goto walk_a_step_in_expr;
     679                                  } else {
     680                                          goto done;
     681                                  }
     682                          } else {
     683                                  goto walk_a_step_in_expr;
     684                          }                                
     685                  } 
     686                  if (!(cur_sel->type_mask & TYPE_SELECTOR)
     687                      && !(cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
     688                          if (!cur_sel->add_sel) {
     689                                  goto done;
     690                          }
     691                          if (additional_selector_matches_node
     692                              (a_this, cur_sel->add_sel, cur_node)
     693                              == TRUE) {
     694                                  goto walk_a_step_in_expr;
     695                          } else {
     696                                  goto done;
     697                          }
     698                  } else {
     699                          goto done ;
     700                  }
     701  
     702          walk_a_step_in_expr:
     703                  if (a_recurse == FALSE) {
     704                          *a_result = TRUE;
     705                          goto done;
     706                  }
     707  
     708                  /*
     709                   *here, depending on the combinator of cur_sel
     710                   *choose the axis of the xml tree traversal
     711                   *and walk one step in the xml tree.
     712                   */
     713                  if (!cur_sel->prev)
     714                          break;
     715  
     716                  switch (cur_sel->combinator) {
     717                  case NO_COMBINATOR:
     718                          break;
     719  
     720                  case COMB_WS:  /*descendant selector */
     721                  {
     722                          xmlNode *n = NULL;
     723                          enum CRStatus status = CR_OK;
     724                          gboolean matches = FALSE;
     725  
     726                          /*
     727                           *walk the xml tree upward looking for a parent
     728                           *node that matches the preceding selector.
     729                           */
     730                          for (n = cur_node->parent; n; n = n->parent) {
     731                                  status = sel_matches_node_real
     732                                          (a_this, cur_sel->prev,
     733                                           n, &matches, FALSE, TRUE);
     734  
     735                                  if (status != CR_OK)
     736                                          goto done;
     737  
     738                                  if (matches == TRUE) {
     739                                          cur_node = n ;
     740                                          break;
     741                                  }
     742                          }
     743  
     744                          if (!n) {
     745                                  /*
     746                                   *didn't find any ancestor that matches
     747                                   *the previous simple selector.
     748                                   */
     749                                  goto done;
     750                          }
     751                          /*
     752                           *in this case, the preceding simple sel
     753                           *will have been interpreted twice, which
     754                           *is a cpu and mem waste ... I need to find
     755                           *another way to do this. Anyway, this is
     756                           *my first attempt to write this function and
     757                           *I am a bit clueless.
     758                           */
     759                          break;
     760                  }
     761  
     762                  case COMB_PLUS:
     763                          cur_node = get_prev_element_node (cur_node);
     764                          if (!cur_node)
     765                                  goto done;
     766                          break;
     767  
     768                  case COMB_GT:
     769                          cur_node = get_next_parent_element_node (cur_node);
     770                          if (!cur_node)
     771                                  goto done;
     772                          break;
     773  
     774                  default:
     775                          goto done;
     776                  }
     777                  continue;
     778          }
     779  
     780          /*
     781           *if we reached this point, it means the selector matches
     782           *the xml node.
     783           */
     784          *a_result = TRUE;
     785  
     786   done:
     787          return CR_OK;
     788  }
     789  
     790  
     791  /**
     792   *Returns  array of the ruleset statements that matches the
     793   *given xml node.
     794   *The engine keeps in memory the last statement he
     795   *visited during the match. So, the next call
     796   *to this function will eventually return a rulesets list starting
     797   *from the last ruleset statement visited during the previous call.
     798   *The enable users to get matching rulesets in an incremental way.
     799   *Note that for each statement returned, 
     800   *the engine calculates the specificity of the selector
     801   *that matched the xml node and stores it in the "specifity" field
     802   *of the statement structure.
     803   *
     804   *@param a_sel_eng the current selection engine
     805   *@param a_node the xml node for which the request
     806   *is being made.
     807   *@param a_sel_list the list of selectors to perform the search in.
     808   *@param a_rulesets in/out parameter. A pointer to the
     809   *returned array of rulesets statements that match the xml node
     810   *given in parameter. The caller allocates the array before calling this
     811   *function.
     812   *@param a_len in/out parameter the length (in sizeof (#CRStatement*)) 
     813   *of the returned array.
     814   *(the length of a_rulesets, more precisely).
     815   *The caller must set it to the length of a_ruleset prior to calling this
     816   *function. In return, the function sets it to the length 
     817   *(in sizeof (#CRStatement)) of the actually returned CRStatement array.
     818   *@return CR_OUTPUT_TOO_SHORT_ERROR if found more rulesets than the size
     819   *of the a_rulesets array. In this case, the first *a_len rulesets found
     820   *are put in a_rulesets, and a further call will return the following
     821   *ruleset(s) following the same principle.
     822   *@return CR_OK if all the rulesets found have been returned. In this
     823   *case, *a_len is set to the actual number of ruleset found.
     824   *@return CR_BAD_PARAM_ERROR in case any of the given parameter are
     825   *bad (e.g null pointer).
     826   *@return CR_ERROR if any other error occurred.
     827   */
     828  static enum CRStatus
     829  cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
     830                                        CRStyleSheet * a_stylesheet,
     831                                        xmlNode * a_node,
     832                                        CRStatement ** a_rulesets,
     833                                        gulong * a_len)
     834  {
     835          CRStatement *cur_stmt = NULL;
     836          CRSelector *sel_list = NULL,
     837                  *cur_sel = NULL;
     838          gboolean matches = FALSE;
     839          enum CRStatus status = CR_OK;
     840          gulong i = 0;
     841  
     842          g_return_val_if_fail (a_this
     843                                && a_stylesheet
     844                                && a_node && a_rulesets, CR_BAD_PARAM_ERROR);
     845  
     846          if (!a_stylesheet->statements) {
     847                  *a_rulesets = NULL;
     848                  *a_len = 0;
     849                  return CR_OK;
     850          }
     851  
     852          /*
     853           *if this stylesheet is "new one"
     854           *let's remember it for subsequent calls.
     855           */
     856          if (PRIVATE (a_this)->sheet != a_stylesheet) {
     857                  PRIVATE (a_this)->sheet = a_stylesheet;
     858                  PRIVATE (a_this)->cur_stmt = a_stylesheet->statements;
     859          }
     860  
     861          /*
     862           *walk through the list of statements and,
     863           *get the selectors list inside the statements that
     864           *contain some, and try to match our xml node in these
     865           *selectors lists.
     866           */
     867          for (cur_stmt = PRIVATE (a_this)->cur_stmt, i = 0;
     868               (PRIVATE (a_this)->cur_stmt = cur_stmt);
     869               cur_stmt = cur_stmt->next) {
     870                  /*
     871                   *initialyze the selector list in which we will
     872                   *really perform the search.
     873                   */
     874                  sel_list = NULL;
     875  
     876                  /*
     877                   *get the the damn selector list in 
     878                   *which we have to look
     879                   */
     880                  switch (cur_stmt->type) {
     881                  case RULESET_STMT:
     882                          if (cur_stmt->kind.ruleset
     883                              && cur_stmt->kind.ruleset->sel_list) {
     884                                  sel_list = cur_stmt->kind.ruleset->sel_list;
     885                          }
     886                          break;
     887  
     888                  case AT_MEDIA_RULE_STMT:
     889                          if (cur_stmt->kind.media_rule
     890                              && cur_stmt->kind.media_rule->rulesets
     891                              && cur_stmt->kind.media_rule->rulesets->
     892                              kind.ruleset
     893                              && cur_stmt->kind.media_rule->rulesets->
     894                              kind.ruleset->sel_list) {
     895                                  sel_list =
     896                                          cur_stmt->kind.media_rule->
     897                                          rulesets->kind.ruleset->sel_list;
     898                          }
     899                          break;
     900  
     901                  case AT_IMPORT_RULE_STMT:
     902                          /*
     903                           *some recursivity may be needed here.
     904                           *I don't like this :(
     905                           */
     906                          break;
     907                  default:
     908                          break;
     909                  }
     910  
     911                  if (!sel_list)
     912                          continue;
     913  
     914                  /*
     915                   *now, we have a comma separated selector list to look in.
     916                   *let's walk it and try to match the xml_node
     917                   *on each item of the list.
     918                   */
     919                  for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next) {
     920                          if (!cur_sel->simple_sel)
     921                                  continue;
     922  
     923                          status = cr_sel_eng_matches_node
     924                                  (a_this, cur_sel->simple_sel,
     925                                   a_node, &matches);
     926  
     927                          if (status == CR_OK && matches == TRUE) {
     928                                  /*
     929                                   *bingo!!! we found one ruleset that
     930                                   *matches that fucking node.
     931                                   *lets put it in the out array.
     932                                   */
     933  
     934                                  if (i < *a_len) {
     935                                          a_rulesets[i] = cur_stmt;
     936                                          i++;
     937  
     938                                          /*
     939                                           *For the cascade computing algorithm
     940                                           *(which is gonna take place later)
     941                                           *we must compute the specificity
     942                                           *(css2 spec chap 6.4.1) of the selector
     943                                           *that matched the current xml node
     944                                           *and store it in the css2 statement
     945                                           *(statement == ruleset here).
     946                                           */
     947                                          status = cr_simple_sel_compute_specificity (cur_sel->simple_sel);
     948  
     949                                          g_return_val_if_fail (status == CR_OK,
     950                                                                CR_ERROR);
     951                                          cur_stmt->specificity =
     952                                                  cur_sel->simple_sel->
     953                                                  specificity;
     954                                  } else
     955                                  {
     956                                          *a_len = i;
     957                                          return CR_OUTPUT_TOO_SHORT_ERROR;
     958                                  }
     959                          }
     960                  }
     961          }
     962  
     963          /*
     964           *if we reached this point, it means
     965           *we reached the end of stylesheet.
     966           *no need to store any info about the stylesheet
     967           *anymore.
     968           */
     969          g_return_val_if_fail (!PRIVATE (a_this)->cur_stmt, CR_ERROR);
     970          PRIVATE (a_this)->sheet = NULL;
     971          *a_len = i;
     972          return CR_OK;
     973  }
     974  
     975  static enum CRStatus
     976  put_css_properties_in_props_list (CRPropList ** a_props, CRStatement * a_stmt)
     977  {
     978          CRPropList *props = NULL,
     979                  *pair = NULL,
     980                  *tmp_props = NULL;
     981          CRDeclaration *cur_decl = NULL;
     982  
     983          g_return_val_if_fail (a_props && a_stmt
     984                                && a_stmt->type == RULESET_STMT
     985                                && a_stmt->kind.ruleset, CR_BAD_PARAM_ERROR);
     986  
     987          props = *a_props;
     988  
     989          for (cur_decl = a_stmt->kind.ruleset->decl_list;
     990               cur_decl; cur_decl = cur_decl->next) {
     991                  CRDeclaration *decl;
     992  
     993                  decl = NULL;
     994                  pair = NULL;
     995  
     996                  if (!cur_decl->property 
     997                      || !cur_decl->property->stryng
     998                      || !cur_decl->property->stryng->str)
     999                          continue;
    1000                  /*
    1001                   *First, test if the property is not
    1002                   *already present in our properties list
    1003                   *If yes, apply the cascading rules to
    1004                   *compute the precedence. If not, insert
    1005                   *the property into the list
    1006                   */
    1007                  cr_prop_list_lookup_prop (props,
    1008                                            cur_decl->property, 
    1009                                            &pair);
    1010  
    1011                  if (!pair) {
    1012                          tmp_props = cr_prop_list_append2
    1013                                  (props, cur_decl->property, cur_decl);
    1014                          if (tmp_props) {
    1015                                  props = tmp_props;
    1016                                  tmp_props = NULL;
    1017                          }
    1018                          continue;
    1019                  }
    1020  
    1021                  /*
    1022                   *A property with the same name already exists.
    1023                   *We must apply here 
    1024                   *some cascading rules
    1025                   *to compute the precedence.
    1026                   */
    1027                  cr_prop_list_get_decl (pair, &decl);
    1028                  g_return_val_if_fail (decl, CR_ERROR);
    1029  
    1030                  /*
    1031                   *first, look at the origin.
    1032                   *6.4.1 says: 
    1033                   *"for normal declarations, 
    1034                   *author style sheets override user 
    1035                   *style sheets which override 
    1036                   *the default style sheet."
    1037                   */
    1038                  if (decl->parent_statement
    1039                      && decl->parent_statement->parent_sheet
    1040                      && (decl->parent_statement->parent_sheet->origin
    1041                          < a_stmt->parent_sheet->origin)) {
    1042                          /*
    1043                           *if the already selected declaration
    1044                           *is marked as being !important the current
    1045                           *declaration must not overide it 
    1046                           *(unless the already selected declaration 
    1047                           *has an UA origin)
    1048                           */
    1049                          if (decl->important == TRUE
    1050                              && decl->parent_statement->parent_sheet->origin
    1051                              != ORIGIN_UA) {
    1052                                  continue;
    1053                          }
    1054                          tmp_props = cr_prop_list_unlink (props, pair);
    1055                          if (props) {
    1056                                  cr_prop_list_destroy (pair);
    1057                          }
    1058                          props = tmp_props;
    1059                          tmp_props = NULL;
    1060                          props = cr_prop_list_append2
    1061                                  (props, cur_decl->property, cur_decl);
    1062  
    1063                          continue;
    1064                  } else if (decl->parent_statement
    1065                             && decl->parent_statement->parent_sheet
    1066                             && (decl->parent_statement->
    1067                                 parent_sheet->origin
    1068                                 > a_stmt->parent_sheet->origin)) {
    1069                          cr_utils_trace_info
    1070                                  ("We should not reach this line\n");
    1071                          continue;
    1072                  }
    1073  
    1074                  /*
    1075                   *A property with the same
    1076                   *name and the same origin already exists.
    1077                   *shit. This is lasting longer than expected ...
    1078                   *Luckily, the spec says in 6.4.1:
    1079                   *"more specific selectors will override 
    1080                   *more general ones"
    1081                   *and
    1082                   *"if two rules have the same weight, 
    1083                   *origin and specificity, 
    1084                   *the later specified wins"
    1085                   */
    1086                  if (a_stmt->specificity
    1087                      >= decl->parent_statement->specificity) {
    1088                          if (decl->important == TRUE)
    1089                                  continue;
    1090                          props = cr_prop_list_unlink (props, pair);
    1091                          if (pair) {
    1092                                  cr_prop_list_destroy (pair);
    1093                                  pair = NULL;
    1094                          }
    1095                          props = cr_prop_list_append2 (props,
    1096                                                        cur_decl->property,
    1097                                                        cur_decl);
    1098                  }
    1099          }
    1100          /*TODO: this may leak. Check this out */
    1101          *a_props = props;
    1102  
    1103          return CR_OK;
    1104  }
    1105  
    1106  static void
    1107  set_style_from_props (CRStyle * a_style, CRPropList * a_props)
    1108  {
    1109          CRPropList *cur = NULL;
    1110          CRDeclaration *decl = NULL;
    1111  
    1112          for (cur = a_props; cur; cur = cr_prop_list_get_next (cur)) {
    1113                  cr_prop_list_get_decl (cur, &decl);
    1114                  cr_style_set_style_from_decl (a_style, decl);
    1115                  decl = NULL;
    1116          }
    1117  }
    1118  
    1119  /****************************************
    1120   *PUBLIC METHODS
    1121   ****************************************/
    1122  
    1123  /**
    1124   * cr_sel_eng_new:
    1125   *Creates a new instance of #CRSelEng.
    1126   *
    1127   *Returns the newly built instance of #CRSelEng of
    1128   *NULL if an error occurs.
    1129   */
    1130  CRSelEng *
    1131  cr_sel_eng_new (void)
    1132  {
    1133          CRSelEng *result = NULL;
    1134  
    1135          result = g_try_malloc (sizeof (CRSelEng));
    1136          if (!result) {
    1137                  cr_utils_trace_info ("Out of memory");
    1138                  return NULL;
    1139          }
    1140          memset (result, 0, sizeof (CRSelEng));
    1141  
    1142          PRIVATE (result) = g_try_malloc (sizeof (CRSelEngPriv));
    1143          if (!PRIVATE (result)) {
    1144                  cr_utils_trace_info ("Out of memory");
    1145                  g_free (result);
    1146                  return NULL;
    1147          }
    1148          memset (PRIVATE (result), 0, sizeof (CRSelEngPriv));
    1149          cr_sel_eng_register_pseudo_class_sel_handler
    1150                  (result, (guchar *) "first-child",
    1151                   IDENT_PSEUDO, (CRPseudoClassSelectorHandler)
    1152                   first_child_pseudo_class_handler);
    1153          cr_sel_eng_register_pseudo_class_sel_handler
    1154                  (result, (guchar *) "lang",
    1155                   FUNCTION_PSEUDO, (CRPseudoClassSelectorHandler)
    1156                   lang_pseudo_class_handler);
    1157  
    1158          return result;
    1159  }
    1160  
    1161  /**
    1162   * cr_sel_eng_register_pseudo_class_sel_handler:
    1163   *@a_this: the current instance of #CRSelEng
    1164   *@a_pseudo_class_sel_name: the name of the pseudo class selector.
    1165   *@a_pseudo_class_type: the type of the pseudo class selector.
    1166   *@a_handler: the actual handler or callback to be called during
    1167   *the selector evaluation process.
    1168   *
    1169   *Adds a new handler entry in the handlers entry table.
    1170   *
    1171   *Returns CR_OK, upon successful completion, an error code otherwise.
    1172   */
    1173  enum CRStatus
    1174  cr_sel_eng_register_pseudo_class_sel_handler (CRSelEng * a_this,
    1175                                                guchar * a_name,
    1176                                                enum CRPseudoType a_type,
    1177                                                CRPseudoClassSelectorHandler
    1178                                                a_handler)
    1179  {
    1180          struct CRPseudoClassSelHandlerEntry *handler_entry = NULL;
    1181          GList *list = NULL;
    1182  
    1183          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1184                                && a_handler && a_name, CR_BAD_PARAM_ERROR);
    1185  
    1186          handler_entry = g_try_malloc
    1187                  (sizeof (struct CRPseudoClassSelHandlerEntry));
    1188          if (!handler_entry) {
    1189                  return CR_OUT_OF_MEMORY_ERROR;
    1190          }
    1191          memset (handler_entry, 0,
    1192                  sizeof (struct CRPseudoClassSelHandlerEntry));
    1193          handler_entry->name = (guchar *) g_strdup ((const gchar *) a_name);
    1194          handler_entry->type = a_type;
    1195          handler_entry->handler = a_handler;
    1196          list = g_list_append (PRIVATE (a_this)->pcs_handlers, handler_entry);
    1197          if (!list) {
    1198                  return CR_OUT_OF_MEMORY_ERROR;
    1199          }
    1200          PRIVATE (a_this)->pcs_handlers = list;
    1201          return CR_OK;
    1202  }
    1203  
    1204  enum CRStatus
    1205  cr_sel_eng_unregister_pseudo_class_sel_handler (CRSelEng * a_this,
    1206                                                  guchar * a_name,
    1207                                                  enum CRPseudoType a_type)
    1208  {
    1209          GList *elem = NULL,
    1210                  *deleted_elem = NULL;
    1211          gboolean found = FALSE;
    1212          struct CRPseudoClassSelHandlerEntry *entry = NULL;
    1213  
    1214          g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
    1215  
    1216          for (elem = PRIVATE (a_this)->pcs_handlers;
    1217               elem; elem = g_list_next (elem)) {
    1218                  entry = elem->data;
    1219                  if (!strcmp ((const char *) entry->name, (const char *) a_name)
    1220                      && entry->type == a_type) {
    1221                          found = TRUE;
    1222                          break;
    1223                  }
    1224          }
    1225          if (found == FALSE)
    1226                  return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
    1227          PRIVATE (a_this)->pcs_handlers = g_list_delete_link
    1228                  (PRIVATE (a_this)->pcs_handlers, elem);
    1229          entry = elem->data;
    1230          if (entry->name)
    1231                  g_free (entry->name);
    1232          g_free (elem);
    1233          g_list_free (deleted_elem);
    1234  
    1235          return CR_OK;
    1236  }
    1237  
    1238  /**
    1239   * cr_sel_eng_unregister_all_pseudo_class_sel_handlers:
    1240   *@a_this: the current instance of #CRSelEng .
    1241   *
    1242   *Unregisters all the pseudo class sel handlers
    1243   *and frees all the associated allocated datastructures.
    1244   *
    1245   *Returns CR_OK upon succesful completion, an error code
    1246   *otherwise.
    1247   */
    1248  enum CRStatus
    1249  cr_sel_eng_unregister_all_pseudo_class_sel_handlers (CRSelEng * a_this)
    1250  {
    1251          GList *elem = NULL;
    1252          struct CRPseudoClassSelHandlerEntry *entry = NULL;
    1253  
    1254          g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
    1255  
    1256          if (!PRIVATE (a_this)->pcs_handlers)
    1257                  return CR_OK;
    1258          for (elem = PRIVATE (a_this)->pcs_handlers;
    1259               elem; elem = g_list_next (elem)) {
    1260                  entry = elem->data;
    1261                  if (!entry)
    1262                          continue;
    1263                  if (entry->name) {
    1264                          g_free (entry->name);
    1265                          entry->name = NULL;
    1266                  }
    1267                  g_free (entry);
    1268                  elem->data = NULL;
    1269          }
    1270          g_list_free (PRIVATE (a_this)->pcs_handlers);
    1271          PRIVATE (a_this)->pcs_handlers = NULL;
    1272          return CR_OK;
    1273  }
    1274  
    1275  enum CRStatus
    1276  cr_sel_eng_get_pseudo_class_selector_handler (CRSelEng * a_this,
    1277                                                guchar * a_name,
    1278                                                enum CRPseudoType a_type,
    1279                                                CRPseudoClassSelectorHandler *
    1280                                                a_handler)
    1281  {
    1282          GList *elem = NULL;
    1283          struct CRPseudoClassSelHandlerEntry *entry = NULL;
    1284          gboolean found = FALSE;
    1285  
    1286          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1287                                && a_name, CR_BAD_PARAM_ERROR);
    1288  
    1289          for (elem = PRIVATE (a_this)->pcs_handlers;
    1290               elem; elem = g_list_next (elem)) {
    1291                  entry = elem->data;
    1292                  if (!strcmp ((const char *) a_name, (const char *) entry->name)
    1293                      && entry->type == a_type) {
    1294                          found = TRUE;
    1295                          break;
    1296                  }
    1297          }
    1298  
    1299          if (found == FALSE)
    1300                  return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
    1301          *a_handler = entry->handler;
    1302          return CR_OK;
    1303  }
    1304  
    1305  /**
    1306   * cr_sel_eng_matches_node:
    1307   *@a_this: the selection engine.
    1308   *@a_sel: the simple selector against which the xml node 
    1309   *is going to be matched.
    1310   *@a_node: the node against which the selector is going to be matched.
    1311   *@a_result: out parameter. The result of the match. Is set to
    1312   *TRUE if the selector matches the node, FALSE otherwise. This value
    1313   *is considered if and only if this functions returns CR_OK.
    1314   *
    1315   *Evaluates a chained list of simple selectors (known as a css2 selector).
    1316   *Says wheter if this selector matches the xml node given in parameter or
    1317   *not.
    1318   *
    1319   *Returns the CR_OK if the selection ran correctly, an error code otherwise.
    1320   */
    1321  enum CRStatus
    1322  cr_sel_eng_matches_node (CRSelEng * a_this, CRSimpleSel * a_sel,
    1323                           xmlNode * a_node, gboolean * a_result)
    1324  {
    1325          g_return_val_if_fail (a_this && PRIVATE (a_this)
    1326                                && a_this && a_node
    1327                                && a_result, CR_BAD_PARAM_ERROR);
    1328  
    1329          if (a_node->type != XML_ELEMENT_NODE) {
    1330                  *a_result = FALSE;
    1331                  return CR_OK;
    1332          }
    1333  
    1334          return sel_matches_node_real (a_this, a_sel, 
    1335                                        a_node, a_result, 
    1336                                        TRUE, TRUE);
    1337  }
    1338  
    1339  /**
    1340   * cr_sel_eng_get_matched_rulesets:
    1341   *@a_this: the current instance of the selection engine.
    1342   *@a_sheet: the stylesheet that holds the selectors.
    1343   *@a_node: the xml node to consider during the walk thru
    1344   *the stylesheet.
    1345   *@a_rulesets: out parameter. A pointer to an array of
    1346   *rulesets statement pointers. *a_rulesets is allocated by
    1347   *this function and must be freed by the caller. However, the caller
    1348   *must not alter the rulesets statements pointer because they
    1349   *point to statements that are still in the css stylesheet.
    1350   *@a_len: the length of *a_ruleset.
    1351   *
    1352   *Returns an array of pointers to selectors that matches
    1353   *the xml node given in parameter.
    1354   *
    1355   *Returns CR_OK upon sucessfull completion, an error code otherwise.
    1356   */
    1357  enum CRStatus
    1358  cr_sel_eng_get_matched_rulesets (CRSelEng * a_this,
    1359                                   CRStyleSheet * a_sheet,
    1360                                   xmlNode * a_node,
    1361                                   CRStatement *** a_rulesets, gulong * a_len)
    1362  {
    1363          CRStatement **stmts_tab = NULL;
    1364          enum CRStatus status = CR_OK;
    1365          gulong tab_size = 0,
    1366                  tab_len = 0,
    1367                  index = 0;
    1368          gushort stmts_chunck_size = 8;
    1369  
    1370          g_return_val_if_fail (a_this
    1371                                && a_sheet
    1372                                && a_node
    1373                                && a_rulesets && *a_rulesets == NULL
    1374                                && a_len, CR_BAD_PARAM_ERROR);
    1375  
    1376          stmts_tab = g_try_malloc (stmts_chunck_size * sizeof (CRStatement *));
    1377  
    1378          if (!stmts_tab) {
    1379                  cr_utils_trace_info ("Out of memory");
    1380                  status = CR_ERROR;
    1381                  goto error;
    1382          }
    1383          memset (stmts_tab, 0, stmts_chunck_size * sizeof (CRStatement *));
    1384  
    1385          tab_size = stmts_chunck_size;
    1386          tab_len = tab_size;
    1387  
    1388          while ((status = cr_sel_eng_get_matched_rulesets_real
    1389                  (a_this, a_sheet, a_node, stmts_tab + index, &tab_len))
    1390                 == CR_OUTPUT_TOO_SHORT_ERROR) {
    1391                  stmts_tab = g_try_realloc (stmts_tab,
    1392                                             (tab_size + stmts_chunck_size)
    1393                                             * sizeof (CRStatement *));
    1394                  if (!stmts_tab) {
    1395                          cr_utils_trace_info ("Out of memory");
    1396                          status = CR_ERROR;
    1397                          goto error;
    1398                  }
    1399                  tab_size += stmts_chunck_size;
    1400                  index += tab_len;
    1401                  tab_len = tab_size - index;
    1402          }
    1403  
    1404          tab_len = tab_size - stmts_chunck_size + tab_len;
    1405          *a_rulesets = stmts_tab;
    1406          *a_len = tab_len;
    1407  
    1408          return CR_OK;
    1409  
    1410        error:
    1411  
    1412          if (stmts_tab) {
    1413                  g_free (stmts_tab);
    1414                  stmts_tab = NULL;
    1415  
    1416          }
    1417  
    1418          *a_len = 0;
    1419          return status;
    1420  }
    1421  
    1422  
    1423  enum CRStatus
    1424  cr_sel_eng_get_matched_properties_from_cascade (CRSelEng * a_this,
    1425                                                  CRCascade * a_cascade,
    1426                                                  xmlNode * a_node,
    1427                                                  CRPropList ** a_props)
    1428  {
    1429          CRStatement **stmts_tab = NULL;
    1430          enum CRStatus status = CR_OK;
    1431          gulong tab_size = 0,
    1432                  tab_len = 0,
    1433                  i = 0,
    1434                  index = 0;
    1435          enum CRStyleOrigin origin = 0;
    1436          gushort stmts_chunck_size = 8;
    1437          CRStyleSheet *sheet = NULL;
    1438  
    1439          g_return_val_if_fail (a_this
    1440                                && a_cascade
    1441                                && a_node && a_props, CR_BAD_PARAM_ERROR);
    1442  
    1443          for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++) {
    1444                  sheet = cr_cascade_get_sheet (a_cascade, origin);
    1445                  if (!sheet)
    1446                          continue;
    1447                  if (tab_size - index < 1) {
    1448                          stmts_tab = g_try_realloc
    1449                                  (stmts_tab, (tab_size + stmts_chunck_size)
    1450                                   * sizeof (CRStatement *));
    1451                          if (!stmts_tab) {
    1452                                  cr_utils_trace_info ("Out of memory");
    1453                                  status = CR_ERROR;
    1454                                  goto cleanup;
    1455                          }
    1456                          tab_size += stmts_chunck_size;
    1457                          /*
    1458                           *compute the max size left for
    1459                           *cr_sel_eng_get_matched_rulesets_real()'s output tab 
    1460                           */
    1461                          tab_len = tab_size - index;
    1462                  }
    1463                  while ((status = cr_sel_eng_get_matched_rulesets_real
    1464                          (a_this, sheet, a_node, stmts_tab + index, &tab_len))
    1465                         == CR_OUTPUT_TOO_SHORT_ERROR) {
    1466                          stmts_tab = g_try_realloc
    1467                                  (stmts_tab, (tab_size + stmts_chunck_size)
    1468                                   * sizeof (CRStatement *));
    1469                          if (!stmts_tab) {
    1470                                  cr_utils_trace_info ("Out of memory");
    1471                                  status = CR_ERROR;
    1472                                  goto cleanup;
    1473                          }
    1474                          tab_size += stmts_chunck_size;
    1475                          index += tab_len;
    1476                          /*
    1477                           *compute the max size left for
    1478                           *cr_sel_eng_get_matched_rulesets_real()'s output tab 
    1479                           */
    1480                          tab_len = tab_size - index;
    1481                  }
    1482                  if (status != CR_OK) {
    1483                          cr_utils_trace_info ("Error while running "
    1484                                               "selector engine");
    1485                          goto cleanup;
    1486                  }
    1487                  index += tab_len;
    1488                  tab_len = tab_size - index;
    1489          }
    1490  
    1491          /*
    1492           *TODO, walk down the stmts_tab and build the
    1493           *property_name/declaration hashtable.
    1494           *Make sure one can walk from the declaration to
    1495           *the stylesheet.
    1496           */
    1497          for (i = 0; i < index; i++) {
    1498                  CRStatement *stmt = stmts_tab[i];
    1499  
    1500                  if (!stmt)
    1501                          continue;
    1502                  switch (stmt->type) {
    1503                  case RULESET_STMT:
    1504                          if (!stmt->parent_sheet)
    1505                                  continue;
    1506                          status = put_css_properties_in_props_list
    1507                                  (a_props, stmt);
    1508                          break;
    1509                  default:
    1510                          break;
    1511                  }
    1512  
    1513          }
    1514          status = CR_OK ;
    1515   cleanup:
    1516          if (stmts_tab) {
    1517                  g_free (stmts_tab);
    1518                  stmts_tab = NULL;
    1519          }
    1520  
    1521          return status;
    1522  }
    1523  
    1524  enum CRStatus
    1525  cr_sel_eng_get_matched_style (CRSelEng * a_this,
    1526                                CRCascade * a_cascade,
    1527                                xmlNode * a_node,
    1528                                CRStyle * a_parent_style, 
    1529                                CRStyle ** a_style,
    1530                                gboolean a_set_props_to_initial_values)
    1531  {
    1532          enum CRStatus status = CR_OK;
    1533  
    1534          CRPropList *props = NULL;
    1535  
    1536          g_return_val_if_fail (a_this && a_cascade
    1537                                && a_node && a_style, CR_BAD_PARAM_ERROR);
    1538  
    1539          status = cr_sel_eng_get_matched_properties_from_cascade
    1540                  (a_this, a_cascade, a_node, &props);
    1541  
    1542          g_return_val_if_fail (status == CR_OK, status);
    1543          if (props) {
    1544                  if (!*a_style) {
    1545                          *a_style = cr_style_new (a_set_props_to_initial_values) ;
    1546                          g_return_val_if_fail (*a_style, CR_ERROR);
    1547                  } else {
    1548                          if (a_set_props_to_initial_values == TRUE) {
    1549                                  cr_style_set_props_to_initial_values (*a_style) ;
    1550                          } else {
    1551                                  cr_style_set_props_to_default_values (*a_style);
    1552                          }
    1553                  }
    1554                  (*a_style)->parent_style = a_parent_style;
    1555  
    1556                  set_style_from_props (*a_style, props);
    1557                  if (props) {
    1558                          cr_prop_list_destroy (props);
    1559                          props = NULL;
    1560                  }
    1561          }
    1562          return CR_OK;
    1563  }
    1564  
    1565  /**
    1566   * cr_sel_eng_destroy:
    1567   *@a_this: the current instance of the selection engine.
    1568   *
    1569   *The destructor of #CRSelEng
    1570   */
    1571  void
    1572  cr_sel_eng_destroy (CRSelEng * a_this)
    1573  {
    1574          g_return_if_fail (a_this);
    1575  
    1576          if (!PRIVATE (a_this))
    1577                  goto end ;
    1578          if (PRIVATE (a_this)->pcs_handlers) {
    1579                  cr_sel_eng_unregister_all_pseudo_class_sel_handlers
    1580                          (a_this) ;
    1581                  PRIVATE (a_this)->pcs_handlers = NULL ;
    1582          }
    1583          g_free (PRIVATE (a_this));
    1584          PRIVATE (a_this) = NULL;
    1585   end:
    1586          if (a_this) {
    1587                  g_free (a_this);
    1588          }
    1589  }