~ubuntu-branches/ubuntu/utopic/inkscape/utopic-proposed

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/libcroco/cr-sel-eng.c

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

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