]> gitweb.michael.orlitzky.com - libsvgtiny.git/blob - src/svgtiny_css.c
7e096487f14ac33e1e0f7ad37874ddf553e71b92
[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 * Test the given node for an attribute whose value contains the
1115 * given substring
1116 *
1117 * This will return true (via the "match" pointer) if the libdom node
1118 * has an attribute with the given name and whose value contains the
1119 * given substring. The comparison is case-sensitive. This corresponds
1120 * to [attr*=value] in CSS.
1121 *
1122 * \param pw Pointer to the current SVG parser state
1123 * \param node Libdom SVG node to test
1124 * \param qname Attribute name to check for
1125 * \param substring Value substring to check for
1126 * \param match Pointer to the test result
1127 *
1128 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1129 * intern the attribute name (which usually indicates memory
1130 * exhaustion)
1131 */
1132 css_error node_has_attribute_substring(void *pw, void *node,
1133 const css_qname *qname, lwc_string *substring,
1134 bool *match)
1135 {
1136 UNUSED(pw);
1137 dom_string *name;
1138 dom_string *attr_val;
1139 dom_exception err;
1140 size_t attr_len; /* length of attr_val */
1141 size_t substrlen; /* length of "substring" */
1142
1143 /* Convenience pointers we use when comparing substrings */
1144 const char *p;
1145 const char *p_max;
1146
1147 substrlen = lwc_string_length(substring);
1148 if (substrlen == 0) {
1149 /* In this case, the spec says that "if 'val' is the
1150 * empty string, it will never represent anything." */
1151 return CSS_OK;
1152 }
1153
1154 /* Intern the attribute name as a dom_string so we can
1155 * use dom_element_get_attribute() */
1156 err = dom_string_create_interned(
1157 (const uint8_t *) lwc_string_data(qname->name),
1158 lwc_string_length(qname->name),
1159 &name);
1160 if (err != DOM_NO_ERR) {
1161 return CSS_NOMEM;
1162 }
1163
1164 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
1165 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
1166 /* There was an error getting the attribute's value or
1167 * the attribute doesn't exist. So, no match? */
1168 dom_string_unref(name);
1169 *match = false;
1170 return CSS_OK;
1171 }
1172
1173 /* Otherwise, we have the attribute value from the given node,
1174 * and the first thing we want to do is check to see if the
1175 * whole thing matches the substring. */
1176 dom_string_unref(name);
1177 *match = dom_string_lwc_isequal(attr_val, substring);
1178
1179 /* If not, check to see if an, uh, substring matches the
1180 * substring */
1181 if (*match == false) {
1182 p = dom_string_data(attr_val);
1183
1184 /* Check every long-enough suffix for a prefix match */
1185 attr_len = dom_string_byte_length(attr_val);
1186 if (attr_len >= substrlen) {
1187 p_max = p + attr_len - substrlen;
1188 while (p <= p_max) {
1189 if (strncasecmp(p,
1190 lwc_string_data(substring),
1191 substrlen) == 0) {
1192 *match = true;
1193 break;
1194 }
1195 p++;
1196 }
1197 }
1198 }
1199
1200 dom_string_unref(attr_val);
1201
1202 return CSS_OK;
1203 }
1204
1205
1206 /**
1207 * Test whether or not the given node is the document's root element
1208 * This corresponds to the CSS :root pseudo-selector.
1209 *
1210 * \param pw Pointer to the current SVG parser state
1211 * \param node Libdom SVG node to test
1212 * \param match Pointer to the test result
1213 *
1214 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1215 */
1216 css_error node_is_root(void *pw, void *node, bool *match)
1217 {
1218 UNUSED(pw);
1219 dom_node *parent;
1220 dom_node_type type;
1221 dom_exception err;
1222
1223 err = dom_node_get_parent_node((dom_node *)node, &parent);
1224 if (err != DOM_NO_ERR) {
1225 return CSS_NOMEM;
1226 }
1227
1228 /* It's the root element if it doesn't have a parent element */
1229 if (parent != NULL) {
1230 err = dom_node_get_node_type(parent, &type);
1231 dom_node_unref(parent);
1232 if (err != DOM_NO_ERR) {
1233 return CSS_NOMEM;
1234 }
1235 if (type != DOM_DOCUMENT_NODE) {
1236 /* DOM_DOCUMENT_NODE is the only allowable
1237 * type of parent node for the root element */
1238 *match = false;
1239 return CSS_OK;
1240 }
1241 }
1242
1243 *match = true;
1244 return CSS_OK;
1245 }
1246
1247
1248 /**
1249 * Used internally in node_count_siblings() to "count" the given
1250 * sibling node. It factors out the node type and name checks.
1251 */
1252 static int node_count_siblings_check(dom_node *dnode,
1253 bool check_name,
1254 dom_string *name)
1255 {
1256 int ret;
1257 dom_node_type type;
1258 dom_exception exc;
1259 dom_string *dnode_name;
1260
1261 /* We flip this to 1 if/when we count this node */
1262 ret = 0;
1263
1264 if (dnode == NULL) {
1265 return ret;
1266 }
1267
1268 exc = dom_node_get_node_type(dnode, &type);
1269 if ((exc != DOM_NO_ERR) || (type != DOM_ELEMENT_NODE)) {
1270 /* We only count element siblings */
1271 return ret;
1272 }
1273
1274 /* ... with the right name */
1275 if (check_name) {
1276 dnode_name = NULL;
1277 exc = dom_node_get_node_name(dnode, &dnode_name);
1278
1279 if ((exc == DOM_NO_ERR) && (dnode_name != NULL)) {
1280 if (dom_string_isequal(name,
1281 dnode_name)) {
1282 ret = 1;
1283 }
1284 dom_string_unref(dnode_name);
1285 }
1286 }
1287 else {
1288 ret = 1;
1289 }
1290
1291 return ret;
1292 }
1293
1294 /**
1295 * Count the given node's sibling elements
1296 *
1297 * This counts the given node's sibling elements in one direction,
1298 * either forwards or backwards, in the DOM. Keep in mind that the
1299 * libdom tree is upside-down compared to the CSS one; so "next" and
1300 * "previous" are actually reversed; the default is to count preceding
1301 * libdom siblings which correspond to subsequent CSS siblings.
1302 *
1303 * This operation is central to the CSS :first-child, :nth-child, and
1304 * :last-child (et cetera) pseudo-selectors.
1305 *
1306 * If same_name is true, then only nodes having the same
1307 * (case-sensitive) name as the given node are counted.
1308 *
1309 * \param pw Pointer to the current SVG parser state
1310 * \param node Libdom SVG node whose siblings we're counting
1311 * \param same_name Whether or not to count only siblings having
1312 * the same name as the given node
1313 * \param after Count subsequent siblings rather than precedent
1314 * ones (the default)
1315 * \param count Pointer to the return value, the number of sibling
1316 * elements
1317 *
1318 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1319 */
1320 css_error node_count_siblings(void *pw, void *node,
1321 bool same_name, bool after, int32_t *count)
1322 {
1323 UNUSED(pw);
1324 dom_exception exc;
1325 dom_node *dnode; /* node, but with the right type */
1326 dom_string *dnode_name;
1327 dom_node *next; /* "next" sibling (depends on direction) */
1328
1329 /* Pointer to the "next sibling" function */
1330 dom_exception (*next_func)(dom_node *, dom_node **);
1331
1332 *count = 0;
1333
1334 dnode_name = NULL;
1335 dnode = (dom_node *)node;
1336 if (same_name) {
1337 exc = dom_node_get_node_name(dnode, &dnode_name);
1338 if ((exc != DOM_NO_ERR) || (dnode_name == NULL)) {
1339 return CSS_NOMEM;
1340 }
1341 }
1342
1343 /* Increment the reference counter for dnode for as long as
1344 * we retain a reference to it. */
1345 dnode = dom_node_ref(dnode);
1346
1347 next_func = dom_node_get_previous_sibling;
1348 if (after) {
1349 next_func = dom_node_get_next_sibling;
1350 }
1351
1352 do {
1353 exc = next_func(dnode, &next);
1354 if (exc != DOM_NO_ERR) {
1355 break;
1356 }
1357
1358 /* If next_func worked, we're about to swap "next"
1359 * with "dnode" meaning that we will no longer retain
1360 * a reference to the current dnode. */
1361 dom_node_unref(dnode);
1362 dnode = next;
1363
1364 *count += node_count_siblings_check(dnode,
1365 same_name,
1366 dnode_name);
1367 } while (dnode != NULL);
1368
1369 if (dnode_name != NULL) {
1370 dom_string_unref(dnode_name);
1371 }
1372
1373 return CSS_OK;
1374 }
1375
1376
1377 /**
1378 * Determine whether or not the given element is empty
1379 *
1380 * An element is "nonempty" if it has a child that is either an
1381 * element node or a text node.
1382 *
1383 * \param pw Pointer to the current SVG parser state
1384 * \param node Libdom SVG node to check for emptiness
1385 * \param is_empty Pointer to the return value
1386 *
1387 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1388 */
1389 css_error node_is_empty(void *pw, void *node, bool *is_empty)
1390 {
1391 UNUSED(pw);
1392 dom_node *child; /* current child node pointer */
1393 dom_node *next; /* next child node pointer */
1394 dom_node_type type; /* what type of node is "child" */
1395 dom_exception err;
1396
1397 /* Assume that it's empty by default */
1398 *is_empty = true;
1399
1400 /* Get the given node's first child. Implementation detail:
1401 * this increments the reference counter on the child node. */
1402 err = dom_node_get_first_child((dom_node *)node, &child);
1403 if (err != DOM_NO_ERR) {
1404 return CSS_NOMEM;
1405 }
1406
1407 /* And now loop through all children looking for a
1408 * text/element node. If we find one, the original
1409 * node is "nonempty" */
1410 while (child != NULL) {
1411 err = dom_node_get_node_type(child, &type);
1412 if (err != DOM_NO_ERR) {
1413 dom_node_unref(child);
1414 return CSS_NOMEM;
1415 }
1416
1417 if (type == DOM_ELEMENT_NODE || type == DOM_TEXT_NODE) {
1418 *is_empty = false;
1419 dom_node_unref(child);
1420 return CSS_OK;
1421 }
1422
1423 err = dom_node_get_next_sibling(child, &next);
1424 if (err != DOM_NO_ERR) {
1425 dom_node_unref(child);
1426 return CSS_NOMEM;
1427 }
1428
1429 /* If we're moving to the next node, we can release
1430 * the reference to the current one */
1431 dom_node_unref(child);
1432 child = next;
1433 }
1434
1435 return CSS_OK;
1436 }
1437
1438
1439 /**
1440 * Determine whether or not the given node is a link
1441 *
1442 * A node is a link if it is an element node whose name is "a" and if
1443 * it has an "href" attribute (case-sensitive). This selector
1444 * corresponds to node:link pseudo-class in CSS.
1445 *
1446 * This pseudo-class is a bit awkward because the two standards (HTML5
1447 * and CSS) disagree on what it means, and because libsvgtiny does not
1448 * have enough information to determine if a link has been "visited"
1449 * yet -- that's a UI property. CSS says that :link is for unvisited
1450 * links, which we can't determine. HTML5 says that each link must
1451 * be either a :link or :visited. Since we can't decide either way,
1452 * It seems less wrong to declare that all links are unvisited; i.e.
1453 * that they match :link.
1454 *
1455 * \param pw Pointer to the current SVG parser state
1456 * \param node Libdom SVG node to check
1457 * \param is_link Pointer to the boolean return value
1458 *
1459 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1460 */
1461 css_error node_is_link(void *pw, void *node, bool *is_link)
1462 {
1463 dom_exception exc;
1464 dom_node *dnode; /* node, but with the right type */
1465 dom_string *dnode_name;
1466 bool has_href;
1467 struct svgtiny_parse_state* state;
1468
1469 dnode = (dom_node *)node;
1470 dnode_name = NULL;
1471 has_href = false; /* assume no href attribute */
1472 *is_link = false; /* assume that it's not a link */
1473
1474 exc = dom_node_get_node_name(dnode, &dnode_name);
1475 if ((exc != DOM_NO_ERR) || (dnode_name == NULL)) {
1476 return CSS_NOMEM;
1477 }
1478
1479 state = (struct svgtiny_parse_state *)pw;
1480 if (dom_string_isequal(dnode_name, state->interned_a)) {
1481 exc = dom_element_has_attribute(node,
1482 state->interned_href,
1483 &has_href);
1484 if (exc == DOM_NO_ERR && has_href) {
1485 *is_link = true;
1486 }
1487 }
1488
1489 dom_string_unref(dnode_name);
1490 return CSS_OK;
1491 }
1492
1493
1494 /**
1495 * Check if the given node is being "hovered" over
1496 *
1497 * This check always fails because the SVG DOM does not have the
1498 * necessary information (it's a UI property).
1499 *
1500 * \param pw Pointer to the current SVG parser state; unused
1501 * \param node Libdom SVG node to check; unused
1502 * \param is_hover Pointer to the boolean return value
1503 *
1504 * \return Always returns CSS_OK
1505 */
1506 css_error node_is_hover(void *pw, void *node, bool *is_hover)
1507 {
1508 UNUSED(pw);
1509 UNUSED(node);
1510 *is_hover = false;
1511 return CSS_OK;
1512 }
1513
1514
1515 /**
1516 * Check if the given node is "active"
1517 *
1518 * This check always fails because the SVG DOM does not have the
1519 * necessary information (it's a UI property).
1520 *
1521 * \param pw Pointer to the current SVG parser state; unused
1522 * \param node Libdom SVG node to check; unused
1523 * \param is_active Pointer to the boolean return value
1524 *
1525 * \return Always returns CSS_OK
1526 */
1527 css_error node_is_active(void *pw, void *node, bool *is_active)
1528 {
1529 UNUSED(pw);
1530 UNUSED(node);
1531 *is_active = false;
1532 return CSS_OK;
1533 }
1534
1535
1536 /**
1537 * Check if the given node has the focus
1538 *
1539 * This check always fails because the SVG DOM does not have the
1540 * necessary information (it's a UI property).
1541 *
1542 * \param pw Pointer to the current SVG parser state; unused
1543 * \param node Libdom SVG node to check; unused
1544 * \param is_focus Pointer to the boolean return value
1545 *
1546 * \return Always returns CSS_OK
1547 */
1548 css_error node_is_focus(void *pw, void *node, bool *is_focus)
1549 {
1550 UNUSED(pw);
1551 UNUSED(node);
1552 *is_focus = false;
1553 return CSS_OK;
1554 }
1555
1556
1557 /**
1558 * Check if the given node is enabled
1559 *
1560 * This check always fails because the SVG DOM does not have the
1561 * necessary information (it's a UI property).
1562 *
1563 * \param pw Pointer to the current SVG parser state; unused
1564 * \param node Libdom SVG node to check; unused
1565 * \param is_enabled Pointer to the boolean return value
1566 *
1567 * \return Always returns CSS_OK
1568 */
1569 css_error node_is_enabled(void *pw, void *node, bool *is_enabled)
1570 {
1571 UNUSED(pw);
1572 UNUSED(node);
1573 *is_enabled = false;
1574 return CSS_OK;
1575 }
1576
1577
1578 /**
1579 * Check if the given node is disabled
1580 *
1581 * This check always fails because the SVG DOM does not have the
1582 * necessary information (it's a UI property). Beware, until they are
1583 * implemented, this is NOT the logical negation of node_is_enabled!
1584 *
1585 * \param pw Pointer to the current SVG parser state; unused
1586 * \param node Libdom SVG node to check; unused
1587 * \param is_disabled Pointer to the boolean return value
1588 *
1589 * \return Always returns CSS_OK
1590 */
1591 css_error node_is_disabled(void *pw, void *node, bool *is_disabled)
1592 {
1593 UNUSED(pw);
1594 UNUSED(node);
1595 *is_disabled = false;
1596 return CSS_OK;
1597 }
1598
1599
1600 /**
1601 * Test whether or not the given node is "checked"
1602 *
1603 * This test always fails because the SVG DOM does not have the
1604 * necessary information (it's a UI property).
1605 *
1606 * \param pw Pointer to the current SVG parser state; unused
1607 * \param node Libdom SVG node to check; unused
1608 * \param is_checked Pointer to the boolean return value
1609 *
1610 * \return Always returns CSS_OK
1611 */
1612 css_error node_is_checked(void *pw, void *node, bool *is_checked)
1613 {
1614 UNUSED(pw);
1615 UNUSED(node);
1616 *is_checked = false;
1617 return CSS_OK;
1618 }
1619
1620
1621 /**
1622 * Check if the given node is the "target" of the document URL
1623 *
1624 * This test always fails because the SVG DOM does not have the
1625 * necessary information (it's a UI property).
1626 *
1627 * \param pw Pointer to the current SVG parser state; unused
1628 * \param node Libdom SVG node to check; unused
1629 * \param is_target Pointer to the boolean return value
1630 *
1631 * \return Always returns CSS_OK
1632 */
1633 css_error node_is_target(void *pw, void *node, bool *is_target)
1634 {
1635 UNUSED(pw);
1636 UNUSED(node);
1637 *is_target = false;
1638 return CSS_OK;
1639 }