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 *match
= dom_string_lwc_isequal(state
->interned_universal
, qname
->name
);
654 /* It's the universal selector. In NetSurf, all node
655 * names match the universal selector, and nothing in
656 * the libcss documentation suggests another approach,
657 * so we follow NetSurf here. */
661 err
= dom_node_get_node_name((dom_node
*)node
, &name
);
662 if (err
!= DOM_NO_ERR
) {
666 /* Unlike with HTML, SVG element names are case-sensitive */
667 *match
= dom_string_lwc_isequal(name
, qname
->name
);
668 dom_string_unref(name
);
675 * Test the given node for the given class
677 * This will return true (via the "match" pointer) if the libdom node
678 * has the given class. The comparison is case-sensitive. It
679 * corresponds to node.class in CSS.
681 * \param pw Pointer to the current SVG parser state
682 * \param node Libdom SVG node to test
683 * \param name Class name to check for
684 * \param match Pointer to the test result
686 * \return Always returns CSS_OK
688 css_error
node_has_class(void *pw
, void *node
,
689 lwc_string
*name
, bool *match
)
692 /* libdom implements this for us and apparently it cannot fail */
693 dom_element_has_class((dom_node
*)node
, name
, match
);
699 * Test the given node for the given id
701 * This will return true (via the "match" pointer) if the libdom node
702 * has the given id. The comparison is case-sensitive. It corresponds
705 * \param pw Pointer to the current SVG parser state
706 * \param node Libdom SVG node to test
707 * \param name Id to check for
708 * \param match Pointer to the test result
710 * \return Always returns CSS_OK
712 css_error
node_has_id(void *pw
, void *node
,
713 lwc_string
*name
, bool *match
)
717 struct svgtiny_parse_state
*state
;
719 attr
= NULL
; /* a priori the "id" attribute may not exist */
720 *match
= false; /* default to no match */
722 state
= (struct svgtiny_parse_state
*)pw
;
723 err
= dom_element_get_attribute((dom_node
*)node
,
724 state
->interned_id
, &attr
);
725 if (err
!= DOM_NO_ERR
|| attr
== NULL
) {
729 *match
= dom_string_lwc_isequal(attr
, name
);
730 dom_string_unref(attr
);
737 * Test the given node for the given attribute
739 * This will return true (via the "match" pointer) if the libdom node
740 * has an attribute with the given name. The comparison is
741 * case-sensitive. It corresponds to node[attr] in CSS.
743 * \param pw Pointer to the current SVG parser state
744 * \param node Libdom SVG node to test
745 * \param qname Attribute name to check for
746 * \param match Pointer to the test result
748 * \return Returns CSS_OK if successful and CSS_NOMEM if anything
751 css_error
node_has_attribute(void *pw
, void *node
,
752 const css_qname
*qname
, bool *match
)
758 /* intern the attribute name as a dom_string so we can
759 * delegate to dom_element_has_attribute() */
760 err
= dom_string_create_interned(
761 (const uint8_t *) lwc_string_data(qname
->name
),
762 lwc_string_length(qname
->name
),
764 if (err
!= DOM_NO_ERR
) {
768 err
= dom_element_has_attribute((dom_node
*)node
, name
, match
);
769 if (err
!= DOM_NO_ERR
) {
770 dom_string_unref(name
);
774 dom_string_unref(name
);
780 * Test the given node for an attribute with a specific value
782 * This will return true (via the "match" pointer) if the libdom node
783 * has an attribute with the given name and value. The comparison is
784 * case-sensitive. It corresponds to node[attr=value] in CSS.
786 * \param pw Pointer to the current SVG parser state
787 * \param node Libdom SVG node to test
788 * \param qname Attribute name to check for
789 * \param value Attribute value to check for
790 * \param match Pointer to the test result
792 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
793 * intern the attribute name (which usually indicates memory
796 css_error
node_has_attribute_equal(void *pw
, void *node
,
797 const css_qname
*qname
, lwc_string
*value
,
800 /* Implementation note: NetSurf always returns "no match" when
801 * the value is empty (length zero). We allow it, because why
806 dom_string
*attr_val
;
809 /* Intern the attribute name as a dom_string so we can
810 * use dom_element_get_attribute() */
811 err
= dom_string_create_interned(
812 (const uint8_t *) lwc_string_data(qname
->name
),
813 lwc_string_length(qname
->name
),
815 if (err
!= DOM_NO_ERR
) {
819 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
820 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
821 /* There was an error getting the attribute's value or
822 * the attribute doesn't exist. So, no match? */
823 dom_string_unref(name
);
828 /* Otherwise, we have the attribute value from the given node
829 * and all we need to do is compare. */
830 dom_string_unref(name
);
831 *match
= dom_string_lwc_isequal(attr_val
, value
);
832 dom_string_unref(attr_val
);
839 * Test the given node for an attribute with a specific value,
840 * possibly followed by a single hyphen
842 * This will return true (via the "match" pointer) if the libdom node
843 * has an attribute with the given name and value or with the given
844 * name and a value that is followed by exactly one hyphen. The
845 * comparison is case-sensitive. This corresponds to [attr|=value]
848 * \param pw Pointer to the current SVG parser state
849 * \param node Libdom SVG node to test
850 * \param qname Attribute name to check for
851 * \param value Attribute value to check for
852 * \param match Pointer to the test result
854 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
855 * intern the attribute name (which usually indicates memory
858 css_error
node_has_attribute_dashmatch(void *pw
, void *node
,
859 const css_qname
*qname
, lwc_string
*value
,
862 /* Implementation note: NetSurf always returns "no match" when
863 * the value is empty (length zero). We allow it, because why
868 dom_string
*attr_val
;
871 const char *vdata
; /* to hold the data underlying "value" */
873 const char *avdata
; /* to hold the found attribute value data */
876 /* Intern the attribute name as a dom_string so we can
877 * use dom_element_get_attribute() */
878 err
= dom_string_create_interned(
879 (const uint8_t *) lwc_string_data(qname
->name
),
880 lwc_string_length(qname
->name
),
882 if (err
!= DOM_NO_ERR
) {
886 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
887 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
888 /* There was an error getting the attribute's value or
889 * the attribute doesn't exist. So, no match? */
890 dom_string_unref(name
);
895 /* Otherwise, we have the attribute value from the given node
896 * and all we need to do is compare. */
897 dom_string_unref(name
);
898 *match
= dom_string_lwc_isequal(attr_val
, value
);
900 /* Exact match, we're done */
901 dom_string_unref(attr_val
);
905 /* No exact match, try it with a hyphen on the end */
906 vdata
= lwc_string_data(value
); /* needle */
907 vdata_len
= lwc_string_length(value
);
908 avdata
= dom_string_data(attr_val
); /* haystack */
909 avdata_len
= dom_string_byte_length(attr_val
);
910 dom_string_unref(attr_val
);
912 if (avdata_len
> vdata_len
&& avdata
[vdata_len
] == '-') {
913 if (strncasecmp(avdata
, vdata
, vdata_len
) == 0) {
914 /* If there's a hyphen in the right position,
915 * it suffices to compare the strings only up
926 * Test the given node for an attribute whose value is a
927 * space-separated list of words, one of which is the given word
929 * This will return true (via the "match" pointer) if the libdom node
930 * has an attribute with the given name and whose value when
931 * considered as a space-separated list of words contains the given
932 * word. The comparison is case-sensitive. This corresponds to
933 * [attr~=value] in CSS.
935 * \param pw Pointer to the current SVG parser state
936 * \param node Libdom SVG node to test
937 * \param qname Attribute name to check for
938 * \param word Value word to check for
939 * \param match Pointer to the test result
941 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
942 * intern the attribute name (which usually indicates memory
945 css_error
node_has_attribute_includes(void *pw
, void *node
,
946 const css_qname
*qname
, lwc_string
*word
,
952 dom_string
*attr_val
;
954 size_t wordlen
; /* length of "word" */
956 /* pointers used to parse a space-separated list of words */
961 *match
= false; /* default to no match */
963 wordlen
= lwc_string_length(word
);
965 /* In this case, the spec says that "if 'val' is the
966 * empty string, it will never represent anything." */
970 /* Intern the attribute name as a dom_string so we can
971 * use dom_element_get_attribute() */
972 err
= dom_string_create_interned(
973 (const uint8_t *) lwc_string_data(qname
->name
),
974 lwc_string_length(qname
->name
),
976 if (err
!= DOM_NO_ERR
) {
980 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
981 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
982 /* There was an error getting the attribute's value or
983 * the attribute doesn't exist. So, no match? */
984 dom_string_unref(name
);
988 /* Parse the list comparing each word against "word" */
989 start
= dom_string_data(attr_val
);
990 end
= start
+ dom_string_byte_length(attr_val
);
991 dom_string_unref(attr_val
);
993 for (p
= start
; p
<= end
; p
++) {
994 /* Move forward until we find the end of the first word */
995 if (*p
== ' ' || *p
== '\0') {
996 /* If the length of that word is the length of the
997 * word we're looking for, do the comparison. */
998 if ((size_t) (p
- start
) == wordlen
&&
1000 lwc_string_data(word
),
1005 /* No match? Set "start" to the beginning of
1006 * the next word and loop. */
1016 * Test the given node for an attribute whose value begins with the
1019 * This will return true (via the "match" pointer) if the libdom node
1020 * has an attribute with the given name and whose value begins with
1021 * the given prefix string. The comparison is case-sensitive. This
1022 * corresponds to [attr^=value] in CSS.
1024 * \param pw Pointer to the current SVG parser state
1025 * \param node Libdom SVG node to test
1026 * \param qname Attribute name to check for
1027 * \param prefix Value prefix to check for
1028 * \param match Pointer to the test result
1030 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1031 * intern the attribute name (which usually indicates memory
1034 css_error
node_has_attribute_prefix(void *pw
, void *node
,
1035 const css_qname
*qname
, lwc_string
*prefix
,
1040 dom_string
*attr_val
;
1042 const char *avdata
; /* attribute value data */
1043 size_t avdata_len
; /* length of that attribute value data */
1044 size_t prefixlen
; /* length of "prefix" */
1046 prefixlen
= lwc_string_length(prefix
);
1047 if (prefixlen
== 0) {
1048 /* In this case, the spec says that "if 'val' is the
1049 * empty string, it will never represent anything." */
1053 /* Intern the attribute name as a dom_string so we can
1054 * use dom_element_get_attribute() */
1055 err
= dom_string_create_interned(
1056 (const uint8_t *) lwc_string_data(qname
->name
),
1057 lwc_string_length(qname
->name
),
1059 if (err
!= DOM_NO_ERR
) {
1063 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
1064 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
1065 /* There was an error getting the attribute's value or
1066 * the attribute doesn't exist. So, no match? */
1067 dom_string_unref(name
);
1072 /* Otherwise, we have the attribute value from the given node,
1073 * and the first thing we want to do is check to see if the
1074 * whole thing matches the prefix. */
1075 dom_string_unref(name
);
1076 *match
= dom_string_lwc_isequal(attr_val
, prefix
);
1078 /* If not, check to see if an, uh, prefix matches the
1080 if (*match
== false) {
1081 avdata
= dom_string_data(attr_val
);
1082 avdata_len
= dom_string_byte_length(attr_val
);
1083 if ((avdata_len
>= prefixlen
) &&
1084 (strncasecmp(avdata
,
1085 lwc_string_data(prefix
),
1087 /* Use strncasecmp to compare only the first
1088 * "n" characters, where "n" is the length of
1094 dom_string_unref(attr_val
);
1101 * Test the given node for an attribute whose value end with the
1104 * This will return true (via the "match" pointer) if the libdom node
1105 * has an attribute with the given name and whose value ends with
1106 * the given suffix string. The comparison is case-sensitive. This
1107 * corresponds to [attr$=value] in CSS.
1109 * \param pw Pointer to the current SVG parser state
1110 * \param node Libdom SVG node to test
1111 * \param qname Attribute name to check for
1112 * \param suffix Value suffix to check for
1113 * \param match Pointer to the test result
1115 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1116 * intern the attribute name (which usually indicates memory
1119 css_error
node_has_attribute_suffix(void *pw
, void *node
,
1120 const css_qname
*qname
, lwc_string
*suffix
,
1125 dom_string
*attr_val
;
1127 const char *avdata
; /* attribute value data */
1128 size_t avdata_len
; /* length of that attribute value data */
1129 size_t suffixlen
; /* length of "suffix" */
1131 /* convenience pointer we'll use when matching the suffix */
1132 const char *suffix_start
;
1134 suffixlen
= lwc_string_length(suffix
);
1135 if (suffixlen
== 0) {
1136 /* In this case, the spec says that "if 'val' is the
1137 * empty string, it will never represent anything." */
1141 /* Intern the attribute name as a dom_string so we can
1142 * use dom_element_get_attribute() */
1143 err
= dom_string_create_interned(
1144 (const uint8_t *) lwc_string_data(qname
->name
),
1145 lwc_string_length(qname
->name
),
1147 if (err
!= DOM_NO_ERR
) {
1151 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
1152 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
1153 /* There was an error getting the attribute's value or
1154 * the attribute doesn't exist. So, no match? */
1155 dom_string_unref(name
);
1160 /* Otherwise, we have the attribute value from the given node,
1161 * and the first thing we want to do is check to see if the
1162 * whole thing matches the suffix. */
1163 dom_string_unref(name
);
1164 *match
= dom_string_lwc_isequal(attr_val
, suffix
);
1166 /* If not, check to see if an, uh, suffix matches the
1168 if (*match
== false) {
1169 avdata
= dom_string_data(attr_val
);
1170 avdata_len
= dom_string_byte_length(attr_val
);
1172 suffix_start
= (char *)(avdata
+ avdata_len
- suffixlen
);
1174 if ((avdata_len
>= suffixlen
) &&
1175 (strncasecmp(suffix_start
,
1176 lwc_string_data(suffix
),
1178 /* Use strncasecmp to compare only the last
1179 * "n" characters, where "n" is the length of
1185 dom_string_unref(attr_val
);
1192 * Implement node_has_attribute_substring() with optional case-
1193 * insensitivity. This corresponds to [attr*=value i] in CSS and is
1194 * not supported by libcss yet, but it allows us to factor out some
1197 static css_error
_node_has_attribute_substring(void *pw
, void *node
,
1198 const css_qname
*qname
, lwc_string
*substring
,
1199 bool *match
, bool insensitive
)
1203 dom_string
*attr_val
;
1205 size_t attr_len
; /* length of attr_val */
1206 size_t substrlen
; /* length of "substring" */
1208 /* Convenience pointers we use when comparing substrings */
1212 substrlen
= lwc_string_length(substring
);
1213 if (substrlen
== 0) {
1214 /* In this case, the spec says that "if 'val' is the
1215 * empty string, it will never represent anything." */
1219 /* Intern the attribute name as a dom_string so we can
1220 * use dom_element_get_attribute() */
1221 err
= dom_string_create_interned(
1222 (const uint8_t *) lwc_string_data(qname
->name
),
1223 lwc_string_length(qname
->name
),
1225 if (err
!= DOM_NO_ERR
) {
1229 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
1230 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
1231 /* There was an error getting the attribute's value or
1232 * the attribute doesn't exist. So, no match? */
1233 dom_string_unref(name
);
1238 /* Otherwise, we have the attribute value from the given node,
1239 * and the first thing we want to do is check to see if the
1240 * whole thing matches the substring. */
1241 dom_string_unref(name
);
1244 *match
= dom_string_caseless_lwc_isequal(attr_val
, substring
);
1247 *match
= dom_string_lwc_isequal(attr_val
, substring
);
1250 /* If not, check to see if an, uh, substring matches the
1252 if (*match
== false) {
1253 p
= dom_string_data(attr_val
);
1255 /* Check every long-enough suffix for a prefix match */
1256 attr_len
= dom_string_byte_length(attr_val
);
1257 if (attr_len
>= substrlen
) {
1258 p_max
= p
+ attr_len
- substrlen
;
1259 while (p
<= p_max
) {
1261 lwc_string_data(substring
),
1271 dom_string_unref(attr_val
);
1277 * Test the given node for an attribute whose value contains the
1280 * This will return true (via the "match" pointer) if the libdom node
1281 * has an attribute with the given name and whose value contains the
1282 * given substring. The comparison is case-sensitive. This corresponds
1283 * to [attr*=value] in CSS.
1285 * \param pw Pointer to the current SVG parser state
1286 * \param node Libdom SVG node to test
1287 * \param qname Attribute name to check for
1288 * \param substring Value substring to check for
1289 * \param match Pointer to the test result
1291 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1292 * intern the attribute name (which usually indicates memory
1295 css_error
node_has_attribute_substring(void *pw
, void *node
,
1296 const css_qname
*qname
, lwc_string
*substring
,
1299 return _node_has_attribute_substring(pw
, node
, qname
, substring
,
1305 * Test whether or not the given node is the document's root element
1306 * This corresponds to the CSS :root pseudo-selector.
1308 * \param pw Pointer to the current SVG parser state
1309 * \param node Libdom SVG node to test
1310 * \param match Pointer to the test result
1312 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1314 css_error
node_is_root(void *pw
, void *node
, bool *match
)
1321 err
= dom_node_get_parent_node((dom_node
*)node
, &parent
);
1322 if (err
!= DOM_NO_ERR
) {
1326 /* It's the root element if it doesn't have a parent element */
1327 if (parent
!= NULL
) {
1328 err
= dom_node_get_node_type(parent
, &type
);
1329 dom_node_unref(parent
);
1330 if (err
!= DOM_NO_ERR
) {
1333 if (type
!= DOM_DOCUMENT_NODE
) {
1334 /* DOM_DOCUMENT_NODE is the only allowable
1335 * type of parent node for the root element */
1347 * Used internally in node_count_siblings() to "count" the given
1348 * sibling node. It factors out the node type and name checks.
1350 static int node_count_siblings_check(dom_node
*dnode
,
1357 dom_string
*dnode_name
;
1359 /* We flip this to 1 if/when we count this node */
1362 if (dnode
== NULL
) {
1366 exc
= dom_node_get_node_type(dnode
, &type
);
1367 if ((exc
!= DOM_NO_ERR
) || (type
!= DOM_ELEMENT_NODE
)) {
1368 /* We only count element siblings */
1372 /* ... with the right name */
1375 exc
= dom_node_get_node_name(dnode
, &dnode_name
);
1377 if ((exc
== DOM_NO_ERR
) && (dnode_name
!= NULL
)) {
1378 if (dom_string_isequal(name
,
1382 dom_string_unref(dnode_name
);
1393 * Count the given node's sibling elements
1395 * This counts the given node's sibling elements in one direction,
1396 * either forwards or backwards, in the DOM. Keep in mind that the
1397 * libdom tree is upside-down compared to the CSS one; so "next" and
1398 * "previous" are actually reversed; the default is to count preceding
1399 * libdom siblings which correspond to subsequent CSS siblings.
1401 * This operation is central to the CSS :first-child, :nth-child, and
1402 * :last-child (et cetera) pseudo-selectors.
1404 * If same_name is true, then only nodes having the same
1405 * (case-sensitive) name as the given node are counted.
1407 * \param pw Pointer to the current SVG parser state
1408 * \param node Libdom SVG node whose siblings we're counting
1409 * \param same_name Whether or not to count only siblings having
1410 * the same name as the given node
1411 * \param after Count subsequent siblings rather than precedent
1412 * ones (the default)
1413 * \param count Pointer to the return value, the number of sibling
1416 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1418 css_error
node_count_siblings(void *pw
, void *node
,
1419 bool same_name
, bool after
, int32_t *count
)
1423 dom_node
*dnode
; /* node, but with the right type */
1424 dom_string
*dnode_name
;
1425 dom_node
*next
; /* "next" sibling (depends on direction) */
1427 /* Pointer to the "next sibling" function */
1428 dom_exception (*next_func
)(dom_node
*, dom_node
**);
1433 dnode
= (dom_node
*)node
;
1435 exc
= dom_node_get_node_name(dnode
, &dnode_name
);
1436 if ((exc
!= DOM_NO_ERR
) || (dnode_name
== NULL
)) {
1441 /* Increment the reference counter for dnode for as long as
1442 * we retain a reference to it. */
1443 dnode
= dom_node_ref(dnode
);
1445 next_func
= dom_node_get_previous_sibling
;
1447 next_func
= dom_node_get_next_sibling
;
1451 exc
= next_func(dnode
, &next
);
1452 if (exc
!= DOM_NO_ERR
) {
1456 /* If next_func worked, we're about to swap "next"
1457 * with "dnode" meaning that we will no longer retain
1458 * a reference to the current dnode. */
1459 dom_node_unref(dnode
);
1462 *count
+= node_count_siblings_check(dnode
,
1465 } while (dnode
!= NULL
);
1467 if (dnode_name
!= NULL
) {
1468 dom_string_unref(dnode_name
);
1476 * Determine whether or not the given element is empty
1478 * An element is "nonempty" if it has a child that is either an
1479 * element node or a text node.
1481 * \param pw Pointer to the current SVG parser state
1482 * \param node Libdom SVG node to check for emptiness
1483 * \param is_empty Pointer to the return value
1485 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1487 css_error
node_is_empty(void *pw
, void *node
, bool *is_empty
)
1490 dom_node
*child
; /* current child node pointer */
1491 dom_node
*next
; /* next child node pointer */
1492 dom_node_type type
; /* what type of node is "child" */
1495 /* Assume that it's empty by default */
1498 /* Get the given node's first child. Implementation detail:
1499 * this increments the reference counter on the child node. */
1500 err
= dom_node_get_first_child((dom_node
*)node
, &child
);
1501 if (err
!= DOM_NO_ERR
) {
1505 /* And now loop through all children looking for a
1506 * text/element node. If we find one, the original
1507 * node is "nonempty" */
1508 while (child
!= NULL
) {
1509 err
= dom_node_get_node_type(child
, &type
);
1510 if (err
!= DOM_NO_ERR
) {
1511 dom_node_unref(child
);
1515 if (type
== DOM_ELEMENT_NODE
|| type
== DOM_TEXT_NODE
) {
1517 dom_node_unref(child
);
1521 err
= dom_node_get_next_sibling(child
, &next
);
1522 if (err
!= DOM_NO_ERR
) {
1523 dom_node_unref(child
);
1527 /* If we're moving to the next node, we can release
1528 * the reference to the current one */
1529 dom_node_unref(child
);
1538 * Determine whether or not the given node is a link
1540 * A node is a link if it is an element node whose name is "a" and if
1541 * it has an "href" attribute (case-sensitive). This selector
1542 * corresponds to node:link pseudo-class in CSS.
1544 * This pseudo-class is a bit awkward because the two standards (HTML5
1545 * and CSS) disagree on what it means, and because libsvgtiny does not
1546 * have enough information to determine if a link has been "visited"
1547 * yet -- that's a UI property. CSS says that :link is for unvisited
1548 * links, which we can't determine. HTML5 says that each link must
1549 * be either a :link or :visited. Since we can't decide either way,
1550 * It seems less wrong to declare that all links are unvisited; i.e.
1551 * that they match :link.
1553 * \param pw Pointer to the current SVG parser state
1554 * \param node Libdom SVG node to check
1555 * \param is_link Pointer to the boolean return value
1557 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1559 css_error
node_is_link(void *pw
, void *node
, bool *is_link
)
1562 dom_node
*dnode
; /* node, but with the right type */
1563 dom_string
*dnode_name
;
1565 struct svgtiny_parse_state
* state
;
1567 dnode
= (dom_node
*)node
;
1569 has_href
= false; /* assume no href attribute */
1570 *is_link
= false; /* assume that it's not a link */
1572 exc
= dom_node_get_node_name(dnode
, &dnode_name
);
1573 if ((exc
!= DOM_NO_ERR
) || (dnode_name
== NULL
)) {
1577 state
= (struct svgtiny_parse_state
*)pw
;
1578 if (dom_string_isequal(dnode_name
, state
->interned_a
)) {
1579 exc
= dom_element_has_attribute(node
,
1580 state
->interned_href
,
1582 if (exc
== DOM_NO_ERR
&& has_href
) {
1587 dom_string_unref(dnode_name
);
1592 * Check if the given node is a link that has been visited already
1594 * This check always fails because the SVG DOM does not have the
1595 * necessary information (it's a UI property).
1597 * \param pw Pointer to the current SVG parser state; unused
1598 * \param node Libdom SVG node to check; unused
1599 * \param is_visited Pointer to the boolean return value
1601 * \return Always returns CSS_OK
1603 css_error
node_is_visited(void *pw
, void *node
, bool *is_visited
)
1607 *is_visited
= false;
1613 * Check if the given node is being "hovered" over
1615 * This check always fails because the SVG DOM does not have the
1616 * necessary information (it's a UI property).
1618 * \param pw Pointer to the current SVG parser state; unused
1619 * \param node Libdom SVG node to check; unused
1620 * \param is_hover Pointer to the boolean return value
1622 * \return Always returns CSS_OK
1624 css_error
node_is_hover(void *pw
, void *node
, bool *is_hover
)
1634 * Check if the given node is "active"
1636 * This check always fails because the SVG DOM does not have the
1637 * necessary information (it's a UI property).
1639 * \param pw Pointer to the current SVG parser state; unused
1640 * \param node Libdom SVG node to check; unused
1641 * \param is_active Pointer to the boolean return value
1643 * \return Always returns CSS_OK
1645 css_error
node_is_active(void *pw
, void *node
, bool *is_active
)
1655 * Check if the given node has the focus
1657 * This check always fails because the SVG DOM does not have the
1658 * necessary information (it's a UI property).
1660 * \param pw Pointer to the current SVG parser state; unused
1661 * \param node Libdom SVG node to check; unused
1662 * \param is_focus Pointer to the boolean return value
1664 * \return Always returns CSS_OK
1666 css_error
node_is_focus(void *pw
, void *node
, bool *is_focus
)
1676 * Check if the given node is enabled
1678 * This check always fails because the SVG DOM does not have the
1679 * necessary information (it's a UI property).
1681 * \param pw Pointer to the current SVG parser state; unused
1682 * \param node Libdom SVG node to check; unused
1683 * \param is_enabled Pointer to the boolean return value
1685 * \return Always returns CSS_OK
1687 css_error
node_is_enabled(void *pw
, void *node
, bool *is_enabled
)
1691 *is_enabled
= false;
1697 * Check if the given node is disabled
1699 * This check always fails because the SVG DOM does not have the
1700 * necessary information (it's a UI property). Beware, until they are
1701 * implemented, this is NOT the logical negation of node_is_enabled!
1703 * \param pw Pointer to the current SVG parser state; unused
1704 * \param node Libdom SVG node to check; unused
1705 * \param is_disabled Pointer to the boolean return value
1707 * \return Always returns CSS_OK
1709 css_error
node_is_disabled(void *pw
, void *node
, bool *is_disabled
)
1713 *is_disabled
= false;
1719 * Test whether or not the given node is "checked"
1721 * This test always fails because the SVG DOM does not have the
1722 * necessary information (it's a UI property).
1724 * \param pw Pointer to the current SVG parser state; unused
1725 * \param node Libdom SVG node to check; unused
1726 * \param is_checked Pointer to the boolean return value
1728 * \return Always returns CSS_OK
1730 css_error
node_is_checked(void *pw
, void *node
, bool *is_checked
)
1734 *is_checked
= false;
1740 * Check if the given node is the "target" of the document URL
1742 * This test always fails because the SVG DOM does not have the
1743 * necessary information (it's a UI property).
1745 * \param pw Pointer to the current SVG parser state; unused
1746 * \param node Libdom SVG node to check; unused
1747 * \param is_target Pointer to the boolean return value
1749 * \return Always returns CSS_OK
1751 css_error
node_is_target(void *pw
, void *node
, bool *is_target
)
1761 * Check if the given node is the given language
1763 * This test is corresponds to the CSS :lang() selector and is not
1764 * fully implemented yet: it looks only for "lang" attributes on the
1765 * given element and its parents, and performs a simple substring
1766 * check. This results in a partial implementation of CSS Level 3 for
1767 * SVG 2.0. In particular, it ignores all "xml:lang" attributes in
1768 * favor of the "lang" attribute that is defined only in SVG 2.0.
1770 * \param pw Pointer to the current SVG parser state; unused
1771 * \param node Libdom SVG node to check
1772 * \param lang The language to match
1773 * \param is_lang Pointer to the boolean return value
1775 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1777 static css_error
node_is_lang(void *pw
, void *node
,
1778 lwc_string
*lang
, bool *is_lang
)
1781 /* SVG2 elements support both "lang" and "xml:lang"
1782 * attributes; earlier versions have only the XML
1783 * attribute. It would not be too hard to add support for
1784 * xml:lang" here. The main difficulty standing in the way of
1785 * a full Level 4 implementation is the complexity of the
1788 * https://www.w3.org/TR/selectors-4/#the-lang-pseudo
1793 dom_exception d_err
;
1794 dom_node
*n
; /* current node */
1795 dom_node
*p
; /* parent node */
1796 bool match
; /* retval from node_has_attribute_substring() */
1798 /* Define the attribute name "lang" that we're looking for.
1799 * We only use a css_qname here because that's what the
1800 * node_has_attribute_substring() takes; the namespace
1801 * portion of it is irrelevant. */
1805 if (lwc_intern_string("lang", 4, &attr
.name
) != lwc_error_ok
) {
1809 *is_lang
= false; /* default to no match */
1810 n
= (dom_node
*)node
;
1812 /* Loop through all parents of the given node looking for a
1813 * substring match */
1815 c_err
= _node_has_attribute_substring(pw
, (void *)n
, &attr
,
1816 lang
, &match
, true);
1817 if (c_err
!= CSS_OK
) {
1818 lwc_string_destroy(attr
.name
);
1822 /* matched this element; we're done */
1823 lwc_string_destroy(attr
.name
);
1828 /* no match on this element, try its parent */
1829 d_err
= dom_node_get_parent_node(n
, &p
);
1830 if (d_err
!= DOM_NO_ERR
) {
1831 lwc_string_destroy(attr
.name
);
1837 /* If we never find a match we may wind up here */
1838 lwc_string_destroy(attr
.name
);
1844 * Return presentational hints for the given node
1846 * Unless an SVG is being rendered from within an HTML document,
1847 * there are no presentational hints. We always return an empty
1850 * \param pw Pointer to the current SVG parser state; unused
1851 * \param node Libdom SVG node whose hints we want; unused
1852 * \param nhints How many hints are returned (return by reference)
1853 * \param hints Array of css_hint structures (return by reference)
1855 * \return Always returns CSS_OK
1857 css_error
node_presentational_hint(void *pw
, void *node
,
1858 uint32_t *nhints
, css_hint
**hints
)
1869 * User-agent defaults for CSS properties
1871 * Ideally we would provide _no_ defaults here, because we don't yet
1872 * support any CSS properties that can use them. However, we run into
1873 * libcss parent/child style composition issues unless these defaults
1874 * are provided. And it's harmless to provide them, so let's do it.
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
,
1887 case CSS_PROP_COLOR
:
1888 hint
->data
.color
= 0xff000000;
1889 hint
->status
= CSS_COLOR_COLOR
;
1891 case CSS_PROP_FONT_FAMILY
:
1892 hint
->data
.strings
= NULL
;
1893 hint
->status
= CSS_FONT_FAMILY_SANS_SERIF
;
1895 case CSS_PROP_QUOTES
:
1896 hint
->data
.strings
= NULL
;
1897 hint
->status
= CSS_QUOTES_NONE
;
1899 case CSS_PROP_VOICE_FAMILY
:
1900 hint
->data
.strings
= NULL
;
1912 * A "user handler" function that we pass to dom_node_set_user_data()
1913 * in set_libcss_node_data(). The set_libcss_node_data() documentation
1914 * says that if the node (on which data is set) is is deleted or
1915 * cloned, or if its ancestors are modified, then we must call
1916 * css_libcss_node_data_handler() on the user data. This function
1917 * basically just checks to see which DOM event has happened and
1918 * calls css_libcss_node_data_handler() when any of those criteria
1921 static void svgtiny_dom_user_data_handler(dom_node_operation operation
,
1922 dom_string
*key
, void *data
, struct dom_node
*src
,
1923 struct dom_node
*dst
)
1925 /* We only care about the userdata that is identified by our
1926 * userdata key. Unfortunately I see no obvious way to obtain
1927 * the copy of the userdata key that is already interned in
1928 * svgtiny_strings.h; so we duplicate it here (ugh). */
1930 dom_string_create((const uint8_t *)"_libcss_user_data", 17, &str
);
1931 if (dom_string_isequal(str
,key
) == false || data
== NULL
) {
1932 /* Wrong key, or no data */
1936 /* Check the DOM operation, and make the corresponding call to
1937 * css_libcss_node_data_handler(). No error handling is done.
1939 switch (operation
) {
1940 case DOM_NODE_CLONED
:
1941 css_libcss_node_data_handler(&svgtiny_select_handler
,
1943 NULL
, src
, dst
, data
);
1945 case DOM_NODE_RENAMED
:
1946 css_libcss_node_data_handler(&svgtiny_select_handler
,
1948 NULL
, src
, NULL
, data
);
1950 case DOM_NODE_IMPORTED
:
1951 case DOM_NODE_ADOPTED
:
1952 case DOM_NODE_DELETED
:
1953 css_libcss_node_data_handler(&svgtiny_select_handler
,
1955 NULL
, src
, NULL
, data
);
1957 /* Our list of cases should have been exhaustive */
1963 * Store libcss data on a node
1965 * This is part of the libcss select handler API that we need to
1966 * implement. It is essentially a thin dom_node_set_user_data()
1969 * \param pw Pointer to the current SVG parser state
1970 * \param node Libdom SVG node on which to store the data
1971 * \param libcss_node_data Pointer to the data to store
1973 * \return Always returns CSS_OK
1975 css_error
set_libcss_node_data(void *pw
, void *node
,
1976 void *libcss_node_data
)
1978 struct svgtiny_parse_state
*state
;
1981 /* A unique "userdata key" (a string) is used to identify this
1983 state
= (struct svgtiny_parse_state
*)pw
;
1984 dom_node_set_user_data((dom_node
*)node
,
1985 state
->interned_userdata_key
,
1987 svgtiny_dom_user_data_handler
,
1990 /* dom_node_set_user_data() always returns DOM_NO_ERR */
1996 * Retrieve libcss data from a node
1998 * This is part of the libcss select handler API that we need to
1999 * implement. It is essentially a thin dom_node_get_user_data()
2002 * \param pw Pointer to the current SVG parser state
2003 * \param node Libdom SVG node from which to get the data
2004 * \param libcss_node_data Address at which to store a pointer to the data
2006 * \return Always returns CSS_OK
2008 css_error
get_libcss_node_data(void *pw
, void *node
,
2009 void **libcss_node_data
)
2011 struct svgtiny_parse_state
*state
;
2013 /* A unique "userdata key" (a string) is used to identify this
2015 state
= (struct svgtiny_parse_state
*)pw
;
2016 dom_node_get_user_data((dom_node
*)node
,
2017 state
->interned_userdata_key
,
2020 /* dom_node_get_user_data() always returns DOM_NO_ERR */
2026 * The vtable of select handler callbacks passed by libsvgtiny to
2027 * css_select_style().
2029 static css_select_handler svgtiny_select_handler
= {
2030 CSS_SELECT_HANDLER_VERSION_1
,
2034 named_ancestor_node
,
2037 named_generic_sibling_node
,
2044 node_has_attribute_equal
,
2045 node_has_attribute_dashmatch
,
2046 node_has_attribute_includes
,
2047 node_has_attribute_prefix
,
2048 node_has_attribute_suffix
,
2049 node_has_attribute_substring
,
2051 node_count_siblings
,
2063 node_presentational_hint
,
2064 ua_default_for_property
,
2065 set_libcss_node_data
,
2066 get_libcss_node_data
,