]> gitweb.michael.orlitzky.com - libsvgtiny.git/blob - src/svgtiny_css.c
de82a835071e602853810ad0f5e6c38554891fdc
[libsvgtiny.git] / src / svgtiny_css.c
1 #include <libcss/libcss.h>
2 #include <strings.h> /* strncasecmp */
3
4 #include "svgtiny.h"
5 #include "svgtiny_internal.h"
6
7 static css_error node_name(void *pw, void *node, css_qname *qname);
8 static css_error node_classes(void *pw, void *node,
9 lwc_string ***classes, uint32_t *n_classes);
10 static css_error node_id(void *pw, void *node, lwc_string **id);
11 static css_error named_parent_node(void *pw, void *node,
12 const css_qname *qname, void **parent);
13 static css_error named_sibling_node(void *pw, void *node,
14 const css_qname *qname, void **sibling);
15 static css_error named_generic_sibling_node(void *pw, void *node,
16 const css_qname *qname, void **sibling);
17 static css_error parent_node(void *pw, void *node, void **parent);
18 static css_error sibling_node(void *pw, void *node, void **sibling);
19 static css_error node_has_name(void *pw, void *node,
20 const css_qname *qname, bool *match);
21 static css_error node_has_class(void *pw, void *node,
22 lwc_string *name, bool *match);
23 static css_error node_has_id(void *pw, void *node,
24 lwc_string *name, bool *match);
25 static css_error node_has_attribute(void *pw, void *node,
26 const css_qname *qname, bool *match);
27 static css_error node_has_attribute_equal(void *pw, void *node,
28 const css_qname *qname, lwc_string *value,
29 bool *match);
30 static css_error node_has_attribute_dashmatch(void *pw, void *node,
31 const css_qname *qname, lwc_string *value,
32 bool *match);
33 static css_error node_has_attribute_includes(void *pw, void *node,
34 const css_qname *qname, lwc_string *word,
35 bool *match);
36 static css_error node_has_attribute_prefix(void *pw, void *node,
37 const css_qname *qname, lwc_string *prefix,
38 bool *match);
39 static css_error node_has_attribute_suffix(void *pw, void *node,
40 const css_qname *qname, lwc_string *suffix,
41 bool *match);
42 static css_error node_has_attribute_substring(void *pw, void *node,
43 const css_qname *qname, lwc_string *substring,
44 bool *match);
45 static css_error node_is_root(void *pw, void *node, bool *match);
46 static css_error node_count_siblings(void *pw, void *node,
47 bool same_name, bool after, int32_t *count);
48 static css_error node_is_empty(void *pw, void *node, bool *is_empty);
49 static css_error node_is_link(void *pw, void *node, bool *is_link);
50 static css_error node_is_hover(void *pw, void *node, bool *is_hover);
51 static css_error node_is_active(void *pw, void *node, bool *is_active);
52 static css_error node_is_focus(void *pw, void *node, bool *is_focus);
53 static css_error node_is_enabled(void *pw, void *node, bool *is_enabled);
54 static css_error node_is_disabled(void *pw, void *node, bool *is_disabled);
55 static css_error node_is_checked(void *pw, void *node, bool *is_checked);
56 static css_error node_is_target(void *pw, void *node, bool *is_target);
57
58
59 /**
60 * Resolve a relative URL to an absolute one by doing nothing. This is
61 * the simplest possible implementation of a URL resolver, needed for
62 * parsing CSS.
63 */
64 css_error svgtiny_resolve_url(void *pw,
65 const char *base, lwc_string *rel, lwc_string **abs)
66 {
67 UNUSED(pw);
68 UNUSED(base);
69
70 /* Copy the relative URL to the absolute one (the return
71 value) */
72 *abs = lwc_string_ref(rel);
73 return CSS_OK;
74 }
75
76 /**
77 * Create a stylesheet with the default set of params.
78 *
79 * \param sheet A stylesheet pointer, passed in by reference, that
80 * we use to store the newly-created stylesheet.
81 * \param inline_style True if this stylesheet represents an inline
82 * style, and false otherwise.
83 *
84 * \return The return value from css_stylesheet_create() is returned.
85 */
86 css_error svgtiny_create_stylesheet(css_stylesheet **sheet,
87 bool inline_style)
88 {
89 css_stylesheet_params params;
90
91 params.params_version = CSS_STYLESHEET_PARAMS_VERSION_1;
92 params.level = CSS_LEVEL_DEFAULT;
93 params.charset = NULL;
94 params.url = "";
95 params.title = NULL;
96 params.allow_quirks = false;
97 params.inline_style = inline_style;
98 params.resolve = svgtiny_resolve_url;
99 params.resolve_pw = NULL;
100 params.import = NULL;
101 params.import_pw = NULL;
102 params.color = NULL;
103 params.color_pw = NULL;
104 params.font = NULL;
105 params.font_pw = NULL;
106
107 return css_stylesheet_create(&params, sheet);
108 }
109
110
111 /**************************/
112 /* libcss select handlers */
113 /**************************/
114 /*
115 * From here on we implement the "select handler "API defined in
116 * libcss's include/libcss/select.h and discussed briefly in its
117 * docs/API document.
118 */
119
120
121 /**
122 * Retrieve the given node's name
123 *
124 * \param pw Pointer to the current SVG parser state
125 * \param node Libdom SVG node
126 * \param qname Address at which to store the node name
127 *
128 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
129 */
130 css_error node_name(void *pw, void *node, css_qname *qname)
131 {
132 dom_string *name;
133 dom_exception err;
134 struct svgtiny_parse_state *state;
135
136 err = dom_node_get_node_name((dom_node *)node, &name);
137 if (err != DOM_NO_ERR) {
138 return CSS_NOMEM;
139 }
140
141 state = (struct svgtiny_parse_state *)pw;
142 qname->ns = lwc_string_ref(state->interned_svg_xmlns);
143
144 err = dom_string_intern(name, &qname->name);
145 if (err != DOM_NO_ERR) {
146 dom_string_unref(name);
147 return CSS_NOMEM;
148 }
149
150 dom_string_unref(name);
151
152 return CSS_OK;
153 }
154
155
156 /**
157 * Retrieve the given node's classes
158 *
159 * \param pw Pointer to the current SVG parser state
160 * \param node Libdom SVG node
161 * \param classes Address at which to store the class name array
162 * \param n_classes Address at which to store the length of the class
163 * name array
164 *
165 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
166 *
167 * \note CSS_NOMEM is not possible in practice as of libdom-0.4.1,
168 * because the underlying libdom function never fails
169 */
170 css_error node_classes(void *pw, void *node,
171 lwc_string ***classes, uint32_t *n_classes)
172 {
173 UNUSED(pw);
174 dom_exception err;
175
176 err = dom_element_get_classes((dom_node *)node, classes, n_classes);
177
178 /* The implementation does not do it, but the documentation
179 for dom_element_get_classes() says that a DOM_NO_MEM_ERR is
180 possible here, so we handle it to be on the safe side. */
181 if (err != DOM_NO_ERR) {
182 return CSS_NOMEM;
183 }
184
185 return CSS_OK;
186 }
187
188
189 /**
190 * Retrieve the given node's id
191 *
192 * \param pw Pointer to the current SVG parser state
193 * \param node Libdom SVG node
194 * \param id Address at which to store the id
195 *
196 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
197 */
198 css_error node_id(void *pw, void *node, lwc_string **id)
199 {
200 dom_string *attr;
201 dom_exception err;
202 struct svgtiny_parse_state *state;
203
204 /* Begin with the assumption that this node has no id */
205 *id = NULL;
206
207 state = (struct svgtiny_parse_state *)pw;
208 err = dom_element_get_attribute((dom_node *)node,
209 state->interned_id, &attr);
210 if (err != DOM_NO_ERR) {
211 return CSS_NOMEM;
212 }
213 else if (attr == NULL) {
214 /* The node has no id attribute and our return value
215 is already set to NULL so we're done */
216 return CSS_OK;
217 }
218
219 /* If we found an id attribute (a dom_string), intern it into
220 an lwc_string that we can return, and then cleanup the
221 dom_string. */
222 err = dom_string_intern(attr, id);
223 if (err != DOM_NO_ERR) {
224 dom_string_unref(attr);
225 return CSS_NOMEM;
226 }
227 dom_string_unref(attr);
228 return CSS_OK;
229 }
230
231
232
233 /**
234 * Find the first parent of the given element having the given name
235 *
236 * \param pw Pointer to the current SVG parser state
237 * \param node Libdom SVG node
238 * \param qname Name of the parent node to search for
239 * \param parent Address at which to store the parent node pointer
240 *
241 * \return Always returns CSS_OK
242 *
243 * \post If a suitable element is found, a pointer to it will be
244 * stored at the address pointed to by \a parent; otherwise,
245 * NULL will be stored at the address pointed to by \a parent
246 */
247 css_error named_parent_node(void *pw, void *node,
248 const css_qname *qname, void **parent)
249 {
250 UNUSED(pw);
251 /* dom_element_named_parent_node() was invented to implement
252 * this select handler so there isn't much for us to do except
253 * call it. It's OK if node isn't an element, libdom checks
254 * for it. */
255 dom_element_named_parent_node((dom_element *)node,
256 qname->name,
257 (struct dom_element **)parent);
258
259 /* Implementation detail: dom_element_named_parent_node()
260 * increments the reference count of the parent element before
261 * returning it to us. According to docs/RefCnt in the libdom
262 * repository, this will prevent the parent element from being
263 * destroyed if it is pruned from the DOM. That sounds good,
264 * since we don't want to be using a pointer to an object that
265 * has been destroyed... but we also have no way of later
266 * decrementing the reference count ourselves, and don't want
267 * to make the returned node eternal. Decrementing the
268 * reference counter now allows it to be destroyed when the
269 * DOM no longer needs it, and so long as no other parts of
270 * libsvgtiny are messing with the DOM during parsing, that
271 * shouldn't (ha ha) cause any problems. */
272 dom_node_unref(*parent);
273
274 return CSS_OK;
275 }
276
277
278 /**
279 * Find the "next-sibling" of the given element having the given name
280 *
281 * This search corresponds to the "+ foo" combinator in CSS and will
282 * find only "foo" element nodes that immediately precede the given
283 * node under the same parent in the DOM. In CSS the tree is viewed
284 * top-down and in libdom it is viewed from the bottom-up; as a result
285 * "next" and "previous" are sometimes backwards. This is case-sensitive.
286 *
287 * \param pw Pointer to the current SVG parser state
288 * \param node Libdom SVG node
289 * \param qname Name of the sibling node to search for
290 * \param sibling Address at which to store the sibling node pointer
291 *
292 * \return Always returns CSS_OK
293 *
294 * \post If a suitable element is found, a pointer to it will be
295 * stored at the address pointed to by \a sibling; otherwise,
296 * NULL will be stored at the address pointed to by \a sibling
297 */
298 css_error named_sibling_node(void *pw, void *node,
299 const css_qname *qname, void **sibling)
300 {
301 UNUSED(pw);
302 dom_node *n = node; /* the current node */
303 dom_node *prev; /* the previous node */
304 dom_exception err;
305 dom_node_type type;
306 dom_string *name;
307
308 *sibling = NULL; /* default to nothing found */
309
310 /* Begin the search; the first iteration we do outside of the
311 * loop. Implementation detil: dom_node_get_previous_sibling()
312 * increments the reference counter on the returned node. A
313 * comment within named_parent_node() explains why we
314 * decrement it ASAP. */
315 err = dom_node_get_previous_sibling(n, &n);
316 if (err != DOM_NO_ERR) {
317 return CSS_OK;
318 }
319
320 while (n != NULL) {
321 /* We're looking for the first ELEMENT sibling */
322 err = dom_node_get_node_type(n, &type);
323 if (err != DOM_NO_ERR) {
324 dom_node_unref(n);
325 return CSS_OK;
326 }
327
328 if (type == DOM_ELEMENT_NODE) {
329 /* We found an element node, does it have the
330 * right name? */
331 err = dom_node_get_node_name(n, &name);
332 if (err != DOM_NO_ERR) {
333 dom_node_unref(n);
334 return CSS_OK;
335 }
336
337 if (dom_string_lwc_isequal(name,
338 qname->name)) {
339 /* The name is right, return it */
340 *sibling = n;
341 }
342
343 /* There's only one next-sibling element node
344 * and we've already found it, so if its name
345 * wasn't right, we return the default value
346 * of NULL below */
347 dom_string_unref(name);
348 dom_node_unref(n);
349 return CSS_OK;
350 }
351
352 /* Not an element node, so we move on the the previous
353 * previous sibling */
354 err = dom_node_get_previous_sibling(n, &prev);
355 if (err != DOM_NO_ERR) {
356 dom_node_unref(n);
357 return CSS_OK;
358 }
359
360 dom_node_unref(n);
361 n = prev;
362 }
363
364 return CSS_OK;
365 }
366
367
368 /**
369 * Find the first "subsequent-sibling" of the given element having the
370 * given name
371 *
372 * This search corresponds to the "~ foo" combinator in CSS and will
373 * find only "foo" element nodes that precede the given node (under
374 * the same parent) in the DOM. In CSS the tree is viewed top-down and
375 * in libdom it is viewed from the bottom-up; as a result "next" and
376 * "previous" are sometimes backwards. This is case-sensitive.
377 *
378 * \param pw Pointer to the current SVG parser state
379 * \param node Libdom SVG node
380 * \param qname Name of the sibling node to search for
381 * \param sibling Address at which to store the sibling node pointer
382 *
383 * \return Always returns CSS_OK
384 *
385 * \post If a suitable element is found, a pointer to it will be
386 * stored at the address pointed to by \a sibling; otherwise,
387 * NULL will be stored at the address pointed to by \a sibling
388 */
389 css_error named_generic_sibling_node(void *pw, void *node,
390 const css_qname *qname, void **sibling)
391 {
392 UNUSED(pw);
393 dom_node *n = node; /* the current node */
394 dom_node *prev; /* the previous node */
395 dom_exception err;
396 dom_node_type type;
397 dom_string *name;
398
399
400 *sibling = NULL; /* default to nothing found */
401
402 /* Begin the search; the first iteration we do outside of the
403 * loop. Implementation detil: dom_node_get_previous_sibling()
404 * increments the reference counter on the returned node. A
405 * comment within named_parent_node() explains why we
406 * decrement it ASAP. */
407 err = dom_node_get_previous_sibling(n, &n);
408 if (err != DOM_NO_ERR) {
409 return CSS_OK;
410 }
411
412 while (n != NULL) {
413 err = dom_node_get_node_type(n, &type);
414 if (err != DOM_NO_ERR) {
415 dom_node_unref(n);
416 return CSS_OK;
417 }
418
419 if (type == DOM_ELEMENT_NODE) {
420 /* We only want ELEMENT nodes */
421 err = dom_node_get_node_name(n, &name);
422 if (err != DOM_NO_ERR) {
423 dom_node_unref(n);
424 return CSS_OK;
425 }
426
427 if (dom_string_lwc_isequal(name,
428 qname->name)) {
429 /* Found one. Save it and stop the search */
430 dom_string_unref(name);
431 dom_node_unref(n);
432 *sibling = n;
433 return CSS_OK;
434 }
435
436 dom_string_unref(name);
437 }
438
439 /* This sibling wasn't an element with the desired
440 name, so move on to the previous sibling */
441 err = dom_node_get_previous_sibling(n, &prev);
442 if (err != DOM_NO_ERR) {
443 dom_node_unref(n);
444 return CSS_OK;
445 }
446
447 dom_node_unref(n);
448 n = prev;
449 }
450
451 return CSS_OK;
452 }
453
454
455 /**
456 * Return a pointer to the given node's parent
457 *
458 * \param pw Pointer to the current SVG parser state
459 * \param node Libdom SVG node
460 * \param parent Address at which to store the node's parent pointer
461 *
462 * \return Always returns CSS_OK
463 */
464 css_error parent_node(void *pw, void *node, void **parent)
465 {
466 UNUSED(pw);
467 /* Libdom basically implements this for us */
468 dom_element_parent_node(node, (struct dom_element **)parent);
469
470 /* See the comment in named_parent_node() for why we decrement
471 * this reference counter here. */
472 dom_node_unref(*parent);
473
474 return CSS_OK;
475 }
476
477
478 /**
479 * Find the "next-sibling" of the given element
480 *
481 * This search corresponds "+ *" in CSS and will find the first
482 * element node that immediately precedes the given node under the
483 * same parent in the DOM. In CSS the tree is viewed top-down and in
484 * libdom it is viewed from the bottom-up; as a result "next" and
485 * "previous" are sometimes backwards.
486 *
487 * \param pw Pointer to the current SVG parser state
488 * \param node Libdom SVG node
489 * \param sibling Address at which to store the sibling node pointer
490 *
491 * \return Always returns CSS_OK
492 *
493 * \post If a suitable element is found, a pointer to it will be
494 * stored at the address pointed to by \a sibling; otherwise,
495 * NULL will be stored at the address pointed to by \a sibling
496 */
497 css_error sibling_node(void *pw, void *node, void **sibling)
498 {
499 UNUSED(pw);
500 dom_node *n = node; /* the current node */
501 dom_node *prev; /* the previous node */
502 dom_exception err;
503 dom_node_type type;
504
505 *sibling = NULL; /* default to nothing found */
506
507 /* Begin the search; the first iteration we do outside of the
508 * loop. Implementation detil: dom_node_get_previous_sibling()
509 * increments the reference counter on the returned node. A
510 * comment within named_parent_node() explains why we
511 * decrement it ASAP. */
512 err = dom_node_get_previous_sibling(n, &n);
513 if (err != DOM_NO_ERR) {
514 return CSS_OK;
515 }
516
517 while (n != NULL) {
518 err = dom_node_get_node_type(n, &type);
519 if (err != DOM_NO_ERR) {
520 dom_node_unref(n);
521 return CSS_OK;
522 }
523
524 if (type == DOM_ELEMENT_NODE) {
525 /* We found a sibling node that is also an
526 element and that's all we wanted. */
527 *sibling = n;
528 dom_node_unref(n);
529 return CSS_OK;
530 }
531
532 /* This sibling node was not an element; move on to
533 the previous sibling */
534 err = dom_node_get_previous_sibling(n, &prev);
535 if (err != DOM_NO_ERR) {
536 dom_node_unref(n);
537 return CSS_OK;
538 }
539
540 dom_node_unref(n);
541 n = prev;
542 }
543
544 return CSS_OK;
545 }
546
547
548 /**
549 * Test the given node for the given name
550 *
551 * This will return true (via the "match" pointer) if the libdom node
552 * has the given name or if that name is the universal selector;
553 * otherwise it returns false. The comparison is case-sensitive. It
554 * corresponds to a rule like "body { ... }" in CSS.
555 *
556 * \param pw Pointer to the current SVG parser state
557 * \param node Libdom SVG node to test
558 * \param qname Name to check for
559 * \param match Pointer to the test result
560 *
561 * \return Always returns CSS_OK
562 */
563 css_error node_has_name(void *pw, void *node,
564 const css_qname *qname, bool *match)
565 {
566 struct svgtiny_parse_state *state;
567 dom_string *name;
568 dom_exception err;
569
570 /* Start by checking to see if qname is the universal selector */
571 state = (struct svgtiny_parse_state *)pw;
572 if (lwc_string_isequal(qname->name,
573 state->interned_universal, match) == lwc_error_ok) {
574 if (*match) {
575 /* It's the universal selector. In NetSurf, all node
576 * names match the universal selector, and nothing in
577 * the libcss documentation suggests another approach,
578 * so we follow NetSurf here. */
579 return CSS_OK;
580 }
581 }
582
583 err = dom_node_get_node_name((dom_node *)node, &name);
584 if (err != DOM_NO_ERR) {
585 return CSS_OK;
586 }
587
588 /* Unlike with HTML, SVG element names are case-sensitive */
589 *match = dom_string_lwc_isequal(name, qname->name);
590 dom_string_unref(name);
591
592 return CSS_OK;
593 }
594
595
596 /**
597 * Test the given node for the given class
598 *
599 * This will return true (via the "match" pointer) if the libdom node
600 * has the given class. The comparison is case-sensitive. It
601 * corresponds to node.class in CSS.
602 *
603 * \param pw Pointer to the current SVG parser state
604 * \param node Libdom SVG node to test
605 * \param name Class name to check for
606 * \param match Pointer to the test result
607 *
608 * \return Always returns CSS_OK
609 */
610 css_error node_has_class(void *pw, void *node,
611 lwc_string *name, bool *match)
612 {
613 UNUSED(pw);
614 /* libdom implements this for us and apparently it cannot fail */
615 dom_element_has_class((dom_node *)node, name, match);
616 return CSS_OK;
617 }
618
619
620 /**
621 * Test the given node for the given id
622 *
623 * This will return true (via the "match" pointer) if the libdom node
624 * has the given id. The comparison is case-sensitive. It corresponds
625 * to node#id in CSS.
626 *
627 * \param pw Pointer to the current SVG parser state
628 * \param node Libdom SVG node to test
629 * \param name Id to check for
630 * \param match Pointer to the test result
631 *
632 * \return Always returns CSS_OK
633 */
634 css_error node_has_id(void *pw, void *node,
635 lwc_string *name, bool *match)
636 {
637 dom_string *attr;
638 dom_exception err;
639 struct svgtiny_parse_state *state;
640
641 attr = NULL; /* a priori the "id" attribute may not exist */
642 *match = false; /* default to no match */
643
644 state = (struct svgtiny_parse_state *)pw;
645 err = dom_element_get_attribute((dom_node *)node,
646 state->interned_id, &attr);
647 if (err != DOM_NO_ERR || attr == NULL) {
648 return CSS_OK;
649 }
650
651 *match = dom_string_lwc_isequal(attr, name);
652 dom_string_unref(attr);
653
654 return CSS_OK;
655 }
656
657
658 /**
659 * Test the given node for the given attribute
660 *
661 * This will return true (via the "match" pointer) if the libdom node
662 * has an attribute with the given name. The comparison is
663 * case-sensitive. It corresponds to node[attr] in CSS.
664 *
665 * \param pw Pointer to the current SVG parser state
666 * \param node Libdom SVG node to test
667 * \param qname Attribute name to check for
668 * \param match Pointer to the test result
669 *
670 * \return Returns CSS_OK if successful and CSS_NOMEM if anything
671 * goes wrong
672 */
673 css_error node_has_attribute(void *pw, void *node,
674 const css_qname *qname, bool *match)
675 {
676 UNUSED(pw);
677 dom_string *name;
678 dom_exception err;
679
680 /* intern the attribute name as a dom_string so we can
681 * delegate to dom_element_has_attribute() */
682 err = dom_string_create_interned(
683 (const uint8_t *) lwc_string_data(qname->name),
684 lwc_string_length(qname->name),
685 &name);
686 if (err != DOM_NO_ERR) {
687 return CSS_NOMEM;
688 }
689
690 err = dom_element_has_attribute((dom_node *)node, name, match);
691 if (err != DOM_NO_ERR) {
692 dom_string_unref(name);
693 return CSS_OK;
694 }
695
696 dom_string_unref(name);
697 return CSS_OK;
698 }
699
700
701 /**
702 * Test the given node for an attribute with a specific value
703 *
704 * This will return true (via the "match" pointer) if the libdom node
705 * has an attribute with the given name and value. The comparison is
706 * case-sensitive. It corresponds to node[attr=value] in CSS.
707 *
708 * \param pw Pointer to the current SVG parser state
709 * \param node Libdom SVG node to test
710 * \param qname Attribute name to check for
711 * \param value Attribute value to check for
712 * \param match Pointer to the test result
713 *
714 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
715 * intern the attribute name (which usually indicates memory
716 * exhaustion)
717 */
718 css_error node_has_attribute_equal(void *pw, void *node,
719 const css_qname *qname, lwc_string *value,
720 bool *match)
721 {
722 /* Implementation note: NetSurf always returns "no match" when
723 * the value is empty (length zero). We allow it, because why
724 * not? */
725
726 UNUSED(pw);
727 dom_string *name;
728 dom_string *attr_val;
729 dom_exception err;
730
731 /* Intern the attribute name as a dom_string so we can
732 * use dom_element_get_attribute() */
733 err = dom_string_create_interned(
734 (const uint8_t *) lwc_string_data(qname->name),
735 lwc_string_length(qname->name),
736 &name);
737 if (err != DOM_NO_ERR) {
738 return CSS_NOMEM;
739 }
740
741 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
742 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
743 /* There was an error getting the attribute's value or
744 * the attribute doesn't exist. So, no match? */
745 dom_string_unref(name);
746 *match = false;
747 return CSS_OK;
748 }
749
750 /* Otherwise, we have the attribute value from the given node
751 * and all we need to do is compare. */
752 dom_string_unref(name);
753 *match = dom_string_lwc_isequal(attr_val, value);
754 dom_string_unref(attr_val);
755
756 return CSS_OK;
757 }
758
759
760 /**
761 * Test the given node for an attribute with a specific value,
762 * possibly followed by a single hyphen
763 *
764 * This will return true (via the "match" pointer) if the libdom node
765 * has an attribute with the given name and value or with the given
766 * name and a value that is followed by exactly one hyphen. The
767 * comparison is case-sensitive. This corresponds to [attr|=value]
768 * in CSS.
769 *
770 * \param pw Pointer to the current SVG parser state
771 * \param node Libdom SVG node to test
772 * \param qname Attribute name to check for
773 * \param value Attribute value to check for
774 * \param match Pointer to the test result
775 *
776 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
777 * intern the attribute name (which usually indicates memory
778 * exhaustion)
779 */
780 css_error node_has_attribute_dashmatch(void *pw, void *node,
781 const css_qname *qname, lwc_string *value,
782 bool *match)
783 {
784 /* Implementation note: NetSurf always returns "no match" when
785 * the value is empty (length zero). We allow it, because why
786 * not? */
787
788 UNUSED(pw);
789 dom_string *name;
790 dom_string *attr_val;
791 dom_exception err;
792
793 const char *vdata; /* to hold the data underlying "value" */
794 size_t vdata_len;
795 const char *avdata; /* to hold the found attribute value data */
796 size_t avdata_len;
797
798 /* Intern the attribute name as a dom_string so we can
799 * use dom_element_get_attribute() */
800 err = dom_string_create_interned(
801 (const uint8_t *) lwc_string_data(qname->name),
802 lwc_string_length(qname->name),
803 &name);
804 if (err != DOM_NO_ERR) {
805 return CSS_NOMEM;
806 }
807
808 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
809 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
810 /* There was an error getting the attribute's value or
811 * the attribute doesn't exist. So, no match? */
812 dom_string_unref(name);
813 *match = false;
814 return CSS_OK;
815 }
816
817 /* Otherwise, we have the attribute value from the given node
818 * and all we need to do is compare. */
819 dom_string_unref(name);
820 *match = dom_string_lwc_isequal(attr_val, value);
821 if (*match) {
822 /* Exact match, we're done */
823 dom_string_unref(attr_val);
824 return CSS_OK;
825 }
826
827 /* No exact match, try it with a hyphen on the end */
828 vdata = lwc_string_data(value); /* needle */
829 vdata_len = lwc_string_length(value);
830 avdata = dom_string_data(attr_val); /* haystack */
831 avdata_len = dom_string_byte_length(attr_val);
832 dom_string_unref(attr_val);
833
834 if (avdata_len > vdata_len && avdata[vdata_len] == '-') {
835 if (strncasecmp(avdata, vdata, vdata_len) == 0) {
836 /* If there's a hyphen in the right position,
837 * it suffices to compare the strings only up
838 * to the hyphen */
839 *match = true;
840 }
841 }
842
843 return CSS_OK;
844 }
845
846
847 /**
848 * Test the given node for an attribute whose value is a
849 * space-separated list of words, one of which is the given word
850 *
851 * This will return true (via the "match" pointer) if the libdom node
852 * has an attribute with the given name and whose value when
853 * considered as a space-separated list of words contains the given
854 * word. The comparison is case-sensitive. This corresponds to
855 * [attr~=value] in CSS.
856 *
857 * \param pw Pointer to the current SVG parser state
858 * \param node Libdom SVG node to test
859 * \param qname Attribute name to check for
860 * \param word Value word to check for
861 * \param match Pointer to the test result
862 *
863 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
864 * intern the attribute name (which usually indicates memory
865 * exhaustion)
866 */
867 css_error node_has_attribute_includes(void *pw, void *node,
868 const css_qname *qname, lwc_string *word,
869 bool *match)
870 {
871 UNUSED(pw);
872
873 dom_string *name;
874 dom_string *attr_val;
875 dom_exception err;
876 size_t wordlen; /* length of "word" */
877
878 /* pointers used to parse a space-separated list of words */
879 const char *p;
880 const char *start;
881 const char *end;
882
883 *match = false; /* default to no match */
884
885 wordlen = lwc_string_length(word);
886 if (wordlen == 0) {
887 /* In this case, the spec says that "if 'val' is the
888 * empty string, it will never represent anything." */
889 return CSS_OK;
890 }
891
892 /* Intern the attribute name as a dom_string so we can
893 * use dom_element_get_attribute() */
894 err = dom_string_create_interned(
895 (const uint8_t *) lwc_string_data(qname->name),
896 lwc_string_length(qname->name),
897 &name);
898 if (err != DOM_NO_ERR) {
899 return CSS_NOMEM;
900 }
901
902 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
903 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
904 /* There was an error getting the attribute's value or
905 * the attribute doesn't exist. So, no match? */
906 dom_string_unref(name);
907 return CSS_OK;
908 }
909
910 /* Parse the list comparing each word against "word" */
911 start = dom_string_data(attr_val);
912 end = start + dom_string_byte_length(attr_val);
913 dom_string_unref(attr_val);
914
915 for (p = start; p <= end; p++) {
916 /* Move forward until we find the end of the first word */
917 if (*p == ' ' || *p == '\0') {
918 /* If the length of that word is the length of the
919 * word we're looking for, do the comparison. */
920 if ((size_t) (p - start) == wordlen &&
921 strncasecmp(start,
922 lwc_string_data(word),
923 wordlen) == 0) {
924 *match = true;
925 break;
926 }
927 /* No match? Set "start" to the beginning of
928 * the next word and loop. */
929 start = p + 1;
930 }
931 }
932
933 return CSS_OK;
934 }
935
936
937 /**
938 * Test the given node for an attribute whose value begins with the
939 * given prefix
940 *
941 * This will return true (via the "match" pointer) if the libdom node
942 * has an attribute with the given name and whose value begins with
943 * the given prefix string. The comparison is case-sensitive. This
944 * corresponds to [attr^=value] in CSS.
945 *
946 * \param pw Pointer to the current SVG parser state
947 * \param node Libdom SVG node to test
948 * \param qname Attribute name to check for
949 * \param prefix Value prefix to check for
950 * \param match Pointer to the test result
951 *
952 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
953 * intern the attribute name (which usually indicates memory
954 * exhaustion)
955 */
956 css_error node_has_attribute_prefix(void *pw, void *node,
957 const css_qname *qname, lwc_string *prefix,
958 bool *match)
959 {
960 UNUSED(pw);
961 dom_string *name;
962 dom_string *attr_val;
963 dom_exception err;
964 const char *avdata; /* attribute value data */
965 size_t avdata_len; /* length of that attribute value data */
966 size_t prefixlen; /* length of "prefix" */
967
968 prefixlen = lwc_string_length(prefix);
969 if (prefixlen == 0) {
970 /* In this case, the spec says that "if 'val' is the
971 * empty string, it will never represent anything." */
972 return CSS_OK;
973 }
974
975 /* Intern the attribute name as a dom_string so we can
976 * use dom_element_get_attribute() */
977 err = dom_string_create_interned(
978 (const uint8_t *) lwc_string_data(qname->name),
979 lwc_string_length(qname->name),
980 &name);
981 if (err != DOM_NO_ERR) {
982 return CSS_NOMEM;
983 }
984
985 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
986 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
987 /* There was an error getting the attribute's value or
988 * the attribute doesn't exist. So, no match? */
989 dom_string_unref(name);
990 *match = false;
991 return CSS_OK;
992 }
993
994 /* Otherwise, we have the attribute value from the given node,
995 * and the first thing we want to do is check to see if the
996 * whole thing matches the prefix. */
997 dom_string_unref(name);
998 *match = dom_string_lwc_isequal(attr_val, prefix);
999
1000 /* If not, check to see if an, uh, prefix matches the
1001 * prefix */
1002 if (*match == false) {
1003 avdata = dom_string_data(attr_val);
1004 avdata_len = dom_string_byte_length(attr_val);
1005 if ((avdata_len >= prefixlen) &&
1006 (strncasecmp(avdata,
1007 lwc_string_data(prefix),
1008 prefixlen) == 0)) {
1009 /* Use strncasecmp to compare only the first
1010 * "n" characters, where "n" is the length of
1011 * the prefix. */
1012 *match = true;
1013 }
1014 }
1015
1016 dom_string_unref(attr_val);
1017
1018 return CSS_OK;
1019 }
1020
1021
1022 /**
1023 * Test the given node for an attribute whose value end with the
1024 * given suffix
1025 *
1026 * This will return true (via the "match" pointer) if the libdom node
1027 * has an attribute with the given name and whose value ends with
1028 * the given suffix string. The comparison is case-sensitive. This
1029 * corresponds to [attr$=value] in CSS.
1030 *
1031 * \param pw Pointer to the current SVG parser state
1032 * \param node Libdom SVG node to test
1033 * \param qname Attribute name to check for
1034 * \param suffix Value suffix to check for
1035 * \param match Pointer to the test result
1036 *
1037 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1038 * intern the attribute name (which usually indicates memory
1039 * exhaustion)
1040 */
1041 css_error node_has_attribute_suffix(void *pw, void *node,
1042 const css_qname *qname, lwc_string *suffix,
1043 bool *match)
1044 {
1045 UNUSED(pw);
1046 dom_string *name;
1047 dom_string *attr_val;
1048 dom_exception err;
1049 const char *avdata; /* attribute value data */
1050 size_t avdata_len; /* length of that attribute value data */
1051 size_t suffixlen; /* length of "suffix" */
1052
1053 /* convenience pointer we'll use when matching the suffix */
1054 const char *suffix_start;
1055
1056 suffixlen = lwc_string_length(suffix);
1057 if (suffixlen == 0) {
1058 /* In this case, the spec says that "if 'val' is the
1059 * empty string, it will never represent anything." */
1060 return CSS_OK;
1061 }
1062
1063 /* Intern the attribute name as a dom_string so we can
1064 * use dom_element_get_attribute() */
1065 err = dom_string_create_interned(
1066 (const uint8_t *) lwc_string_data(qname->name),
1067 lwc_string_length(qname->name),
1068 &name);
1069 if (err != DOM_NO_ERR) {
1070 return CSS_NOMEM;
1071 }
1072
1073 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
1074 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
1075 /* There was an error getting the attribute's value or
1076 * the attribute doesn't exist. So, no match? */
1077 dom_string_unref(name);
1078 *match = false;
1079 return CSS_OK;
1080 }
1081
1082 /* Otherwise, we have the attribute value from the given node,
1083 * and the first thing we want to do is check to see if the
1084 * whole thing matches the suffix. */
1085 dom_string_unref(name);
1086 *match = dom_string_lwc_isequal(attr_val, suffix);
1087
1088 /* If not, check to see if an, uh, suffix matches the
1089 * suffix */
1090 if (*match == false) {
1091 avdata = dom_string_data(attr_val);
1092 avdata_len = dom_string_byte_length(attr_val);
1093
1094 suffix_start = (char *)(avdata + avdata_len - suffixlen);
1095
1096 if ((avdata_len >= suffixlen) &&
1097 (strncasecmp(suffix_start,
1098 lwc_string_data(suffix),
1099 suffixlen) == 0)) {
1100 /* Use strncasecmp to compare only the last
1101 * "n" characters, where "n" is the length of
1102 * the suffix. */
1103 *match = true;
1104 }
1105 }
1106
1107 dom_string_unref(attr_val);
1108
1109 return CSS_OK;
1110 }
1111
1112
1113 /**
1114 * Implement node_has_attribute_substring() with optional case-
1115 * insensitivity. This corresponds to [attr*=value i] in CSS and is
1116 * not supported by libcss yet, but it allows us to factor out some
1117 * common code.
1118 */
1119 static css_error _node_has_attribute_substring(void *pw, void *node,
1120 const css_qname *qname, lwc_string *substring,
1121 bool *match, bool insensitive)
1122 {
1123 UNUSED(pw);
1124 dom_string *name;
1125 dom_string *attr_val;
1126 dom_exception err;
1127 size_t attr_len; /* length of attr_val */
1128 size_t substrlen; /* length of "substring" */
1129
1130 /* Convenience pointers we use when comparing substrings */
1131 const char *p;
1132 const char *p_max;
1133
1134 substrlen = lwc_string_length(substring);
1135 if (substrlen == 0) {
1136 /* In this case, the spec says that "if 'val' is the
1137 * empty string, it will never represent anything." */
1138 return CSS_OK;
1139 }
1140
1141 /* Intern the attribute name as a dom_string so we can
1142 * use dom_element_get_attribute() */
1143 err = dom_string_create_interned(
1144 (const uint8_t *) lwc_string_data(qname->name),
1145 lwc_string_length(qname->name),
1146 &name);
1147 if (err != DOM_NO_ERR) {
1148 return CSS_NOMEM;
1149 }
1150
1151 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
1152 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
1153 /* There was an error getting the attribute's value or
1154 * the attribute doesn't exist. So, no match? */
1155 dom_string_unref(name);
1156 *match = false;
1157 return CSS_OK;
1158 }
1159
1160 /* Otherwise, we have the attribute value from the given node,
1161 * and the first thing we want to do is check to see if the
1162 * whole thing matches the substring. */
1163 dom_string_unref(name);
1164
1165 if (insensitive) {
1166 *match = dom_string_caseless_lwc_isequal(attr_val, substring);
1167 }
1168 else {
1169 *match = dom_string_lwc_isequal(attr_val, substring);
1170 }
1171
1172 /* If not, check to see if an, uh, substring matches the
1173 * substring */
1174 if (*match == false) {
1175 p = dom_string_data(attr_val);
1176
1177 /* Check every long-enough suffix for a prefix match */
1178 attr_len = dom_string_byte_length(attr_val);
1179 if (attr_len >= substrlen) {
1180 p_max = p + attr_len - substrlen;
1181 while (p <= p_max) {
1182 if (strncasecmp(p,
1183 lwc_string_data(substring),
1184 substrlen) == 0) {
1185 *match = true;
1186 break;
1187 }
1188 p++;
1189 }
1190 }
1191 }
1192
1193 dom_string_unref(attr_val);
1194
1195 return CSS_OK;
1196 }
1197
1198 /**
1199 * Test the given node for an attribute whose value contains the
1200 * given substring
1201 *
1202 * This will return true (via the "match" pointer) if the libdom node
1203 * has an attribute with the given name and whose value contains the
1204 * given substring. The comparison is case-sensitive. This corresponds
1205 * to [attr*=value] in CSS.
1206 *
1207 * \param pw Pointer to the current SVG parser state
1208 * \param node Libdom SVG node to test
1209 * \param qname Attribute name to check for
1210 * \param substring Value substring to check for
1211 * \param match Pointer to the test result
1212 *
1213 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1214 * intern the attribute name (which usually indicates memory
1215 * exhaustion)
1216 */
1217 css_error node_has_attribute_substring(void *pw, void *node,
1218 const css_qname *qname, lwc_string *substring,
1219 bool *match)
1220 {
1221 return _node_has_attribute_substring(pw, node, qname, substring,
1222 match, false);
1223 }
1224
1225
1226 /**
1227 * Test whether or not the given node is the document's root element
1228 * This corresponds to the CSS :root pseudo-selector.
1229 *
1230 * \param pw Pointer to the current SVG parser state
1231 * \param node Libdom SVG node to test
1232 * \param match Pointer to the test result
1233 *
1234 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1235 */
1236 css_error node_is_root(void *pw, void *node, bool *match)
1237 {
1238 UNUSED(pw);
1239 dom_node *parent;
1240 dom_node_type type;
1241 dom_exception err;
1242
1243 err = dom_node_get_parent_node((dom_node *)node, &parent);
1244 if (err != DOM_NO_ERR) {
1245 return CSS_NOMEM;
1246 }
1247
1248 /* It's the root element if it doesn't have a parent element */
1249 if (parent != NULL) {
1250 err = dom_node_get_node_type(parent, &type);
1251 dom_node_unref(parent);
1252 if (err != DOM_NO_ERR) {
1253 return CSS_NOMEM;
1254 }
1255 if (type != DOM_DOCUMENT_NODE) {
1256 /* DOM_DOCUMENT_NODE is the only allowable
1257 * type of parent node for the root element */
1258 *match = false;
1259 return CSS_OK;
1260 }
1261 }
1262
1263 *match = true;
1264 return CSS_OK;
1265 }
1266
1267
1268 /**
1269 * Used internally in node_count_siblings() to "count" the given
1270 * sibling node. It factors out the node type and name checks.
1271 */
1272 static int node_count_siblings_check(dom_node *dnode,
1273 bool check_name,
1274 dom_string *name)
1275 {
1276 int ret;
1277 dom_node_type type;
1278 dom_exception exc;
1279 dom_string *dnode_name;
1280
1281 /* We flip this to 1 if/when we count this node */
1282 ret = 0;
1283
1284 if (dnode == NULL) {
1285 return ret;
1286 }
1287
1288 exc = dom_node_get_node_type(dnode, &type);
1289 if ((exc != DOM_NO_ERR) || (type != DOM_ELEMENT_NODE)) {
1290 /* We only count element siblings */
1291 return ret;
1292 }
1293
1294 /* ... with the right name */
1295 if (check_name) {
1296 dnode_name = NULL;
1297 exc = dom_node_get_node_name(dnode, &dnode_name);
1298
1299 if ((exc == DOM_NO_ERR) && (dnode_name != NULL)) {
1300 if (dom_string_isequal(name,
1301 dnode_name)) {
1302 ret = 1;
1303 }
1304 dom_string_unref(dnode_name);
1305 }
1306 }
1307 else {
1308 ret = 1;
1309 }
1310
1311 return ret;
1312 }
1313
1314 /**
1315 * Count the given node's sibling elements
1316 *
1317 * This counts the given node's sibling elements in one direction,
1318 * either forwards or backwards, in the DOM. Keep in mind that the
1319 * libdom tree is upside-down compared to the CSS one; so "next" and
1320 * "previous" are actually reversed; the default is to count preceding
1321 * libdom siblings which correspond to subsequent CSS siblings.
1322 *
1323 * This operation is central to the CSS :first-child, :nth-child, and
1324 * :last-child (et cetera) pseudo-selectors.
1325 *
1326 * If same_name is true, then only nodes having the same
1327 * (case-sensitive) name as the given node are counted.
1328 *
1329 * \param pw Pointer to the current SVG parser state
1330 * \param node Libdom SVG node whose siblings we're counting
1331 * \param same_name Whether or not to count only siblings having
1332 * the same name as the given node
1333 * \param after Count subsequent siblings rather than precedent
1334 * ones (the default)
1335 * \param count Pointer to the return value, the number of sibling
1336 * elements
1337 *
1338 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1339 */
1340 css_error node_count_siblings(void *pw, void *node,
1341 bool same_name, bool after, int32_t *count)
1342 {
1343 UNUSED(pw);
1344 dom_exception exc;
1345 dom_node *dnode; /* node, but with the right type */
1346 dom_string *dnode_name;
1347 dom_node *next; /* "next" sibling (depends on direction) */
1348
1349 /* Pointer to the "next sibling" function */
1350 dom_exception (*next_func)(dom_node *, dom_node **);
1351
1352 *count = 0;
1353
1354 dnode_name = NULL;
1355 dnode = (dom_node *)node;
1356 if (same_name) {
1357 exc = dom_node_get_node_name(dnode, &dnode_name);
1358 if ((exc != DOM_NO_ERR) || (dnode_name == NULL)) {
1359 return CSS_NOMEM;
1360 }
1361 }
1362
1363 /* Increment the reference counter for dnode for as long as
1364 * we retain a reference to it. */
1365 dnode = dom_node_ref(dnode);
1366
1367 next_func = dom_node_get_previous_sibling;
1368 if (after) {
1369 next_func = dom_node_get_next_sibling;
1370 }
1371
1372 do {
1373 exc = next_func(dnode, &next);
1374 if (exc != DOM_NO_ERR) {
1375 break;
1376 }
1377
1378 /* If next_func worked, we're about to swap "next"
1379 * with "dnode" meaning that we will no longer retain
1380 * a reference to the current dnode. */
1381 dom_node_unref(dnode);
1382 dnode = next;
1383
1384 *count += node_count_siblings_check(dnode,
1385 same_name,
1386 dnode_name);
1387 } while (dnode != NULL);
1388
1389 if (dnode_name != NULL) {
1390 dom_string_unref(dnode_name);
1391 }
1392
1393 return CSS_OK;
1394 }
1395
1396
1397 /**
1398 * Determine whether or not the given element is empty
1399 *
1400 * An element is "nonempty" if it has a child that is either an
1401 * element node or a text node.
1402 *
1403 * \param pw Pointer to the current SVG parser state
1404 * \param node Libdom SVG node to check for emptiness
1405 * \param is_empty Pointer to the return value
1406 *
1407 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1408 */
1409 css_error node_is_empty(void *pw, void *node, bool *is_empty)
1410 {
1411 UNUSED(pw);
1412 dom_node *child; /* current child node pointer */
1413 dom_node *next; /* next child node pointer */
1414 dom_node_type type; /* what type of node is "child" */
1415 dom_exception err;
1416
1417 /* Assume that it's empty by default */
1418 *is_empty = true;
1419
1420 /* Get the given node's first child. Implementation detail:
1421 * this increments the reference counter on the child node. */
1422 err = dom_node_get_first_child((dom_node *)node, &child);
1423 if (err != DOM_NO_ERR) {
1424 return CSS_NOMEM;
1425 }
1426
1427 /* And now loop through all children looking for a
1428 * text/element node. If we find one, the original
1429 * node is "nonempty" */
1430 while (child != NULL) {
1431 err = dom_node_get_node_type(child, &type);
1432 if (err != DOM_NO_ERR) {
1433 dom_node_unref(child);
1434 return CSS_NOMEM;
1435 }
1436
1437 if (type == DOM_ELEMENT_NODE || type == DOM_TEXT_NODE) {
1438 *is_empty = false;
1439 dom_node_unref(child);
1440 return CSS_OK;
1441 }
1442
1443 err = dom_node_get_next_sibling(child, &next);
1444 if (err != DOM_NO_ERR) {
1445 dom_node_unref(child);
1446 return CSS_NOMEM;
1447 }
1448
1449 /* If we're moving to the next node, we can release
1450 * the reference to the current one */
1451 dom_node_unref(child);
1452 child = next;
1453 }
1454
1455 return CSS_OK;
1456 }
1457
1458
1459 /**
1460 * Determine whether or not the given node is a link
1461 *
1462 * A node is a link if it is an element node whose name is "a" and if
1463 * it has an "href" attribute (case-sensitive). This selector
1464 * corresponds to node:link pseudo-class in CSS.
1465 *
1466 * This pseudo-class is a bit awkward because the two standards (HTML5
1467 * and CSS) disagree on what it means, and because libsvgtiny does not
1468 * have enough information to determine if a link has been "visited"
1469 * yet -- that's a UI property. CSS says that :link is for unvisited
1470 * links, which we can't determine. HTML5 says that each link must
1471 * be either a :link or :visited. Since we can't decide either way,
1472 * It seems less wrong to declare that all links are unvisited; i.e.
1473 * that they match :link.
1474 *
1475 * \param pw Pointer to the current SVG parser state
1476 * \param node Libdom SVG node to check
1477 * \param is_link Pointer to the boolean return value
1478 *
1479 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1480 */
1481 css_error node_is_link(void *pw, void *node, bool *is_link)
1482 {
1483 dom_exception exc;
1484 dom_node *dnode; /* node, but with the right type */
1485 dom_string *dnode_name;
1486 bool has_href;
1487 struct svgtiny_parse_state* state;
1488
1489 dnode = (dom_node *)node;
1490 dnode_name = NULL;
1491 has_href = false; /* assume no href attribute */
1492 *is_link = false; /* assume that it's not a link */
1493
1494 exc = dom_node_get_node_name(dnode, &dnode_name);
1495 if ((exc != DOM_NO_ERR) || (dnode_name == NULL)) {
1496 return CSS_NOMEM;
1497 }
1498
1499 state = (struct svgtiny_parse_state *)pw;
1500 if (dom_string_isequal(dnode_name, state->interned_a)) {
1501 exc = dom_element_has_attribute(node,
1502 state->interned_href,
1503 &has_href);
1504 if (exc == DOM_NO_ERR && has_href) {
1505 *is_link = true;
1506 }
1507 }
1508
1509 dom_string_unref(dnode_name);
1510 return CSS_OK;
1511 }
1512
1513
1514 /**
1515 * Check if the given node is being "hovered" over
1516 *
1517 * This check always fails because the SVG DOM does not have the
1518 * necessary information (it's a UI property).
1519 *
1520 * \param pw Pointer to the current SVG parser state; unused
1521 * \param node Libdom SVG node to check; unused
1522 * \param is_hover Pointer to the boolean return value
1523 *
1524 * \return Always returns CSS_OK
1525 */
1526 css_error node_is_hover(void *pw, void *node, bool *is_hover)
1527 {
1528 UNUSED(pw);
1529 UNUSED(node);
1530 *is_hover = false;
1531 return CSS_OK;
1532 }
1533
1534
1535 /**
1536 * Check if the given node is "active"
1537 *
1538 * This check always fails because the SVG DOM does not have the
1539 * necessary information (it's a UI property).
1540 *
1541 * \param pw Pointer to the current SVG parser state; unused
1542 * \param node Libdom SVG node to check; unused
1543 * \param is_active Pointer to the boolean return value
1544 *
1545 * \return Always returns CSS_OK
1546 */
1547 css_error node_is_active(void *pw, void *node, bool *is_active)
1548 {
1549 UNUSED(pw);
1550 UNUSED(node);
1551 *is_active = false;
1552 return CSS_OK;
1553 }
1554
1555
1556 /**
1557 * Check if the given node has the focus
1558 *
1559 * This check always fails because the SVG DOM does not have the
1560 * necessary information (it's a UI property).
1561 *
1562 * \param pw Pointer to the current SVG parser state; unused
1563 * \param node Libdom SVG node to check; unused
1564 * \param is_focus Pointer to the boolean return value
1565 *
1566 * \return Always returns CSS_OK
1567 */
1568 css_error node_is_focus(void *pw, void *node, bool *is_focus)
1569 {
1570 UNUSED(pw);
1571 UNUSED(node);
1572 *is_focus = false;
1573 return CSS_OK;
1574 }
1575
1576
1577 /**
1578 * Check if the given node is enabled
1579 *
1580 * This check always fails because the SVG DOM does not have the
1581 * necessary information (it's a UI property).
1582 *
1583 * \param pw Pointer to the current SVG parser state; unused
1584 * \param node Libdom SVG node to check; unused
1585 * \param is_enabled Pointer to the boolean return value
1586 *
1587 * \return Always returns CSS_OK
1588 */
1589 css_error node_is_enabled(void *pw, void *node, bool *is_enabled)
1590 {
1591 UNUSED(pw);
1592 UNUSED(node);
1593 *is_enabled = false;
1594 return CSS_OK;
1595 }
1596
1597
1598 /**
1599 * Check if the given node is disabled
1600 *
1601 * This check always fails because the SVG DOM does not have the
1602 * necessary information (it's a UI property). Beware, until they are
1603 * implemented, this is NOT the logical negation of node_is_enabled!
1604 *
1605 * \param pw Pointer to the current SVG parser state; unused
1606 * \param node Libdom SVG node to check; unused
1607 * \param is_disabled Pointer to the boolean return value
1608 *
1609 * \return Always returns CSS_OK
1610 */
1611 css_error node_is_disabled(void *pw, void *node, bool *is_disabled)
1612 {
1613 UNUSED(pw);
1614 UNUSED(node);
1615 *is_disabled = false;
1616 return CSS_OK;
1617 }
1618
1619
1620 /**
1621 * Test whether or not the given node is "checked"
1622 *
1623 * This test always fails because the SVG DOM does not have the
1624 * necessary information (it's a UI property).
1625 *
1626 * \param pw Pointer to the current SVG parser state; unused
1627 * \param node Libdom SVG node to check; unused
1628 * \param is_checked Pointer to the boolean return value
1629 *
1630 * \return Always returns CSS_OK
1631 */
1632 css_error node_is_checked(void *pw, void *node, bool *is_checked)
1633 {
1634 UNUSED(pw);
1635 UNUSED(node);
1636 *is_checked = false;
1637 return CSS_OK;
1638 }
1639
1640
1641 /**
1642 * Check if the given node is the "target" of the document URL
1643 *
1644 * This test always fails because the SVG DOM does not have the
1645 * necessary information (it's a UI property).
1646 *
1647 * \param pw Pointer to the current SVG parser state; unused
1648 * \param node Libdom SVG node to check; unused
1649 * \param is_target Pointer to the boolean return value
1650 *
1651 * \return Always returns CSS_OK
1652 */
1653 css_error node_is_target(void *pw, void *node, bool *is_target)
1654 {
1655 UNUSED(pw);
1656 UNUSED(node);
1657 *is_target = false;
1658 return CSS_OK;
1659 }