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