1 #include <libcss/libcss.h>
2 #include <strings.h> /* strncasecmp */
5 #include "svgtiny_internal.h"
7 /* select handler callbacks */
8 static css_error
node_name(void *pw
, void *node
, css_qname
*qname
);
9 static css_error
node_classes(void *pw
, void *node
,
10 lwc_string
***classes
, uint32_t *n_classes
);
11 static css_error
node_id(void *pw
, void *node
, lwc_string
**id
);
12 static css_error
named_ancestor_node(void *pw
, void *node
,
13 const css_qname
*qname
, void **ancestor
);
14 static css_error
named_parent_node(void *pw
, void *node
,
15 const css_qname
*qname
, void **parent
);
16 static css_error
named_sibling_node(void *pw
, void *node
,
17 const css_qname
*qname
, void **sibling
);
18 static css_error
named_generic_sibling_node(void *pw
, void *node
,
19 const css_qname
*qname
, void **sibling
);
20 static css_error
parent_node(void *pw
, void *node
, void **parent
);
21 static css_error
sibling_node(void *pw
, void *node
, void **sibling
);
22 static css_error
node_has_name(void *pw
, void *node
,
23 const css_qname
*qname
, bool *match
);
24 static css_error
node_has_class(void *pw
, void *node
,
25 lwc_string
*name
, bool *match
);
26 static css_error
node_has_id(void *pw
, void *node
,
27 lwc_string
*name
, bool *match
);
28 static css_error
node_has_attribute(void *pw
, void *node
,
29 const css_qname
*qname
, bool *match
);
30 static css_error
node_has_attribute_equal(void *pw
, void *node
,
31 const css_qname
*qname
, lwc_string
*value
,
33 static css_error
node_has_attribute_dashmatch(void *pw
, void *node
,
34 const css_qname
*qname
, lwc_string
*value
,
36 static css_error
node_has_attribute_includes(void *pw
, void *node
,
37 const css_qname
*qname
, lwc_string
*word
,
39 static css_error
node_has_attribute_prefix(void *pw
, void *node
,
40 const css_qname
*qname
, lwc_string
*prefix
,
42 static css_error
node_has_attribute_suffix(void *pw
, void *node
,
43 const css_qname
*qname
, lwc_string
*suffix
,
45 static css_error
node_has_attribute_substring(void *pw
, void *node
,
46 const css_qname
*qname
, lwc_string
*substring
,
48 static css_error
node_is_root(void *pw
, void *node
, bool *match
);
49 static css_error
node_count_siblings(void *pw
, void *node
,
50 bool same_name
, bool after
, int32_t *count
);
51 static css_error
node_is_empty(void *pw
, void *node
, bool *is_empty
);
52 static css_error
node_is_link(void *pw
, void *node
, bool *is_link
);
53 static css_error
node_is_visited(void *pw
, void *node
, bool *is_visited
);
54 static css_error
node_is_hover(void *pw
, void *node
, bool *is_hover
);
55 static css_error
node_is_active(void *pw
, void *node
, bool *is_active
);
56 static css_error
node_is_focus(void *pw
, void *node
, bool *is_focus
);
57 static css_error
node_is_enabled(void *pw
, void *node
, bool *is_enabled
);
58 static css_error
node_is_disabled(void *pw
, void *node
, bool *is_disabled
);
59 static css_error
node_is_checked(void *pw
, void *node
, bool *is_checked
);
60 static css_error
node_is_target(void *pw
, void *node
, bool *is_target
);
61 static css_error
node_is_lang(void *pw
, void *node
,
62 lwc_string
*lang
, bool *is_lang
);
63 static css_error
node_presentational_hint(void *pw
, void *node
,
64 uint32_t *nhints
, css_hint
**hints
);
65 static css_error
ua_default_for_property(void *pw
, uint32_t property
,
67 static css_error
set_libcss_node_data(void *pw
, void *node
,
68 void *libcss_node_data
);
69 static css_error
get_libcss_node_data(void *pw
, void *node
,
70 void **libcss_node_data
);
72 /* select handler vtable */
73 static struct css_select_handler svgtiny_select_handler
;
76 /* Every call to svgtiny_select_style() needs this, so let's only make
78 static const css_media media_all
= {
79 .type
= CSS_MEDIA_ALL
,
83 * Convenient wrapper around css_select_style()
85 * \param state The current state of the libsvgtiny parser
86 * \param node The node that we're getting styles for
87 * \param inline_sheet The inline stylesheet for the given node
88 * \param result Address at which to store the results array
90 css_error
svgtiny_select_style(struct svgtiny_parse_state
*state
,
92 const css_stylesheet
*inline_sheet
,
93 css_select_results
**result
)
95 return css_select_style(state
->select_ctx
,
100 &svgtiny_select_handler
,
106 * Resolve a relative URL to an absolute one by doing nothing. This is
107 * the simplest possible implementation of a URL resolver, needed for
110 css_error
svgtiny_resolve_url(void *pw
,
111 const char *base
, lwc_string
*rel
, lwc_string
**abs
)
116 /* Copy the relative URL to the absolute one (the return
118 *abs
= lwc_string_ref(rel
);
123 * Create a stylesheet with the default set of params.
125 * \param sheet A stylesheet pointer, passed in by reference, that
126 * we use to store the newly-created stylesheet.
127 * \param inline_style True if this stylesheet represents an inline
128 * style, and false otherwise.
130 * \return The return value from css_stylesheet_create() is returned.
132 css_error
svgtiny_create_stylesheet(css_stylesheet
**sheet
,
135 css_stylesheet_params params
;
137 params
.params_version
= CSS_STYLESHEET_PARAMS_VERSION_1
;
138 params
.level
= CSS_LEVEL_DEFAULT
;
139 params
.charset
= NULL
;
142 params
.allow_quirks
= false;
143 params
.inline_style
= inline_style
;
144 params
.resolve
= svgtiny_resolve_url
;
145 params
.resolve_pw
= NULL
;
146 params
.import = NULL
;
147 params
.import_pw
= NULL
;
149 params
.color_pw
= NULL
;
151 params
.font_pw
= NULL
;
153 return css_stylesheet_create(¶ms
, sheet
);
157 /**************************/
158 /* libcss select handlers */
159 /**************************/
161 * From here on we implement the "select handler "API defined in
162 * libcss's include/libcss/select.h and discussed briefly in its
168 * Retrieve the given node's name
170 * \param pw Pointer to the current SVG parser state
171 * \param node Libdom SVG node
172 * \param qname Address at which to store the node name
174 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
176 css_error
node_name(void *pw
, void *node
, css_qname
*qname
)
180 struct svgtiny_parse_state
*state
;
182 err
= dom_node_get_node_name((dom_node
*)node
, &name
);
183 if (err
!= DOM_NO_ERR
) {
187 state
= (struct svgtiny_parse_state
*)pw
;
188 qname
->ns
= lwc_string_ref(state
->interned_svg_xmlns
);
190 err
= dom_string_intern(name
, &qname
->name
);
191 if (err
!= DOM_NO_ERR
) {
192 dom_string_unref(name
);
196 dom_string_unref(name
);
203 * Retrieve the given node's classes
205 * \param pw Pointer to the current SVG parser state
206 * \param node Libdom SVG node
207 * \param classes Address at which to store the class name array
208 * \param n_classes Address at which to store the length of the class
211 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
213 * \note CSS_NOMEM is not possible in practice as of libdom-0.4.1,
214 * because the underlying libdom function never fails
216 css_error
node_classes(void *pw
, void *node
,
217 lwc_string
***classes
, uint32_t *n_classes
)
222 err
= dom_element_get_classes((dom_node
*)node
, classes
, n_classes
);
224 /* The implementation does not do it, but the documentation
225 for dom_element_get_classes() says that a DOM_NO_MEM_ERR is
226 possible here, so we handle it to be on the safe side. */
227 if (err
!= DOM_NO_ERR
) {
236 * Retrieve the given node's id
238 * \param pw Pointer to the current SVG parser state
239 * \param node Libdom SVG node
240 * \param id Address at which to store the id
242 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
244 css_error
node_id(void *pw
, void *node
, lwc_string
**id
)
248 struct svgtiny_parse_state
*state
;
250 /* Begin with the assumption that this node has no id */
253 state
= (struct svgtiny_parse_state
*)pw
;
254 err
= dom_element_get_attribute((dom_node
*)node
,
255 state
->interned_id
, &attr
);
256 if (err
!= DOM_NO_ERR
) {
259 else if (attr
== NULL
) {
260 /* The node has no id attribute and our return value
261 is already set to NULL so we're done */
265 /* If we found an id attribute (a dom_string), intern it into
266 an lwc_string that we can return, and then cleanup the
268 err
= dom_string_intern(attr
, id
);
269 if (err
!= DOM_NO_ERR
) {
270 dom_string_unref(attr
);
273 dom_string_unref(attr
);
279 * Find the first ancestor of the given element having the given name
281 * This function thinly wraps dom_element_named_ancestor_node(), which
282 * performs exactly the search we want.
284 * \param pw Pointer to the current SVG parser state
285 * \param node Libdom SVG node whose ancestor we want
286 * \param qname Name of the ancestor node to search for
287 * \param ancestor Address at which to store the ancestor node pointer
289 * \return Always returns CSS_OK
291 * \post If a suitable element is found, a pointer to it will be
292 * stored at the address pointed to by \a ancestor; otherwise,
293 * NULL will be stored at the address pointed to by \a ancestor
295 css_error
named_ancestor_node(void *pw
, void *node
,
296 const css_qname
*qname
, void **ancestor
)
300 /* It's OK if node isn't an element, libdom checks for it. */
301 dom_element_named_ancestor_node((dom_element
*)node
,
303 (struct dom_element
**)ancestor
);
305 /* A comment in named_parent_node() explains why we unref
307 dom_node_unref(*ancestor
);
314 * Find the first parent of the given element having the given name
316 * \param pw Pointer to the current SVG parser state
317 * \param node Libdom SVG node
318 * \param qname Name of the parent node to search for
319 * \param parent Address at which to store the parent node pointer
321 * \return Always returns CSS_OK
323 * \post If a suitable element is found, a pointer to it will be
324 * stored at the address pointed to by \a parent; otherwise,
325 * NULL will be stored at the address pointed to by \a parent
327 css_error
named_parent_node(void *pw
, void *node
,
328 const css_qname
*qname
, void **parent
)
331 /* dom_element_named_parent_node() was invented to implement
332 * this select handler so there isn't much for us to do except
333 * call it. It's OK if node isn't an element, libdom checks
335 dom_element_named_parent_node((dom_element
*)node
,
337 (struct dom_element
**)parent
);
339 /* Implementation detail: dom_element_named_parent_node()
340 * increments the reference count of the parent element before
341 * returning it to us. According to docs/RefCnt in the libdom
342 * repository, this will prevent the parent element from being
343 * destroyed if it is pruned from the DOM. That sounds good,
344 * since we don't want to be using a pointer to an object that
345 * has been destroyed... but we also have no way of later
346 * decrementing the reference count ourselves, and don't want
347 * to make the returned node eternal. Decrementing the
348 * reference counter now allows it to be destroyed when the
349 * DOM no longer needs it, and so long as no other parts of
350 * libsvgtiny are messing with the DOM during parsing, that
351 * shouldn't (ha ha) cause any problems. */
352 dom_node_unref(*parent
);
359 * Find the "next-sibling" of the given element having the given name
361 * This search corresponds to the "+ foo" combinator in CSS and will
362 * find only "foo" element nodes that immediately precede the given
363 * node under the same parent in the DOM. In CSS the tree is viewed
364 * top-down and in libdom it is viewed from the bottom-up; as a result
365 * "next" and "previous" are sometimes backwards. This is case-sensitive.
367 * \param pw Pointer to the current SVG parser state
368 * \param node Libdom SVG node
369 * \param qname Name of the sibling node to search for
370 * \param sibling Address at which to store the sibling node pointer
372 * \return Always returns CSS_OK
374 * \post If a suitable element is found, a pointer to it will be
375 * stored at the address pointed to by \a sibling; otherwise,
376 * NULL will be stored at the address pointed to by \a sibling
378 css_error
named_sibling_node(void *pw
, void *node
,
379 const css_qname
*qname
, void **sibling
)
382 dom_node
*n
= node
; /* the current node */
383 dom_node
*prev
; /* the previous node */
388 *sibling
= NULL
; /* default to nothing found */
390 /* Begin the search; the first iteration we do outside of the
391 * loop. Implementation detil: dom_node_get_previous_sibling()
392 * increments the reference counter on the returned node. A
393 * comment within named_parent_node() explains why we
394 * decrement it ASAP. */
395 err
= dom_node_get_previous_sibling(n
, &n
);
396 if (err
!= DOM_NO_ERR
) {
401 /* We're looking for the first ELEMENT sibling */
402 err
= dom_node_get_node_type(n
, &type
);
403 if (err
!= DOM_NO_ERR
) {
408 if (type
== DOM_ELEMENT_NODE
) {
409 /* We found an element node, does it have the
411 err
= dom_node_get_node_name(n
, &name
);
412 if (err
!= DOM_NO_ERR
) {
417 if (dom_string_lwc_isequal(name
,
419 /* The name is right, return it */
423 /* There's only one next-sibling element node
424 * and we've already found it, so if its name
425 * wasn't right, we return the default value
427 dom_string_unref(name
);
432 /* Not an element node, so we move on the the previous
433 * previous sibling */
434 err
= dom_node_get_previous_sibling(n
, &prev
);
435 if (err
!= DOM_NO_ERR
) {
449 * Find the first "subsequent-sibling" of the given element having the
452 * This search corresponds to the "~ foo" combinator in CSS and will
453 * find only "foo" element nodes that precede the given node (under
454 * the same parent) in the DOM. In CSS the tree is viewed top-down and
455 * in libdom it is viewed from the bottom-up; as a result "next" and
456 * "previous" are sometimes backwards. This is case-sensitive.
458 * \param pw Pointer to the current SVG parser state
459 * \param node Libdom SVG node
460 * \param qname Name of the sibling node to search for
461 * \param sibling Address at which to store the sibling node pointer
463 * \return Always returns CSS_OK
465 * \post If a suitable element is found, a pointer to it will be
466 * stored at the address pointed to by \a sibling; otherwise,
467 * NULL will be stored at the address pointed to by \a sibling
469 css_error
named_generic_sibling_node(void *pw
, void *node
,
470 const css_qname
*qname
, void **sibling
)
473 dom_node
*n
= node
; /* the current node */
474 dom_node
*prev
; /* the previous node */
480 *sibling
= NULL
; /* default to nothing found */
482 /* Begin the search; the first iteration we do outside of the
483 * loop. Implementation detil: dom_node_get_previous_sibling()
484 * increments the reference counter on the returned node. A
485 * comment within named_parent_node() explains why we
486 * decrement it ASAP. */
487 err
= dom_node_get_previous_sibling(n
, &n
);
488 if (err
!= DOM_NO_ERR
) {
493 err
= dom_node_get_node_type(n
, &type
);
494 if (err
!= DOM_NO_ERR
) {
499 if (type
== DOM_ELEMENT_NODE
) {
500 /* We only want ELEMENT nodes */
501 err
= dom_node_get_node_name(n
, &name
);
502 if (err
!= DOM_NO_ERR
) {
507 if (dom_string_lwc_isequal(name
,
509 /* Found one. Save it and stop the search */
510 dom_string_unref(name
);
516 dom_string_unref(name
);
519 /* This sibling wasn't an element with the desired
520 name, so move on to the previous sibling */
521 err
= dom_node_get_previous_sibling(n
, &prev
);
522 if (err
!= DOM_NO_ERR
) {
536 * Return a pointer to the given node's parent
538 * \param pw Pointer to the current SVG parser state
539 * \param node Libdom SVG node
540 * \param parent Address at which to store the node's parent pointer
542 * \return Always returns CSS_OK
544 css_error
parent_node(void *pw
, void *node
, void **parent
)
547 /* Libdom basically implements this for us */
548 dom_element_parent_node(node
, (struct dom_element
**)parent
);
550 /* See the comment in named_parent_node() for why we decrement
551 * this reference counter here. */
552 dom_node_unref(*parent
);
559 * Find the "next-sibling" of the given element
561 * This search corresponds "+ *" in CSS and will find the first
562 * element node that immediately precedes the given node under the
563 * same parent in the DOM. In CSS the tree is viewed top-down and in
564 * libdom it is viewed from the bottom-up; as a result "next" and
565 * "previous" are sometimes backwards.
567 * \param pw Pointer to the current SVG parser state
568 * \param node Libdom SVG node
569 * \param sibling Address at which to store the sibling node pointer
571 * \return Always returns CSS_OK
573 * \post If a suitable element is found, a pointer to it will be
574 * stored at the address pointed to by \a sibling; otherwise,
575 * NULL will be stored at the address pointed to by \a sibling
577 css_error
sibling_node(void *pw
, void *node
, void **sibling
)
580 dom_node
*n
= node
; /* the current node */
581 dom_node
*prev
; /* the previous node */
585 *sibling
= NULL
; /* default to nothing found */
587 /* Begin the search; the first iteration we do outside of the
588 * loop. Implementation detil: dom_node_get_previous_sibling()
589 * increments the reference counter on the returned node. A
590 * comment within named_parent_node() explains why we
591 * decrement it ASAP. */
592 err
= dom_node_get_previous_sibling(n
, &n
);
593 if (err
!= DOM_NO_ERR
) {
598 err
= dom_node_get_node_type(n
, &type
);
599 if (err
!= DOM_NO_ERR
) {
604 if (type
== DOM_ELEMENT_NODE
) {
605 /* We found a sibling node that is also an
606 element and that's all we wanted. */
612 /* This sibling node was not an element; move on to
613 the previous sibling */
614 err
= dom_node_get_previous_sibling(n
, &prev
);
615 if (err
!= DOM_NO_ERR
) {
629 * Test the given node for the given name
631 * This will return true (via the "match" pointer) if the libdom node
632 * has the given name or if that name is the universal selector;
633 * otherwise it returns false. The comparison is case-sensitive. It
634 * corresponds to a rule like "body { ... }" in CSS.
636 * \param pw Pointer to the current SVG parser state
637 * \param node Libdom SVG node to test
638 * \param qname Name to check for
639 * \param match Pointer to the test result
641 * \return Always returns CSS_OK
643 css_error
node_has_name(void *pw
, void *node
,
644 const css_qname
*qname
, bool *match
)
646 struct svgtiny_parse_state
*state
;
650 /* Start by checking to see if qname is the universal selector */
651 state
= (struct svgtiny_parse_state
*)pw
;
652 if (lwc_string_isequal(qname
->name
,
653 state
->interned_universal
, match
) == lwc_error_ok
) {
655 /* It's the universal selector. In NetSurf, all node
656 * names match the universal selector, and nothing in
657 * the libcss documentation suggests another approach,
658 * so we follow NetSurf here. */
663 err
= dom_node_get_node_name((dom_node
*)node
, &name
);
664 if (err
!= DOM_NO_ERR
) {
668 /* Unlike with HTML, SVG element names are case-sensitive */
669 *match
= dom_string_lwc_isequal(name
, qname
->name
);
670 dom_string_unref(name
);
677 * Test the given node for the given class
679 * This will return true (via the "match" pointer) if the libdom node
680 * has the given class. The comparison is case-sensitive. It
681 * corresponds to node.class in CSS.
683 * \param pw Pointer to the current SVG parser state
684 * \param node Libdom SVG node to test
685 * \param name Class name to check for
686 * \param match Pointer to the test result
688 * \return Always returns CSS_OK
690 css_error
node_has_class(void *pw
, void *node
,
691 lwc_string
*name
, bool *match
)
694 /* libdom implements this for us and apparently it cannot fail */
695 dom_element_has_class((dom_node
*)node
, name
, match
);
701 * Test the given node for the given id
703 * This will return true (via the "match" pointer) if the libdom node
704 * has the given id. The comparison is case-sensitive. It corresponds
707 * \param pw Pointer to the current SVG parser state
708 * \param node Libdom SVG node to test
709 * \param name Id to check for
710 * \param match Pointer to the test result
712 * \return Always returns CSS_OK
714 css_error
node_has_id(void *pw
, void *node
,
715 lwc_string
*name
, bool *match
)
719 struct svgtiny_parse_state
*state
;
721 attr
= NULL
; /* a priori the "id" attribute may not exist */
722 *match
= false; /* default to no match */
724 state
= (struct svgtiny_parse_state
*)pw
;
725 err
= dom_element_get_attribute((dom_node
*)node
,
726 state
->interned_id
, &attr
);
727 if (err
!= DOM_NO_ERR
|| attr
== NULL
) {
731 *match
= dom_string_lwc_isequal(attr
, name
);
732 dom_string_unref(attr
);
739 * Test the given node for the given attribute
741 * This will return true (via the "match" pointer) if the libdom node
742 * has an attribute with the given name. The comparison is
743 * case-sensitive. It corresponds to node[attr] in CSS.
745 * \param pw Pointer to the current SVG parser state
746 * \param node Libdom SVG node to test
747 * \param qname Attribute name to check for
748 * \param match Pointer to the test result
750 * \return Returns CSS_OK if successful and CSS_NOMEM if anything
753 css_error
node_has_attribute(void *pw
, void *node
,
754 const css_qname
*qname
, bool *match
)
760 /* intern the attribute name as a dom_string so we can
761 * delegate to dom_element_has_attribute() */
762 err
= dom_string_create_interned(
763 (const uint8_t *) lwc_string_data(qname
->name
),
764 lwc_string_length(qname
->name
),
766 if (err
!= DOM_NO_ERR
) {
770 err
= dom_element_has_attribute((dom_node
*)node
, name
, match
);
771 if (err
!= DOM_NO_ERR
) {
772 dom_string_unref(name
);
776 dom_string_unref(name
);
782 * Test the given node for an attribute with a specific value
784 * This will return true (via the "match" pointer) if the libdom node
785 * has an attribute with the given name and value. The comparison is
786 * case-sensitive. It corresponds to node[attr=value] in CSS.
788 * \param pw Pointer to the current SVG parser state
789 * \param node Libdom SVG node to test
790 * \param qname Attribute name to check for
791 * \param value Attribute value to check for
792 * \param match Pointer to the test result
794 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
795 * intern the attribute name (which usually indicates memory
798 css_error
node_has_attribute_equal(void *pw
, void *node
,
799 const css_qname
*qname
, lwc_string
*value
,
802 /* Implementation note: NetSurf always returns "no match" when
803 * the value is empty (length zero). We allow it, because why
808 dom_string
*attr_val
;
811 /* Intern the attribute name as a dom_string so we can
812 * use dom_element_get_attribute() */
813 err
= dom_string_create_interned(
814 (const uint8_t *) lwc_string_data(qname
->name
),
815 lwc_string_length(qname
->name
),
817 if (err
!= DOM_NO_ERR
) {
821 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
822 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
823 /* There was an error getting the attribute's value or
824 * the attribute doesn't exist. So, no match? */
825 dom_string_unref(name
);
830 /* Otherwise, we have the attribute value from the given node
831 * and all we need to do is compare. */
832 dom_string_unref(name
);
833 *match
= dom_string_lwc_isequal(attr_val
, value
);
834 dom_string_unref(attr_val
);
841 * Test the given node for an attribute with a specific value,
842 * possibly followed by a single hyphen
844 * This will return true (via the "match" pointer) if the libdom node
845 * has an attribute with the given name and value or with the given
846 * name and a value that is followed by exactly one hyphen. The
847 * comparison is case-sensitive. This corresponds to [attr|=value]
850 * \param pw Pointer to the current SVG parser state
851 * \param node Libdom SVG node to test
852 * \param qname Attribute name to check for
853 * \param value Attribute value to check for
854 * \param match Pointer to the test result
856 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
857 * intern the attribute name (which usually indicates memory
860 css_error
node_has_attribute_dashmatch(void *pw
, void *node
,
861 const css_qname
*qname
, lwc_string
*value
,
864 /* Implementation note: NetSurf always returns "no match" when
865 * the value is empty (length zero). We allow it, because why
870 dom_string
*attr_val
;
873 const char *vdata
; /* to hold the data underlying "value" */
875 const char *avdata
; /* to hold the found attribute value data */
878 /* Intern the attribute name as a dom_string so we can
879 * use dom_element_get_attribute() */
880 err
= dom_string_create_interned(
881 (const uint8_t *) lwc_string_data(qname
->name
),
882 lwc_string_length(qname
->name
),
884 if (err
!= DOM_NO_ERR
) {
888 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
889 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
890 /* There was an error getting the attribute's value or
891 * the attribute doesn't exist. So, no match? */
892 dom_string_unref(name
);
897 /* Otherwise, we have the attribute value from the given node
898 * and all we need to do is compare. */
899 dom_string_unref(name
);
900 *match
= dom_string_lwc_isequal(attr_val
, value
);
902 /* Exact match, we're done */
903 dom_string_unref(attr_val
);
907 /* No exact match, try it with a hyphen on the end */
908 vdata
= lwc_string_data(value
); /* needle */
909 vdata_len
= lwc_string_length(value
);
910 avdata
= dom_string_data(attr_val
); /* haystack */
911 avdata_len
= dom_string_byte_length(attr_val
);
912 dom_string_unref(attr_val
);
914 if (avdata_len
> vdata_len
&& avdata
[vdata_len
] == '-') {
915 if (strncasecmp(avdata
, vdata
, vdata_len
) == 0) {
916 /* If there's a hyphen in the right position,
917 * it suffices to compare the strings only up
928 * Test the given node for an attribute whose value is a
929 * space-separated list of words, one of which is the given word
931 * This will return true (via the "match" pointer) if the libdom node
932 * has an attribute with the given name and whose value when
933 * considered as a space-separated list of words contains the given
934 * word. The comparison is case-sensitive. This corresponds to
935 * [attr~=value] in CSS.
937 * \param pw Pointer to the current SVG parser state
938 * \param node Libdom SVG node to test
939 * \param qname Attribute name to check for
940 * \param word Value word to check for
941 * \param match Pointer to the test result
943 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
944 * intern the attribute name (which usually indicates memory
947 css_error
node_has_attribute_includes(void *pw
, void *node
,
948 const css_qname
*qname
, lwc_string
*word
,
954 dom_string
*attr_val
;
956 size_t wordlen
; /* length of "word" */
958 /* pointers used to parse a space-separated list of words */
963 *match
= false; /* default to no match */
965 wordlen
= lwc_string_length(word
);
967 /* In this case, the spec says that "if 'val' is the
968 * empty string, it will never represent anything." */
972 /* Intern the attribute name as a dom_string so we can
973 * use dom_element_get_attribute() */
974 err
= dom_string_create_interned(
975 (const uint8_t *) lwc_string_data(qname
->name
),
976 lwc_string_length(qname
->name
),
978 if (err
!= DOM_NO_ERR
) {
982 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
983 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
984 /* There was an error getting the attribute's value or
985 * the attribute doesn't exist. So, no match? */
986 dom_string_unref(name
);
990 /* Parse the list comparing each word against "word" */
991 start
= dom_string_data(attr_val
);
992 end
= start
+ dom_string_byte_length(attr_val
);
993 dom_string_unref(attr_val
);
995 for (p
= start
; p
<= end
; p
++) {
996 /* Move forward until we find the end of the first word */
997 if (*p
== ' ' || *p
== '\0') {
998 /* If the length of that word is the length of the
999 * word we're looking for, do the comparison. */
1000 if ((size_t) (p
- start
) == wordlen
&&
1002 lwc_string_data(word
),
1007 /* No match? Set "start" to the beginning of
1008 * the next word and loop. */
1018 * Test the given node for an attribute whose value begins with the
1021 * This will return true (via the "match" pointer) if the libdom node
1022 * has an attribute with the given name and whose value begins with
1023 * the given prefix string. The comparison is case-sensitive. This
1024 * corresponds to [attr^=value] in CSS.
1026 * \param pw Pointer to the current SVG parser state
1027 * \param node Libdom SVG node to test
1028 * \param qname Attribute name to check for
1029 * \param prefix Value prefix to check for
1030 * \param match Pointer to the test result
1032 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1033 * intern the attribute name (which usually indicates memory
1036 css_error
node_has_attribute_prefix(void *pw
, void *node
,
1037 const css_qname
*qname
, lwc_string
*prefix
,
1042 dom_string
*attr_val
;
1044 const char *avdata
; /* attribute value data */
1045 size_t avdata_len
; /* length of that attribute value data */
1046 size_t prefixlen
; /* length of "prefix" */
1048 prefixlen
= lwc_string_length(prefix
);
1049 if (prefixlen
== 0) {
1050 /* In this case, the spec says that "if 'val' is the
1051 * empty string, it will never represent anything." */
1055 /* Intern the attribute name as a dom_string so we can
1056 * use dom_element_get_attribute() */
1057 err
= dom_string_create_interned(
1058 (const uint8_t *) lwc_string_data(qname
->name
),
1059 lwc_string_length(qname
->name
),
1061 if (err
!= DOM_NO_ERR
) {
1065 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
1066 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
1067 /* There was an error getting the attribute's value or
1068 * the attribute doesn't exist. So, no match? */
1069 dom_string_unref(name
);
1074 /* Otherwise, we have the attribute value from the given node,
1075 * and the first thing we want to do is check to see if the
1076 * whole thing matches the prefix. */
1077 dom_string_unref(name
);
1078 *match
= dom_string_lwc_isequal(attr_val
, prefix
);
1080 /* If not, check to see if an, uh, prefix matches the
1082 if (*match
== false) {
1083 avdata
= dom_string_data(attr_val
);
1084 avdata_len
= dom_string_byte_length(attr_val
);
1085 if ((avdata_len
>= prefixlen
) &&
1086 (strncasecmp(avdata
,
1087 lwc_string_data(prefix
),
1089 /* Use strncasecmp to compare only the first
1090 * "n" characters, where "n" is the length of
1096 dom_string_unref(attr_val
);
1103 * Test the given node for an attribute whose value end with the
1106 * This will return true (via the "match" pointer) if the libdom node
1107 * has an attribute with the given name and whose value ends with
1108 * the given suffix string. The comparison is case-sensitive. This
1109 * corresponds to [attr$=value] in CSS.
1111 * \param pw Pointer to the current SVG parser state
1112 * \param node Libdom SVG node to test
1113 * \param qname Attribute name to check for
1114 * \param suffix Value suffix to check for
1115 * \param match Pointer to the test result
1117 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1118 * intern the attribute name (which usually indicates memory
1121 css_error
node_has_attribute_suffix(void *pw
, void *node
,
1122 const css_qname
*qname
, lwc_string
*suffix
,
1127 dom_string
*attr_val
;
1129 const char *avdata
; /* attribute value data */
1130 size_t avdata_len
; /* length of that attribute value data */
1131 size_t suffixlen
; /* length of "suffix" */
1133 /* convenience pointer we'll use when matching the suffix */
1134 const char *suffix_start
;
1136 suffixlen
= lwc_string_length(suffix
);
1137 if (suffixlen
== 0) {
1138 /* In this case, the spec says that "if 'val' is the
1139 * empty string, it will never represent anything." */
1143 /* Intern the attribute name as a dom_string so we can
1144 * use dom_element_get_attribute() */
1145 err
= dom_string_create_interned(
1146 (const uint8_t *) lwc_string_data(qname
->name
),
1147 lwc_string_length(qname
->name
),
1149 if (err
!= DOM_NO_ERR
) {
1153 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
1154 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
1155 /* There was an error getting the attribute's value or
1156 * the attribute doesn't exist. So, no match? */
1157 dom_string_unref(name
);
1162 /* Otherwise, we have the attribute value from the given node,
1163 * and the first thing we want to do is check to see if the
1164 * whole thing matches the suffix. */
1165 dom_string_unref(name
);
1166 *match
= dom_string_lwc_isequal(attr_val
, suffix
);
1168 /* If not, check to see if an, uh, suffix matches the
1170 if (*match
== false) {
1171 avdata
= dom_string_data(attr_val
);
1172 avdata_len
= dom_string_byte_length(attr_val
);
1174 suffix_start
= (char *)(avdata
+ avdata_len
- suffixlen
);
1176 if ((avdata_len
>= suffixlen
) &&
1177 (strncasecmp(suffix_start
,
1178 lwc_string_data(suffix
),
1180 /* Use strncasecmp to compare only the last
1181 * "n" characters, where "n" is the length of
1187 dom_string_unref(attr_val
);
1194 * Implement node_has_attribute_substring() with optional case-
1195 * insensitivity. This corresponds to [attr*=value i] in CSS and is
1196 * not supported by libcss yet, but it allows us to factor out some
1199 static css_error
_node_has_attribute_substring(void *pw
, void *node
,
1200 const css_qname
*qname
, lwc_string
*substring
,
1201 bool *match
, bool insensitive
)
1205 dom_string
*attr_val
;
1207 size_t attr_len
; /* length of attr_val */
1208 size_t substrlen
; /* length of "substring" */
1210 /* Convenience pointers we use when comparing substrings */
1214 substrlen
= lwc_string_length(substring
);
1215 if (substrlen
== 0) {
1216 /* In this case, the spec says that "if 'val' is the
1217 * empty string, it will never represent anything." */
1221 /* Intern the attribute name as a dom_string so we can
1222 * use dom_element_get_attribute() */
1223 err
= dom_string_create_interned(
1224 (const uint8_t *) lwc_string_data(qname
->name
),
1225 lwc_string_length(qname
->name
),
1227 if (err
!= DOM_NO_ERR
) {
1231 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
1232 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
1233 /* There was an error getting the attribute's value or
1234 * the attribute doesn't exist. So, no match? */
1235 dom_string_unref(name
);
1240 /* Otherwise, we have the attribute value from the given node,
1241 * and the first thing we want to do is check to see if the
1242 * whole thing matches the substring. */
1243 dom_string_unref(name
);
1246 *match
= dom_string_caseless_lwc_isequal(attr_val
, substring
);
1249 *match
= dom_string_lwc_isequal(attr_val
, substring
);
1252 /* If not, check to see if an, uh, substring matches the
1254 if (*match
== false) {
1255 p
= dom_string_data(attr_val
);
1257 /* Check every long-enough suffix for a prefix match */
1258 attr_len
= dom_string_byte_length(attr_val
);
1259 if (attr_len
>= substrlen
) {
1260 p_max
= p
+ attr_len
- substrlen
;
1261 while (p
<= p_max
) {
1263 lwc_string_data(substring
),
1273 dom_string_unref(attr_val
);
1279 * Test the given node for an attribute whose value contains the
1282 * This will return true (via the "match" pointer) if the libdom node
1283 * has an attribute with the given name and whose value contains the
1284 * given substring. The comparison is case-sensitive. This corresponds
1285 * to [attr*=value] in CSS.
1287 * \param pw Pointer to the current SVG parser state
1288 * \param node Libdom SVG node to test
1289 * \param qname Attribute name to check for
1290 * \param substring Value substring to check for
1291 * \param match Pointer to the test result
1293 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1294 * intern the attribute name (which usually indicates memory
1297 css_error
node_has_attribute_substring(void *pw
, void *node
,
1298 const css_qname
*qname
, lwc_string
*substring
,
1301 return _node_has_attribute_substring(pw
, node
, qname
, substring
,
1307 * Test whether or not the given node is the document's root element
1308 * This corresponds to the CSS :root pseudo-selector.
1310 * \param pw Pointer to the current SVG parser state
1311 * \param node Libdom SVG node to test
1312 * \param match Pointer to the test result
1314 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1316 css_error
node_is_root(void *pw
, void *node
, bool *match
)
1323 err
= dom_node_get_parent_node((dom_node
*)node
, &parent
);
1324 if (err
!= DOM_NO_ERR
) {
1328 /* It's the root element if it doesn't have a parent element */
1329 if (parent
!= NULL
) {
1330 err
= dom_node_get_node_type(parent
, &type
);
1331 dom_node_unref(parent
);
1332 if (err
!= DOM_NO_ERR
) {
1335 if (type
!= DOM_DOCUMENT_NODE
) {
1336 /* DOM_DOCUMENT_NODE is the only allowable
1337 * type of parent node for the root element */
1349 * Used internally in node_count_siblings() to "count" the given
1350 * sibling node. It factors out the node type and name checks.
1352 static int node_count_siblings_check(dom_node
*dnode
,
1359 dom_string
*dnode_name
;
1361 /* We flip this to 1 if/when we count this node */
1364 if (dnode
== NULL
) {
1368 exc
= dom_node_get_node_type(dnode
, &type
);
1369 if ((exc
!= DOM_NO_ERR
) || (type
!= DOM_ELEMENT_NODE
)) {
1370 /* We only count element siblings */
1374 /* ... with the right name */
1377 exc
= dom_node_get_node_name(dnode
, &dnode_name
);
1379 if ((exc
== DOM_NO_ERR
) && (dnode_name
!= NULL
)) {
1380 if (dom_string_isequal(name
,
1384 dom_string_unref(dnode_name
);
1395 * Count the given node's sibling elements
1397 * This counts the given node's sibling elements in one direction,
1398 * either forwards or backwards, in the DOM. Keep in mind that the
1399 * libdom tree is upside-down compared to the CSS one; so "next" and
1400 * "previous" are actually reversed; the default is to count preceding
1401 * libdom siblings which correspond to subsequent CSS siblings.
1403 * This operation is central to the CSS :first-child, :nth-child, and
1404 * :last-child (et cetera) pseudo-selectors.
1406 * If same_name is true, then only nodes having the same
1407 * (case-sensitive) name as the given node are counted.
1409 * \param pw Pointer to the current SVG parser state
1410 * \param node Libdom SVG node whose siblings we're counting
1411 * \param same_name Whether or not to count only siblings having
1412 * the same name as the given node
1413 * \param after Count subsequent siblings rather than precedent
1414 * ones (the default)
1415 * \param count Pointer to the return value, the number of sibling
1418 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1420 css_error
node_count_siblings(void *pw
, void *node
,
1421 bool same_name
, bool after
, int32_t *count
)
1425 dom_node
*dnode
; /* node, but with the right type */
1426 dom_string
*dnode_name
;
1427 dom_node
*next
; /* "next" sibling (depends on direction) */
1429 /* Pointer to the "next sibling" function */
1430 dom_exception (*next_func
)(dom_node
*, dom_node
**);
1435 dnode
= (dom_node
*)node
;
1437 exc
= dom_node_get_node_name(dnode
, &dnode_name
);
1438 if ((exc
!= DOM_NO_ERR
) || (dnode_name
== NULL
)) {
1443 /* Increment the reference counter for dnode for as long as
1444 * we retain a reference to it. */
1445 dnode
= dom_node_ref(dnode
);
1447 next_func
= dom_node_get_previous_sibling
;
1449 next_func
= dom_node_get_next_sibling
;
1453 exc
= next_func(dnode
, &next
);
1454 if (exc
!= DOM_NO_ERR
) {
1458 /* If next_func worked, we're about to swap "next"
1459 * with "dnode" meaning that we will no longer retain
1460 * a reference to the current dnode. */
1461 dom_node_unref(dnode
);
1464 *count
+= node_count_siblings_check(dnode
,
1467 } while (dnode
!= NULL
);
1469 if (dnode_name
!= NULL
) {
1470 dom_string_unref(dnode_name
);
1478 * Determine whether or not the given element is empty
1480 * An element is "nonempty" if it has a child that is either an
1481 * element node or a text node.
1483 * \param pw Pointer to the current SVG parser state
1484 * \param node Libdom SVG node to check for emptiness
1485 * \param is_empty Pointer to the return value
1487 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1489 css_error
node_is_empty(void *pw
, void *node
, bool *is_empty
)
1492 dom_node
*child
; /* current child node pointer */
1493 dom_node
*next
; /* next child node pointer */
1494 dom_node_type type
; /* what type of node is "child" */
1497 /* Assume that it's empty by default */
1500 /* Get the given node's first child. Implementation detail:
1501 * this increments the reference counter on the child node. */
1502 err
= dom_node_get_first_child((dom_node
*)node
, &child
);
1503 if (err
!= DOM_NO_ERR
) {
1507 /* And now loop through all children looking for a
1508 * text/element node. If we find one, the original
1509 * node is "nonempty" */
1510 while (child
!= NULL
) {
1511 err
= dom_node_get_node_type(child
, &type
);
1512 if (err
!= DOM_NO_ERR
) {
1513 dom_node_unref(child
);
1517 if (type
== DOM_ELEMENT_NODE
|| type
== DOM_TEXT_NODE
) {
1519 dom_node_unref(child
);
1523 err
= dom_node_get_next_sibling(child
, &next
);
1524 if (err
!= DOM_NO_ERR
) {
1525 dom_node_unref(child
);
1529 /* If we're moving to the next node, we can release
1530 * the reference to the current one */
1531 dom_node_unref(child
);
1540 * Determine whether or not the given node is a link
1542 * A node is a link if it is an element node whose name is "a" and if
1543 * it has an "href" attribute (case-sensitive). This selector
1544 * corresponds to node:link pseudo-class in CSS.
1546 * This pseudo-class is a bit awkward because the two standards (HTML5
1547 * and CSS) disagree on what it means, and because libsvgtiny does not
1548 * have enough information to determine if a link has been "visited"
1549 * yet -- that's a UI property. CSS says that :link is for unvisited
1550 * links, which we can't determine. HTML5 says that each link must
1551 * be either a :link or :visited. Since we can't decide either way,
1552 * It seems less wrong to declare that all links are unvisited; i.e.
1553 * that they match :link.
1555 * \param pw Pointer to the current SVG parser state
1556 * \param node Libdom SVG node to check
1557 * \param is_link Pointer to the boolean return value
1559 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1561 css_error
node_is_link(void *pw
, void *node
, bool *is_link
)
1564 dom_node
*dnode
; /* node, but with the right type */
1565 dom_string
*dnode_name
;
1567 struct svgtiny_parse_state
* state
;
1569 dnode
= (dom_node
*)node
;
1571 has_href
= false; /* assume no href attribute */
1572 *is_link
= false; /* assume that it's not a link */
1574 exc
= dom_node_get_node_name(dnode
, &dnode_name
);
1575 if ((exc
!= DOM_NO_ERR
) || (dnode_name
== NULL
)) {
1579 state
= (struct svgtiny_parse_state
*)pw
;
1580 if (dom_string_isequal(dnode_name
, state
->interned_a
)) {
1581 exc
= dom_element_has_attribute(node
,
1582 state
->interned_href
,
1584 if (exc
== DOM_NO_ERR
&& has_href
) {
1589 dom_string_unref(dnode_name
);
1594 * Check if the given node is a link that has been visited already
1596 * This check always fails because the SVG DOM does not have the
1597 * necessary information (it's a UI property).
1599 * \param pw Pointer to the current SVG parser state; unused
1600 * \param node Libdom SVG node to check; unused
1601 * \param is_visited Pointer to the boolean return value
1603 * \return Always returns CSS_OK
1605 css_error
node_is_visited(void *pw
, void *node
, bool *is_visited
)
1609 *is_visited
= false;
1615 * Check if the given node is being "hovered" over
1617 * This check always fails because the SVG DOM does not have the
1618 * necessary information (it's a UI property).
1620 * \param pw Pointer to the current SVG parser state; unused
1621 * \param node Libdom SVG node to check; unused
1622 * \param is_hover Pointer to the boolean return value
1624 * \return Always returns CSS_OK
1626 css_error
node_is_hover(void *pw
, void *node
, bool *is_hover
)
1636 * Check if the given node is "active"
1638 * This check always fails because the SVG DOM does not have the
1639 * necessary information (it's a UI property).
1641 * \param pw Pointer to the current SVG parser state; unused
1642 * \param node Libdom SVG node to check; unused
1643 * \param is_active Pointer to the boolean return value
1645 * \return Always returns CSS_OK
1647 css_error
node_is_active(void *pw
, void *node
, bool *is_active
)
1657 * Check if the given node has the focus
1659 * This check always fails because the SVG DOM does not have the
1660 * necessary information (it's a UI property).
1662 * \param pw Pointer to the current SVG parser state; unused
1663 * \param node Libdom SVG node to check; unused
1664 * \param is_focus Pointer to the boolean return value
1666 * \return Always returns CSS_OK
1668 css_error
node_is_focus(void *pw
, void *node
, bool *is_focus
)
1678 * Check if the given node is enabled
1680 * This check always fails because the SVG DOM does not have the
1681 * necessary information (it's a UI property).
1683 * \param pw Pointer to the current SVG parser state; unused
1684 * \param node Libdom SVG node to check; unused
1685 * \param is_enabled Pointer to the boolean return value
1687 * \return Always returns CSS_OK
1689 css_error
node_is_enabled(void *pw
, void *node
, bool *is_enabled
)
1693 *is_enabled
= false;
1699 * Check if the given node is disabled
1701 * This check always fails because the SVG DOM does not have the
1702 * necessary information (it's a UI property). Beware, until they are
1703 * implemented, this is NOT the logical negation of node_is_enabled!
1705 * \param pw Pointer to the current SVG parser state; unused
1706 * \param node Libdom SVG node to check; unused
1707 * \param is_disabled Pointer to the boolean return value
1709 * \return Always returns CSS_OK
1711 css_error
node_is_disabled(void *pw
, void *node
, bool *is_disabled
)
1715 *is_disabled
= false;
1721 * Test whether or not the given node is "checked"
1723 * This test always fails because the SVG DOM does not have the
1724 * necessary information (it's a UI property).
1726 * \param pw Pointer to the current SVG parser state; unused
1727 * \param node Libdom SVG node to check; unused
1728 * \param is_checked Pointer to the boolean return value
1730 * \return Always returns CSS_OK
1732 css_error
node_is_checked(void *pw
, void *node
, bool *is_checked
)
1736 *is_checked
= false;
1742 * Check if the given node is the "target" of the document URL
1744 * This test always fails because the SVG DOM does not have the
1745 * necessary information (it's a UI property).
1747 * \param pw Pointer to the current SVG parser state; unused
1748 * \param node Libdom SVG node to check; unused
1749 * \param is_target Pointer to the boolean return value
1751 * \return Always returns CSS_OK
1753 css_error
node_is_target(void *pw
, void *node
, bool *is_target
)
1763 * Check if the given node is the given language
1765 * This test is corresponds to the CSS :lang() selector and is not
1766 * fully implemented yet: it looks only for "lang" attributes on the
1767 * given element and its parents, and performs a simple substring
1768 * check. This results in a partial implementation of CSS Level 3 for
1769 * SVG 2.0. In particular, it ignores all "xml:lang" attributes in
1770 * favor of the "lang" attribute that is defined only in SVG 2.0.
1772 * \param pw Pointer to the current SVG parser state; unused
1773 * \param node Libdom SVG node to check
1774 * \param lang The language to match
1775 * \param is_lang Pointer to the boolean return value
1777 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1779 static css_error
node_is_lang(void *pw
, void *node
,
1780 lwc_string
*lang
, bool *is_lang
)
1783 /* SVG2 elements support both "lang" and "xml:lang"
1784 * attributes; earlier versions have only the XML
1785 * attribute. It would not be too hard to add support for
1786 * xml:lang" here. The main difficulty standing in the way of
1787 * a full Level 4 implementation is the complexity of the
1790 * https://www.w3.org/TR/selectors-4/#the-lang-pseudo
1795 dom_exception d_err
;
1796 dom_node
*n
; /* current node */
1797 dom_node
*p
; /* parent node */
1798 bool match
; /* retval from node_has_attribute_substring() */
1800 /* Define the attribute name "lang" that we're looking for.
1801 * We only use a css_qname here because that's what the
1802 * node_has_attribute_substring() takes; the namespace
1803 * portion of it is irrelevant. */
1807 if (lwc_intern_string("lang", 4, &attr
.name
) != lwc_error_ok
) {
1811 *is_lang
= false; /* default to no match */
1812 n
= (dom_node
*)node
;
1814 /* Loop through all parents of the given node looking for a
1815 * substring match */
1817 c_err
= _node_has_attribute_substring(pw
, (void *)n
, &attr
,
1818 lang
, &match
, true);
1819 if (c_err
!= CSS_OK
) {
1820 lwc_string_destroy(attr
.name
);
1824 /* matched this element; we're done */
1825 lwc_string_destroy(attr
.name
);
1830 /* no match on this element, try its parent */
1831 d_err
= dom_node_get_parent_node(n
, &p
);
1832 if (d_err
!= DOM_NO_ERR
) {
1833 lwc_string_destroy(attr
.name
);
1839 /* If we never find a match we may wind up here */
1840 lwc_string_destroy(attr
.name
);
1846 * Return presentational hints for the given node
1848 * Unless an SVG is being rendered from within an HTML document,
1849 * there are no presentational hints. We always return an empty
1852 * \param pw Pointer to the current SVG parser state; unused
1853 * \param node Libdom SVG node whose hints we want; unused
1854 * \param nhints How many hints are returned (return by reference)
1855 * \param hints Array of css_hint structures (return by reference)
1857 * \return Always returns CSS_OK
1859 css_error
node_presentational_hint(void *pw
, void *node
,
1860 uint32_t *nhints
, css_hint
**hints
)
1871 * User-agent defaults for CSS properties
1873 * For the moment, we provide no defaults, because libsvgtiny does not
1874 * yet support any CSS properties that might need them.
1876 * \param pw Pointer to the current SVG parser state; unused
1877 * \param property LibCSS property identifier; unused
1878 * \param hint Pointer to hint object (a return value); unused
1880 * \return Always returns CSS_INVALID
1882 css_error
ua_default_for_property(void *pw
, uint32_t property
,
1893 * A "user handler" function that we pass to dom_node_set_user_data()
1894 * in set_libcss_node_data(). The set_libcss_node_data() documentation
1895 * says that if the node (on which data is set) is is deleted or
1896 * cloned, or if its ancestors are modified, then we must call
1897 * css_libcss_node_data_handler() on the user data. This function
1898 * basically just checks to see which DOM event has happened and
1899 * calls css_libcss_node_data_handler() when any of those criteria
1902 static void svgtiny_dom_user_data_handler(dom_node_operation operation
,
1903 dom_string
*key
, void *data
, struct dom_node
*src
,
1904 struct dom_node
*dst
)
1906 /* We only care about the userdata that is identified by our
1907 * userdata key. Unfortunately I see no obvious way to obtain
1908 * the copy of the userdata key that is already interned in
1909 * svgtiny_strings.h; so we duplicate it here (ugh). */
1911 dom_string_create((const uint8_t *)"_libcss_user_data", 17, &str
);
1912 if (dom_string_isequal(str
,key
) == false || data
== NULL
) {
1913 /* Wrong key, or no data */
1917 /* Check the DOM operation, and make the corresponding call to
1918 * css_libcss_node_data_handler(). No error handling is done.
1920 switch (operation
) {
1921 case DOM_NODE_CLONED
:
1922 css_libcss_node_data_handler(&svgtiny_select_handler
,
1924 NULL
, src
, dst
, data
);
1926 case DOM_NODE_RENAMED
:
1927 css_libcss_node_data_handler(&svgtiny_select_handler
,
1929 NULL
, src
, NULL
, data
);
1931 case DOM_NODE_IMPORTED
:
1932 case DOM_NODE_ADOPTED
:
1933 case DOM_NODE_DELETED
:
1934 css_libcss_node_data_handler(&svgtiny_select_handler
,
1936 NULL
, src
, NULL
, data
);
1938 /* Our list of cases should have been exhaustive */
1944 * Store libcss data on a node
1946 * This is part of the libcss select handler API that we need to
1947 * implement. It is essentially a thin dom_node_set_user_data()
1950 * \param pw Pointer to the current SVG parser state
1951 * \param node Libdom SVG node on which to store the data
1952 * \param libcss_node_data Pointer to the data to store
1954 * \return Always returns CSS_OK
1956 css_error
set_libcss_node_data(void *pw
, void *node
,
1957 void *libcss_node_data
)
1959 struct svgtiny_parse_state
*state
;
1962 /* A unique "userdata key" (a string) is used to identify this
1964 state
= (struct svgtiny_parse_state
*)pw
;
1965 dom_node_set_user_data((dom_node
*)node
,
1966 state
->interned_userdata_key
,
1968 svgtiny_dom_user_data_handler
,
1971 /* dom_node_set_user_data() always returns DOM_NO_ERR */
1977 * Retrieve libcss data from a node
1979 * This is part of the libcss select handler API that we need to
1980 * implement. It is essentially a thin dom_node_get_user_data()
1983 * \param pw Pointer to the current SVG parser state
1984 * \param node Libdom SVG node from which to get the data
1985 * \param libcss_node_data Address at which to store a pointer to the data
1987 * \return Always returns CSS_OK
1989 css_error
get_libcss_node_data(void *pw
, void *node
,
1990 void **libcss_node_data
)
1992 struct svgtiny_parse_state
*state
;
1994 /* A unique "userdata key" (a string) is used to identify this
1996 state
= (struct svgtiny_parse_state
*)pw
;
1997 dom_node_get_user_data((dom_node
*)node
,
1998 state
->interned_userdata_key
,
2001 /* dom_node_get_user_data() always returns DOM_NO_ERR */
2007 * The vtable of select handler callbacks passed by libsvgtiny to
2008 * css_select_style().
2010 static css_select_handler svgtiny_select_handler
= {
2011 CSS_SELECT_HANDLER_VERSION_1
,
2015 named_ancestor_node
,
2018 named_generic_sibling_node
,
2025 node_has_attribute_equal
,
2026 node_has_attribute_dashmatch
,
2027 node_has_attribute_includes
,
2028 node_has_attribute_prefix
,
2029 node_has_attribute_suffix
,
2030 node_has_attribute_substring
,
2032 node_count_siblings
,
2044 node_presentational_hint
,
2045 ua_default_for_property
,
2046 set_libcss_node_data
,
2047 get_libcss_node_data
,