PageRenderTime 69ms CodeModel.GetById 3ms app.highlight 56ms RepoModel.GetById 1ms app.codeStats 1ms

/cssed-0.4.0/libcroco/parser/cr-sel-eng.c

#
C | 1555 lines | 1044 code | 180 blank | 331 comment | 329 complexity | 952cb9f104507a46704af74444047d28 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file