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