1 #include <libcss/libcss.h>
2 #include <strings.h> /* strncasecmp */
5 #include "svgtiny_internal.h"
7 css_error
svgtiny_resolve_url(void *pw
, const char *base
,
8 lwc_string
*rel
, lwc_string
**abs
);
10 /* select handler callbacks */
11 static css_error
node_name(void *pw
, void *node
, css_qname
*qname
);
12 static css_error
node_classes(void *pw
, void *node
,
13 lwc_string
***classes
, uint32_t *n_classes
);
14 static css_error
node_id(void *pw
, void *node
, lwc_string
**id
);
15 static css_error
named_ancestor_node(void *pw
, void *node
,
16 const css_qname
*qname
, void **ancestor
);
17 static css_error
named_parent_node(void *pw
, void *node
,
18 const css_qname
*qname
, void **parent
);
19 static css_error
named_sibling_node(void *pw
, void *node
,
20 const css_qname
*qname
, void **sibling
);
21 static css_error
named_generic_sibling_node(void *pw
, void *node
,
22 const css_qname
*qname
, void **sibling
);
23 static css_error
parent_node(void *pw
, void *node
, void **parent
);
24 static css_error
sibling_node(void *pw
, void *node
, void **sibling
);
25 static css_error
node_has_name(void *pw
, void *node
,
26 const css_qname
*qname
, bool *match
);
27 static css_error
node_has_class(void *pw
, void *node
,
28 lwc_string
*name
, bool *match
);
29 static css_error
node_has_id(void *pw
, void *node
,
30 lwc_string
*name
, bool *match
);
31 static css_error
node_has_attribute(void *pw
, void *node
,
32 const css_qname
*qname
, bool *match
);
33 static css_error
node_has_attribute_equal(void *pw
, void *node
,
34 const css_qname
*qname
, lwc_string
*value
,
36 static css_error
node_has_attribute_dashmatch(void *pw
, void *node
,
37 const css_qname
*qname
, lwc_string
*value
,
39 static css_error
node_has_attribute_includes(void *pw
, void *node
,
40 const css_qname
*qname
, lwc_string
*word
,
42 static css_error
node_has_attribute_prefix(void *pw
, void *node
,
43 const css_qname
*qname
, lwc_string
*prefix
,
45 static css_error
node_has_attribute_suffix(void *pw
, void *node
,
46 const css_qname
*qname
, lwc_string
*suffix
,
48 static css_error
node_has_attribute_substring(void *pw
, void *node
,
49 const css_qname
*qname
, lwc_string
*substring
,
51 static css_error
node_is_root(void *pw
, void *node
, bool *match
);
52 static css_error
node_count_siblings(void *pw
, void *node
,
53 bool same_name
, bool after
, int32_t *count
);
54 static css_error
node_is_empty(void *pw
, void *node
, bool *is_empty
);
55 static css_error
node_is_link(void *pw
, void *node
, bool *is_link
);
56 static css_error
node_is_visited(void *pw
, void *node
, bool *is_visited
);
57 static css_error
node_is_hover(void *pw
, void *node
, bool *is_hover
);
58 static css_error
node_is_active(void *pw
, void *node
, bool *is_active
);
59 static css_error
node_is_focus(void *pw
, void *node
, bool *is_focus
);
60 static css_error
node_is_enabled(void *pw
, void *node
, bool *is_enabled
);
61 static css_error
node_is_disabled(void *pw
, void *node
, bool *is_disabled
);
62 static css_error
node_is_checked(void *pw
, void *node
, bool *is_checked
);
63 static css_error
node_is_target(void *pw
, void *node
, bool *is_target
);
64 static css_error
node_is_lang(void *pw
, void *node
,
65 lwc_string
*lang
, bool *is_lang
);
66 static css_error
node_presentational_hint(void *pw
, void *node
,
67 uint32_t *nhints
, css_hint
**hints
);
68 static css_error
ua_default_for_property(void *pw
, uint32_t property
,
70 static css_error
set_libcss_node_data(void *pw
, void *node
,
71 void *libcss_node_data
);
72 static css_error
get_libcss_node_data(void *pw
, void *node
,
73 void **libcss_node_data
);
75 /* select handler vtable */
76 static struct css_select_handler svgtiny_select_handler
;
79 /* Every call to svgtiny_select_style() needs this, so let's only make
81 static const css_media media_all
= {
82 .type
= CSS_MEDIA_ALL
,
86 * Convenient wrapper around css_select_style()
88 * \param state The current state of the libsvgtiny parser
89 * \param node The node that we're getting styles for
90 * \param inline_sheet The inline stylesheet for the given node
91 * \param result Address at which to store the results array
93 css_error
svgtiny_select_style(struct svgtiny_parse_state
*state
,
95 const css_stylesheet
*inline_sheet
,
96 css_select_results
**result
)
98 return css_select_style(state
->select_ctx
,
103 &svgtiny_select_handler
,
109 * Resolve a relative URL to an absolute one by doing nothing. This is
110 * the simplest possible implementation of a URL resolver, needed for
113 css_error
svgtiny_resolve_url(void *pw
,
114 const char *base
, lwc_string
*rel
, lwc_string
**abs
)
119 /* Copy the relative URL to the absolute one (the return
121 *abs
= lwc_string_ref(rel
);
126 * Create a stylesheet with the default set of params.
128 * \param sheet A stylesheet pointer, passed in by reference, that
129 * we use to store the newly-created stylesheet.
130 * \param inline_style True if this stylesheet represents an inline
131 * style, and false otherwise.
133 * \return The return value from css_stylesheet_create() is returned.
135 css_error
svgtiny_create_stylesheet(css_stylesheet
**sheet
,
138 css_stylesheet_params params
;
140 params
.params_version
= CSS_STYLESHEET_PARAMS_VERSION_1
;
141 params
.level
= CSS_LEVEL_DEFAULT
;
142 params
.charset
= NULL
;
145 params
.allow_quirks
= false;
146 params
.inline_style
= inline_style
;
147 params
.resolve
= svgtiny_resolve_url
;
148 params
.resolve_pw
= NULL
;
149 params
.import = NULL
;
150 params
.import_pw
= NULL
;
152 params
.color_pw
= NULL
;
154 params
.font_pw
= NULL
;
156 return css_stylesheet_create(¶ms
, sheet
);
160 /**************************/
161 /* libcss select handlers */
162 /**************************/
164 * From here on we implement the "select handler "API defined in
165 * libcss's include/libcss/select.h and discussed briefly in its
171 * Retrieve the given node's name
173 * \param pw Pointer to the current SVG parser state
174 * \param node Libdom SVG node
175 * \param qname Address at which to store the node name
177 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
179 css_error
node_name(void *pw
, void *node
, css_qname
*qname
)
183 struct svgtiny_parse_state
*state
;
185 err
= dom_node_get_node_name((dom_node
*)node
, &name
);
186 if (err
!= DOM_NO_ERR
) {
190 state
= (struct svgtiny_parse_state
*)pw
;
191 qname
->ns
= lwc_string_ref(state
->interned_svg_xmlns
);
193 err
= dom_string_intern(name
, &qname
->name
);
194 if (err
!= DOM_NO_ERR
) {
195 dom_string_unref(name
);
199 dom_string_unref(name
);
206 * Retrieve the given node's classes
208 * \param pw Pointer to the current SVG parser state
209 * \param node Libdom SVG node
210 * \param classes Address at which to store the class name array
211 * \param n_classes Address at which to store the length of the class
214 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
216 * \note CSS_NOMEM is not possible in practice as of libdom-0.4.1,
217 * because the underlying libdom function never fails
219 css_error
node_classes(void *pw
, void *node
,
220 lwc_string
***classes
, uint32_t *n_classes
)
225 err
= dom_element_get_classes((dom_node
*)node
, classes
, n_classes
);
227 /* The implementation does not do it, but the documentation
228 for dom_element_get_classes() says that a DOM_NO_MEM_ERR is
229 possible here, so we handle it to be on the safe side. */
230 if (err
!= DOM_NO_ERR
) {
239 * Retrieve the given node's id
241 * \param pw Pointer to the current SVG parser state
242 * \param node Libdom SVG node
243 * \param id Address at which to store the id
245 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
247 css_error
node_id(void *pw
, void *node
, lwc_string
**id
)
251 struct svgtiny_parse_state
*state
;
253 /* Begin with the assumption that this node has no id */
256 state
= (struct svgtiny_parse_state
*)pw
;
257 err
= dom_element_get_attribute((dom_node
*)node
,
258 state
->interned_id
, &attr
);
259 if (err
!= DOM_NO_ERR
) {
262 else if (attr
== NULL
) {
263 /* The node has no id attribute and our return value
264 is already set to NULL so we're done */
268 /* If we found an id attribute (a dom_string), intern it into
269 an lwc_string that we can return, and then cleanup the
271 err
= dom_string_intern(attr
, id
);
272 if (err
!= DOM_NO_ERR
) {
273 dom_string_unref(attr
);
276 dom_string_unref(attr
);
282 * Find the first ancestor of the given element having the given name
284 * This function thinly wraps dom_element_named_ancestor_node(), which
285 * performs exactly the search we want.
287 * \param pw Pointer to the current SVG parser state
288 * \param node Libdom SVG node whose ancestor we want
289 * \param qname Name of the ancestor node to search for
290 * \param ancestor Address at which to store the ancestor node pointer
292 * \return Always returns CSS_OK
294 * \post If a suitable element is found, a pointer to it will be
295 * stored at the address pointed to by \a ancestor; otherwise,
296 * NULL will be stored at the address pointed to by \a ancestor
298 css_error
named_ancestor_node(void *pw
, void *node
,
299 const css_qname
*qname
, void **ancestor
)
303 /* It's OK if node isn't an element, libdom checks for it. */
304 dom_element_named_ancestor_node((dom_element
*)node
,
306 (struct dom_element
**)ancestor
);
308 /* A comment in named_parent_node() explains why we unref
310 dom_node_unref(*ancestor
);
317 * Find the first parent of the given element having the given name
319 * \param pw Pointer to the current SVG parser state
320 * \param node Libdom SVG node
321 * \param qname Name of the parent node to search for
322 * \param parent Address at which to store the parent node pointer
324 * \return Always returns CSS_OK
326 * \post If a suitable element is found, a pointer to it will be
327 * stored at the address pointed to by \a parent; otherwise,
328 * NULL will be stored at the address pointed to by \a parent
330 css_error
named_parent_node(void *pw
, void *node
,
331 const css_qname
*qname
, void **parent
)
334 /* dom_element_named_parent_node() was invented to implement
335 * this select handler so there isn't much for us to do except
336 * call it. It's OK if node isn't an element, libdom checks
338 dom_element_named_parent_node((dom_element
*)node
,
340 (struct dom_element
**)parent
);
342 /* Implementation detail: dom_element_named_parent_node()
343 * increments the reference count of the parent element before
344 * returning it to us. According to docs/RefCnt in the libdom
345 * repository, this will prevent the parent element from being
346 * destroyed if it is pruned from the DOM. That sounds good,
347 * since we don't want to be using a pointer to an object that
348 * has been destroyed... but we also have no way of later
349 * decrementing the reference count ourselves, and don't want
350 * to make the returned node eternal. Decrementing the
351 * reference counter now allows it to be destroyed when the
352 * DOM no longer needs it, and so long as no other parts of
353 * libsvgtiny are messing with the DOM during parsing, that
354 * shouldn't (ha ha) cause any problems. */
355 dom_node_unref(*parent
);
362 * Find the "next-sibling" of the given element having the given name
364 * This search corresponds to the "+ foo" combinator in CSS and will
365 * find only "foo" element nodes that immediately precede the given
366 * node under the same parent in the DOM. In CSS the tree is viewed
367 * top-down and in libdom it is viewed from the bottom-up; as a result
368 * "next" and "previous" are sometimes backwards. This is case-sensitive.
370 * \param pw Pointer to the current SVG parser state
371 * \param node Libdom SVG node
372 * \param qname Name of the sibling node to search for
373 * \param sibling Address at which to store the sibling node pointer
375 * \return Always returns CSS_OK
377 * \post If a suitable element is found, a pointer to it will be
378 * stored at the address pointed to by \a sibling; otherwise,
379 * NULL will be stored at the address pointed to by \a sibling
381 css_error
named_sibling_node(void *pw
, void *node
,
382 const css_qname
*qname
, void **sibling
)
385 dom_node
*n
= node
; /* the current node */
386 dom_node
*prev
; /* the previous node */
391 *sibling
= NULL
; /* default to nothing found */
393 /* Begin the search; the first iteration we do outside of the
394 * loop. Implementation detil: dom_node_get_previous_sibling()
395 * increments the reference counter on the returned node. A
396 * comment within named_parent_node() explains why we
397 * decrement it ASAP. */
398 err
= dom_node_get_previous_sibling(n
, &n
);
399 if (err
!= DOM_NO_ERR
) {
404 /* We're looking for the first ELEMENT sibling */
405 err
= dom_node_get_node_type(n
, &type
);
406 if (err
!= DOM_NO_ERR
) {
411 if (type
== DOM_ELEMENT_NODE
) {
412 /* We found an element node, does it have the
414 err
= dom_node_get_node_name(n
, &name
);
415 if (err
!= DOM_NO_ERR
) {
420 if (dom_string_lwc_isequal(name
,
422 /* The name is right, return it */
426 /* There's only one next-sibling element node
427 * and we've already found it, so if its name
428 * wasn't right, we return the default value
430 dom_string_unref(name
);
435 /* Not an element node, so we move on the the previous
436 * previous sibling */
437 err
= dom_node_get_previous_sibling(n
, &prev
);
438 if (err
!= DOM_NO_ERR
) {
452 * Find the first "subsequent-sibling" of the given element having the
455 * This search corresponds to the "~ foo" combinator in CSS and will
456 * find only "foo" element nodes that precede the given node (under
457 * the same parent) in the DOM. In CSS the tree is viewed top-down and
458 * in libdom it is viewed from the bottom-up; as a result "next" and
459 * "previous" are sometimes backwards. This is case-sensitive.
461 * \param pw Pointer to the current SVG parser state
462 * \param node Libdom SVG node
463 * \param qname Name of the sibling node to search for
464 * \param sibling Address at which to store the sibling node pointer
466 * \return Always returns CSS_OK
468 * \post If a suitable element is found, a pointer to it will be
469 * stored at the address pointed to by \a sibling; otherwise,
470 * NULL will be stored at the address pointed to by \a sibling
472 css_error
named_generic_sibling_node(void *pw
, void *node
,
473 const css_qname
*qname
, void **sibling
)
476 dom_node
*n
= node
; /* the current node */
477 dom_node
*prev
; /* the previous node */
483 *sibling
= NULL
; /* default to nothing found */
485 /* Begin the search; the first iteration we do outside of the
486 * loop. Implementation detil: dom_node_get_previous_sibling()
487 * increments the reference counter on the returned node. A
488 * comment within named_parent_node() explains why we
489 * decrement it ASAP. */
490 err
= dom_node_get_previous_sibling(n
, &n
);
491 if (err
!= DOM_NO_ERR
) {
496 err
= dom_node_get_node_type(n
, &type
);
497 if (err
!= DOM_NO_ERR
) {
502 if (type
== DOM_ELEMENT_NODE
) {
503 /* We only want ELEMENT nodes */
504 err
= dom_node_get_node_name(n
, &name
);
505 if (err
!= DOM_NO_ERR
) {
510 if (dom_string_lwc_isequal(name
,
512 /* Found one. Save it and stop the search */
513 dom_string_unref(name
);
519 dom_string_unref(name
);
522 /* This sibling wasn't an element with the desired
523 name, so move on to the previous sibling */
524 err
= dom_node_get_previous_sibling(n
, &prev
);
525 if (err
!= DOM_NO_ERR
) {
539 * Return a pointer to the given node's parent
541 * \param pw Pointer to the current SVG parser state
542 * \param node Libdom SVG node
543 * \param parent Address at which to store the node's parent pointer
545 * \return Always returns CSS_OK
547 css_error
parent_node(void *pw
, void *node
, void **parent
)
550 /* Libdom basically implements this for us */
551 dom_element_parent_node(node
, (struct dom_element
**)parent
);
553 /* See the comment in named_parent_node() for why we decrement
554 * this reference counter here. */
555 dom_node_unref(*parent
);
562 * Find the "next-sibling" of the given element
564 * This search corresponds "+ *" in CSS and will find the first
565 * element node that immediately precedes the given node under the
566 * same parent in the DOM. In CSS the tree is viewed top-down and in
567 * libdom it is viewed from the bottom-up; as a result "next" and
568 * "previous" are sometimes backwards.
570 * \param pw Pointer to the current SVG parser state
571 * \param node Libdom SVG node
572 * \param sibling Address at which to store the sibling node pointer
574 * \return Always returns CSS_OK
576 * \post If a suitable element is found, a pointer to it will be
577 * stored at the address pointed to by \a sibling; otherwise,
578 * NULL will be stored at the address pointed to by \a sibling
580 css_error
sibling_node(void *pw
, void *node
, void **sibling
)
583 dom_node
*n
= node
; /* the current node */
584 dom_node
*prev
; /* the previous node */
588 *sibling
= NULL
; /* default to nothing found */
590 /* Begin the search; the first iteration we do outside of the
591 * loop. Implementation detil: dom_node_get_previous_sibling()
592 * increments the reference counter on the returned node. A
593 * comment within named_parent_node() explains why we
594 * decrement it ASAP. */
595 err
= dom_node_get_previous_sibling(n
, &n
);
596 if (err
!= DOM_NO_ERR
) {
601 err
= dom_node_get_node_type(n
, &type
);
602 if (err
!= DOM_NO_ERR
) {
607 if (type
== DOM_ELEMENT_NODE
) {
608 /* We found a sibling node that is also an
609 element and that's all we wanted. */
615 /* This sibling node was not an element; move on to
616 the previous sibling */
617 err
= dom_node_get_previous_sibling(n
, &prev
);
618 if (err
!= DOM_NO_ERR
) {
632 * Test the given node for the given name
634 * This will return true (via the "match" pointer) if the libdom node
635 * has the given name or if that name is the universal selector;
636 * otherwise it returns false. The comparison is case-sensitive. It
637 * corresponds to a rule like "body { ... }" in CSS.
639 * \param pw Pointer to the current SVG parser state
640 * \param node Libdom SVG node to test
641 * \param qname Name to check for
642 * \param match Pointer to the test result
644 * \return Always returns CSS_OK
646 css_error
node_has_name(void *pw
, void *node
,
647 const css_qname
*qname
, bool *match
)
649 struct svgtiny_parse_state
*state
;
653 /* Start by checking to see if qname is the universal selector */
654 state
= (struct svgtiny_parse_state
*)pw
;
655 *match
= dom_string_lwc_isequal(state
->interned_universal
, qname
->name
);
657 /* It's the universal selector. In NetSurf, all node
658 * names match the universal selector, and nothing in
659 * the libcss documentation suggests another approach,
660 * so we follow NetSurf here. */
664 err
= dom_node_get_node_name((dom_node
*)node
, &name
);
665 if (err
!= DOM_NO_ERR
) {
669 /* Unlike with HTML, SVG element names are case-sensitive */
670 *match
= dom_string_lwc_isequal(name
, qname
->name
);
671 dom_string_unref(name
);
678 * Test the given node for the given class
680 * This will return true (via the "match" pointer) if the libdom node
681 * has the given class. The comparison is case-sensitive. It
682 * corresponds to node.class in CSS.
684 * \param pw Pointer to the current SVG parser state
685 * \param node Libdom SVG node to test
686 * \param name Class name to check for
687 * \param match Pointer to the test result
689 * \return Always returns CSS_OK
691 css_error
node_has_class(void *pw
, void *node
,
692 lwc_string
*name
, bool *match
)
695 /* libdom implements this for us and apparently it cannot fail */
696 dom_element_has_class((dom_node
*)node
, name
, match
);
702 * Test the given node for the given id
704 * This will return true (via the "match" pointer) if the libdom node
705 * has the given id. The comparison is case-sensitive. It corresponds
708 * \param pw Pointer to the current SVG parser state
709 * \param node Libdom SVG node to test
710 * \param name Id to check for
711 * \param match Pointer to the test result
713 * \return Always returns CSS_OK
715 css_error
node_has_id(void *pw
, void *node
,
716 lwc_string
*name
, bool *match
)
720 struct svgtiny_parse_state
*state
;
722 attr
= NULL
; /* a priori the "id" attribute may not exist */
723 *match
= false; /* default to no match */
725 state
= (struct svgtiny_parse_state
*)pw
;
726 err
= dom_element_get_attribute((dom_node
*)node
,
727 state
->interned_id
, &attr
);
728 if (err
!= DOM_NO_ERR
|| attr
== NULL
) {
732 *match
= dom_string_lwc_isequal(attr
, name
);
733 dom_string_unref(attr
);
740 * Test the given node for the given attribute
742 * This will return true (via the "match" pointer) if the libdom node
743 * has an attribute with the given name. The comparison is
744 * case-sensitive. It corresponds to node[attr] in CSS.
746 * \param pw Pointer to the current SVG parser state
747 * \param node Libdom SVG node to test
748 * \param qname Attribute name to check for
749 * \param match Pointer to the test result
751 * \return Returns CSS_OK if successful and CSS_NOMEM if anything
754 css_error
node_has_attribute(void *pw
, void *node
,
755 const css_qname
*qname
, bool *match
)
761 /* intern the attribute name as a dom_string so we can
762 * delegate to dom_element_has_attribute() */
763 err
= dom_string_create_interned(
764 (const uint8_t *) lwc_string_data(qname
->name
),
765 lwc_string_length(qname
->name
),
767 if (err
!= DOM_NO_ERR
) {
771 err
= dom_element_has_attribute((dom_node
*)node
, name
, match
);
772 if (err
!= DOM_NO_ERR
) {
773 dom_string_unref(name
);
777 dom_string_unref(name
);
783 * Test the given node for an attribute with a specific value
785 * This will return true (via the "match" pointer) if the libdom node
786 * has an attribute with the given name and value. The comparison is
787 * case-sensitive. It corresponds to node[attr=value] in CSS.
789 * \param pw Pointer to the current SVG parser state
790 * \param node Libdom SVG node to test
791 * \param qname Attribute name to check for
792 * \param value Attribute value to check for
793 * \param match Pointer to the test result
795 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
796 * intern the attribute name (which usually indicates memory
799 css_error
node_has_attribute_equal(void *pw
, void *node
,
800 const css_qname
*qname
, lwc_string
*value
,
803 /* Implementation note: NetSurf always returns "no match" when
804 * the value is empty (length zero). We allow it, because why
809 dom_string
*attr_val
;
812 /* Intern the attribute name as a dom_string so we can
813 * use dom_element_get_attribute() */
814 err
= dom_string_create_interned(
815 (const uint8_t *) lwc_string_data(qname
->name
),
816 lwc_string_length(qname
->name
),
818 if (err
!= DOM_NO_ERR
) {
822 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
823 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
824 /* There was an error getting the attribute's value or
825 * the attribute doesn't exist. So, no match? */
826 dom_string_unref(name
);
831 /* Otherwise, we have the attribute value from the given node
832 * and all we need to do is compare. */
833 dom_string_unref(name
);
834 *match
= dom_string_lwc_isequal(attr_val
, value
);
835 dom_string_unref(attr_val
);
842 * Test the given node for an attribute with a specific value,
843 * possibly followed by a single hyphen
845 * This will return true (via the "match" pointer) if the libdom node
846 * has an attribute with the given name and value or with the given
847 * name and a value that is followed by exactly one hyphen. The
848 * comparison is case-sensitive. This corresponds to [attr|=value]
851 * \param pw Pointer to the current SVG parser state
852 * \param node Libdom SVG node to test
853 * \param qname Attribute name to check for
854 * \param value Attribute value to check for
855 * \param match Pointer to the test result
857 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
858 * intern the attribute name (which usually indicates memory
861 css_error
node_has_attribute_dashmatch(void *pw
, void *node
,
862 const css_qname
*qname
, lwc_string
*value
,
865 /* Implementation note: NetSurf always returns "no match" when
866 * the value is empty (length zero). We allow it, because why
871 dom_string
*attr_val
;
874 const char *vdata
; /* to hold the data underlying "value" */
876 const char *avdata
; /* to hold the found attribute value data */
879 /* Intern the attribute name as a dom_string so we can
880 * use dom_element_get_attribute() */
881 err
= dom_string_create_interned(
882 (const uint8_t *) lwc_string_data(qname
->name
),
883 lwc_string_length(qname
->name
),
885 if (err
!= DOM_NO_ERR
) {
889 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
890 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
891 /* There was an error getting the attribute's value or
892 * the attribute doesn't exist. So, no match? */
893 dom_string_unref(name
);
898 /* Otherwise, we have the attribute value from the given node
899 * and all we need to do is compare. */
900 dom_string_unref(name
);
901 *match
= dom_string_lwc_isequal(attr_val
, value
);
903 /* Exact match, we're done */
904 dom_string_unref(attr_val
);
908 /* No exact match, try it with a hyphen on the end */
909 vdata
= lwc_string_data(value
); /* needle */
910 vdata_len
= lwc_string_length(value
);
911 avdata
= dom_string_data(attr_val
); /* haystack */
912 avdata_len
= dom_string_byte_length(attr_val
);
913 dom_string_unref(attr_val
);
915 if (avdata_len
> vdata_len
&& avdata
[vdata_len
] == '-') {
916 if (strncasecmp(avdata
, vdata
, vdata_len
) == 0) {
917 /* If there's a hyphen in the right position,
918 * it suffices to compare the strings only up
929 * Test the given node for an attribute whose value is a
930 * space-separated list of words, one of which is the given word
932 * This will return true (via the "match" pointer) if the libdom node
933 * has an attribute with the given name and whose value when
934 * considered as a space-separated list of words contains the given
935 * word. The comparison is case-sensitive. This corresponds to
936 * [attr~=value] in CSS.
938 * \param pw Pointer to the current SVG parser state
939 * \param node Libdom SVG node to test
940 * \param qname Attribute name to check for
941 * \param word Value word to check for
942 * \param match Pointer to the test result
944 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
945 * intern the attribute name (which usually indicates memory
948 css_error
node_has_attribute_includes(void *pw
, void *node
,
949 const css_qname
*qname
, lwc_string
*word
,
955 dom_string
*attr_val
;
957 size_t wordlen
; /* length of "word" */
959 /* pointers used to parse a space-separated list of words */
964 *match
= false; /* default to no match */
966 wordlen
= lwc_string_length(word
);
968 /* In this case, the spec says that "if 'val' is the
969 * empty string, it will never represent anything." */
973 /* Intern the attribute name as a dom_string so we can
974 * use dom_element_get_attribute() */
975 err
= dom_string_create_interned(
976 (const uint8_t *) lwc_string_data(qname
->name
),
977 lwc_string_length(qname
->name
),
979 if (err
!= DOM_NO_ERR
) {
983 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
984 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
985 /* There was an error getting the attribute's value or
986 * the attribute doesn't exist. So, no match? */
987 dom_string_unref(name
);
991 /* Parse the list comparing each word against "word" */
992 start
= dom_string_data(attr_val
);
993 end
= start
+ dom_string_byte_length(attr_val
);
994 dom_string_unref(attr_val
);
996 for (p
= start
; p
<= end
; p
++) {
997 /* Move forward until we find the end of the first word */
998 if (*p
== ' ' || *p
== '\0') {
999 /* If the length of that word is the length of the
1000 * word we're looking for, do the comparison. */
1001 if ((size_t) (p
- start
) == wordlen
&&
1003 lwc_string_data(word
),
1008 /* No match? Set "start" to the beginning of
1009 * the next word and loop. */
1019 * Test the given node for an attribute whose value begins with the
1022 * This will return true (via the "match" pointer) if the libdom node
1023 * has an attribute with the given name and whose value begins with
1024 * the given prefix string. The comparison is case-sensitive. This
1025 * corresponds to [attr^=value] in CSS.
1027 * \param pw Pointer to the current SVG parser state
1028 * \param node Libdom SVG node to test
1029 * \param qname Attribute name to check for
1030 * \param prefix Value prefix to check for
1031 * \param match Pointer to the test result
1033 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1034 * intern the attribute name (which usually indicates memory
1037 css_error
node_has_attribute_prefix(void *pw
, void *node
,
1038 const css_qname
*qname
, lwc_string
*prefix
,
1043 dom_string
*attr_val
;
1045 const char *avdata
; /* attribute value data */
1046 size_t avdata_len
; /* length of that attribute value data */
1047 size_t prefixlen
; /* length of "prefix" */
1049 prefixlen
= lwc_string_length(prefix
);
1050 if (prefixlen
== 0) {
1051 /* In this case, the spec says that "if 'val' is the
1052 * empty string, it will never represent anything." */
1056 /* Intern the attribute name as a dom_string so we can
1057 * use dom_element_get_attribute() */
1058 err
= dom_string_create_interned(
1059 (const uint8_t *) lwc_string_data(qname
->name
),
1060 lwc_string_length(qname
->name
),
1062 if (err
!= DOM_NO_ERR
) {
1066 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
1067 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
1068 /* There was an error getting the attribute's value or
1069 * the attribute doesn't exist. So, no match? */
1070 dom_string_unref(name
);
1075 /* Otherwise, we have the attribute value from the given node,
1076 * and the first thing we want to do is check to see if the
1077 * whole thing matches the prefix. */
1078 dom_string_unref(name
);
1079 *match
= dom_string_lwc_isequal(attr_val
, prefix
);
1081 /* If not, check to see if an, uh, prefix matches the
1083 if (*match
== false) {
1084 avdata
= dom_string_data(attr_val
);
1085 avdata_len
= dom_string_byte_length(attr_val
);
1086 if ((avdata_len
>= prefixlen
) &&
1087 (strncasecmp(avdata
,
1088 lwc_string_data(prefix
),
1090 /* Use strncasecmp to compare only the first
1091 * "n" characters, where "n" is the length of
1097 dom_string_unref(attr_val
);
1104 * Test the given node for an attribute whose value end with the
1107 * This will return true (via the "match" pointer) if the libdom node
1108 * has an attribute with the given name and whose value ends with
1109 * the given suffix string. The comparison is case-sensitive. This
1110 * corresponds to [attr$=value] in CSS.
1112 * \param pw Pointer to the current SVG parser state
1113 * \param node Libdom SVG node to test
1114 * \param qname Attribute name to check for
1115 * \param suffix Value suffix to check for
1116 * \param match Pointer to the test result
1118 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1119 * intern the attribute name (which usually indicates memory
1122 css_error
node_has_attribute_suffix(void *pw
, void *node
,
1123 const css_qname
*qname
, lwc_string
*suffix
,
1128 dom_string
*attr_val
;
1130 const char *avdata
; /* attribute value data */
1131 size_t avdata_len
; /* length of that attribute value data */
1132 size_t suffixlen
; /* length of "suffix" */
1134 /* convenience pointer we'll use when matching the suffix */
1135 const char *suffix_start
;
1137 suffixlen
= lwc_string_length(suffix
);
1138 if (suffixlen
== 0) {
1139 /* In this case, the spec says that "if 'val' is the
1140 * empty string, it will never represent anything." */
1144 /* Intern the attribute name as a dom_string so we can
1145 * use dom_element_get_attribute() */
1146 err
= dom_string_create_interned(
1147 (const uint8_t *) lwc_string_data(qname
->name
),
1148 lwc_string_length(qname
->name
),
1150 if (err
!= DOM_NO_ERR
) {
1154 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
1155 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
1156 /* There was an error getting the attribute's value or
1157 * the attribute doesn't exist. So, no match? */
1158 dom_string_unref(name
);
1163 /* Otherwise, we have the attribute value from the given node,
1164 * and the first thing we want to do is check to see if the
1165 * whole thing matches the suffix. */
1166 dom_string_unref(name
);
1167 *match
= dom_string_lwc_isequal(attr_val
, suffix
);
1169 /* If not, check to see if an, uh, suffix matches the
1171 if (*match
== false) {
1172 avdata
= dom_string_data(attr_val
);
1173 avdata_len
= dom_string_byte_length(attr_val
);
1175 suffix_start
= (char *)(avdata
+ avdata_len
- suffixlen
);
1177 if ((avdata_len
>= suffixlen
) &&
1178 (strncasecmp(suffix_start
,
1179 lwc_string_data(suffix
),
1181 /* Use strncasecmp to compare only the last
1182 * "n" characters, where "n" is the length of
1188 dom_string_unref(attr_val
);
1195 * Implement node_has_attribute_substring() with optional case-
1196 * insensitivity. This corresponds to [attr*=value i] in CSS and is
1197 * not supported by libcss yet, but it allows us to factor out some
1200 static css_error
_node_has_attribute_substring(void *pw
, void *node
,
1201 const css_qname
*qname
, lwc_string
*substring
,
1202 bool *match
, bool insensitive
)
1206 dom_string
*attr_val
= NULL
;
1208 size_t attr_len
; /* length of attr_val */
1209 size_t substrlen
; /* length of "substring" */
1211 /* Convenience pointers we use when comparing substrings */
1215 substrlen
= lwc_string_length(substring
);
1216 if (substrlen
== 0) {
1217 /* In this case, the spec says that "if 'val' is the
1218 * empty string, it will never represent anything." */
1222 /* Intern the attribute name as a dom_string so we can
1223 * use dom_element_get_attribute() */
1224 err
= dom_string_create_interned(
1225 (const uint8_t *) lwc_string_data(qname
->name
),
1226 lwc_string_length(qname
->name
),
1228 if (err
!= DOM_NO_ERR
) {
1232 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
1233 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
1234 /* There was an error getting the attribute's value or
1235 * the attribute doesn't exist. So, no match? */
1236 dom_string_unref(name
);
1241 /* Otherwise, we have the attribute value from the given node,
1242 * and the first thing we want to do is check to see if the
1243 * whole thing matches the substring. */
1244 dom_string_unref(name
);
1247 *match
= dom_string_caseless_lwc_isequal(attr_val
, substring
);
1250 *match
= dom_string_lwc_isequal(attr_val
, substring
);
1253 /* If not, check to see if an, uh, substring matches the
1255 if (*match
== false) {
1256 p
= dom_string_data(attr_val
);
1258 /* Check every long-enough suffix for a prefix match */
1259 attr_len
= dom_string_byte_length(attr_val
);
1260 if (attr_len
>= substrlen
) {
1261 p_max
= p
+ attr_len
- substrlen
;
1262 while (p
<= p_max
) {
1264 lwc_string_data(substring
),
1274 dom_string_unref(attr_val
);
1280 * Test the given node for an attribute whose value contains the
1283 * This will return true (via the "match" pointer) if the libdom node
1284 * has an attribute with the given name and whose value contains the
1285 * given substring. The comparison is case-sensitive. This corresponds
1286 * to [attr*=value] in CSS.
1288 * \param pw Pointer to the current SVG parser state
1289 * \param node Libdom SVG node to test
1290 * \param qname Attribute name to check for
1291 * \param substring Value substring to check for
1292 * \param match Pointer to the test result
1294 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1295 * intern the attribute name (which usually indicates memory
1298 css_error
node_has_attribute_substring(void *pw
, void *node
,
1299 const css_qname
*qname
, lwc_string
*substring
,
1302 return _node_has_attribute_substring(pw
, node
, qname
, substring
,
1308 * Test whether or not the given node is the document's root element
1309 * This corresponds to the CSS :root pseudo-selector.
1311 * \param pw Pointer to the current SVG parser state
1312 * \param node Libdom SVG node to test
1313 * \param match Pointer to the test result
1315 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1317 css_error
node_is_root(void *pw
, void *node
, bool *match
)
1324 err
= dom_node_get_parent_node((dom_node
*)node
, &parent
);
1325 if (err
!= DOM_NO_ERR
) {
1329 /* It's the root element if it doesn't have a parent element */
1330 if (parent
!= NULL
) {
1331 err
= dom_node_get_node_type(parent
, &type
);
1332 dom_node_unref(parent
);
1333 if (err
!= DOM_NO_ERR
) {
1336 if (type
!= DOM_DOCUMENT_NODE
) {
1337 /* DOM_DOCUMENT_NODE is the only allowable
1338 * type of parent node for the root element */
1350 * Used internally in node_count_siblings() to "count" the given
1351 * sibling node. It factors out the node type and name checks.
1353 static int node_count_siblings_check(dom_node
*dnode
,
1360 dom_string
*dnode_name
;
1362 /* We flip this to 1 if/when we count this node */
1365 if (dnode
== NULL
) {
1369 exc
= dom_node_get_node_type(dnode
, &type
);
1370 if ((exc
!= DOM_NO_ERR
) || (type
!= DOM_ELEMENT_NODE
)) {
1371 /* We only count element siblings */
1375 /* ... with the right name */
1378 exc
= dom_node_get_node_name(dnode
, &dnode_name
);
1380 if ((exc
== DOM_NO_ERR
) && (dnode_name
!= NULL
)) {
1381 if (dom_string_isequal(name
,
1385 dom_string_unref(dnode_name
);
1396 * Count the given node's sibling elements
1398 * This counts the given node's sibling elements in one direction,
1399 * either forwards or backwards, in the DOM. Keep in mind that the
1400 * libdom tree is upside-down compared to the CSS one; so "next" and
1401 * "previous" are actually reversed; the default is to count preceding
1402 * libdom siblings which correspond to subsequent CSS siblings.
1404 * This operation is central to the CSS :first-child, :nth-child, and
1405 * :last-child (et cetera) pseudo-selectors.
1407 * If same_name is true, then only nodes having the same
1408 * (case-sensitive) name as the given node are counted.
1410 * \param pw Pointer to the current SVG parser state
1411 * \param node Libdom SVG node whose siblings we're counting
1412 * \param same_name Whether or not to count only siblings having
1413 * the same name as the given node
1414 * \param after Count subsequent siblings rather than precedent
1415 * ones (the default)
1416 * \param count Pointer to the return value, the number of sibling
1419 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1421 css_error
node_count_siblings(void *pw
, void *node
,
1422 bool same_name
, bool after
, int32_t *count
)
1426 dom_node
*dnode
; /* node, but with the right type */
1427 dom_string
*dnode_name
;
1428 dom_node
*next
; /* "next" sibling (depends on direction) */
1430 /* Pointer to the "next sibling" function */
1431 dom_exception (*next_func
)(dom_node
*, dom_node
**);
1436 dnode
= (dom_node
*)node
;
1438 exc
= dom_node_get_node_name(dnode
, &dnode_name
);
1439 if ((exc
!= DOM_NO_ERR
) || (dnode_name
== NULL
)) {
1444 /* Increment the reference counter for dnode for as long as
1445 * we retain a reference to it. */
1446 dnode
= dom_node_ref(dnode
);
1448 next_func
= dom_node_get_previous_sibling
;
1450 next_func
= dom_node_get_next_sibling
;
1454 exc
= next_func(dnode
, &next
);
1455 if (exc
!= DOM_NO_ERR
) {
1459 /* If next_func worked, we're about to swap "next"
1460 * with "dnode" meaning that we will no longer retain
1461 * a reference to the current dnode. */
1462 dom_node_unref(dnode
);
1465 *count
+= node_count_siblings_check(dnode
,
1468 } while (dnode
!= NULL
);
1470 if (dnode_name
!= NULL
) {
1471 dom_string_unref(dnode_name
);
1479 * Determine whether or not the given element is empty
1481 * An element is "nonempty" if it has a child that is either an
1482 * element node or a text node.
1484 * \param pw Pointer to the current SVG parser state
1485 * \param node Libdom SVG node to check for emptiness
1486 * \param is_empty Pointer to the return value
1488 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1490 css_error
node_is_empty(void *pw
, void *node
, bool *is_empty
)
1493 dom_node
*child
; /* current child node pointer */
1494 dom_node
*next
; /* next child node pointer */
1495 dom_node_type type
; /* what type of node is "child" */
1498 /* Assume that it's empty by default */
1501 /* Get the given node's first child. Implementation detail:
1502 * this increments the reference counter on the child node. */
1503 err
= dom_node_get_first_child((dom_node
*)node
, &child
);
1504 if (err
!= DOM_NO_ERR
) {
1508 /* And now loop through all children looking for a
1509 * text/element node. If we find one, the original
1510 * node is "nonempty" */
1511 while (child
!= NULL
) {
1512 err
= dom_node_get_node_type(child
, &type
);
1513 if (err
!= DOM_NO_ERR
) {
1514 dom_node_unref(child
);
1518 if (type
== DOM_ELEMENT_NODE
|| type
== DOM_TEXT_NODE
) {
1520 dom_node_unref(child
);
1524 err
= dom_node_get_next_sibling(child
, &next
);
1525 if (err
!= DOM_NO_ERR
) {
1526 dom_node_unref(child
);
1530 /* If we're moving to the next node, we can release
1531 * the reference to the current one */
1532 dom_node_unref(child
);
1541 * Determine whether or not the given node is a link
1543 * A node is a link if it is an element node whose name is "a" and if
1544 * it has an "href" attribute (case-sensitive). This selector
1545 * corresponds to node:link pseudo-class in CSS.
1547 * This pseudo-class is a bit awkward because the two standards (HTML5
1548 * and CSS) disagree on what it means, and because libsvgtiny does not
1549 * have enough information to determine if a link has been "visited"
1550 * yet -- that's a UI property. CSS says that :link is for unvisited
1551 * links, which we can't determine. HTML5 says that each link must
1552 * be either a :link or :visited. Since we can't decide either way,
1553 * It seems less wrong to declare that all links are unvisited; i.e.
1554 * that they match :link.
1556 * \param pw Pointer to the current SVG parser state
1557 * \param node Libdom SVG node to check
1558 * \param is_link Pointer to the boolean return value
1560 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1562 css_error
node_is_link(void *pw
, void *node
, bool *is_link
)
1565 dom_node
*dnode
; /* node, but with the right type */
1566 dom_string
*dnode_name
;
1568 struct svgtiny_parse_state
* state
;
1570 dnode
= (dom_node
*)node
;
1572 has_href
= false; /* assume no href attribute */
1573 *is_link
= false; /* assume that it's not a link */
1575 exc
= dom_node_get_node_name(dnode
, &dnode_name
);
1576 if ((exc
!= DOM_NO_ERR
) || (dnode_name
== NULL
)) {
1580 state
= (struct svgtiny_parse_state
*)pw
;
1581 if (dom_string_isequal(dnode_name
, state
->interned_a
)) {
1582 exc
= dom_element_has_attribute(node
,
1583 state
->interned_href
,
1585 if (exc
== DOM_NO_ERR
&& has_href
) {
1590 dom_string_unref(dnode_name
);
1595 * Check if the given node is a link that has been visited already
1597 * This check always fails because the SVG DOM does not have the
1598 * necessary information (it's a UI property).
1600 * \param pw Pointer to the current SVG parser state; unused
1601 * \param node Libdom SVG node to check; unused
1602 * \param is_visited Pointer to the boolean return value
1604 * \return Always returns CSS_OK
1606 css_error
node_is_visited(void *pw
, void *node
, bool *is_visited
)
1610 *is_visited
= false;
1616 * Check if the given node is being "hovered" over
1618 * This check always fails because the SVG DOM does not have the
1619 * necessary information (it's a UI property).
1621 * \param pw Pointer to the current SVG parser state; unused
1622 * \param node Libdom SVG node to check; unused
1623 * \param is_hover Pointer to the boolean return value
1625 * \return Always returns CSS_OK
1627 css_error
node_is_hover(void *pw
, void *node
, bool *is_hover
)
1637 * Check if the given node is "active"
1639 * This check always fails because the SVG DOM does not have the
1640 * necessary information (it's a UI property).
1642 * \param pw Pointer to the current SVG parser state; unused
1643 * \param node Libdom SVG node to check; unused
1644 * \param is_active Pointer to the boolean return value
1646 * \return Always returns CSS_OK
1648 css_error
node_is_active(void *pw
, void *node
, bool *is_active
)
1658 * Check if the given node has the focus
1660 * This check always fails because the SVG DOM does not have the
1661 * necessary information (it's a UI property).
1663 * \param pw Pointer to the current SVG parser state; unused
1664 * \param node Libdom SVG node to check; unused
1665 * \param is_focus Pointer to the boolean return value
1667 * \return Always returns CSS_OK
1669 css_error
node_is_focus(void *pw
, void *node
, bool *is_focus
)
1679 * Check if the given node is enabled
1681 * This check always fails because the SVG DOM does not have the
1682 * necessary information (it's a UI property).
1684 * \param pw Pointer to the current SVG parser state; unused
1685 * \param node Libdom SVG node to check; unused
1686 * \param is_enabled Pointer to the boolean return value
1688 * \return Always returns CSS_OK
1690 css_error
node_is_enabled(void *pw
, void *node
, bool *is_enabled
)
1694 *is_enabled
= false;
1700 * Check if the given node is disabled
1702 * This check always fails because the SVG DOM does not have the
1703 * necessary information (it's a UI property). Beware, until they are
1704 * implemented, this is NOT the logical negation of node_is_enabled!
1706 * \param pw Pointer to the current SVG parser state; unused
1707 * \param node Libdom SVG node to check; unused
1708 * \param is_disabled Pointer to the boolean return value
1710 * \return Always returns CSS_OK
1712 css_error
node_is_disabled(void *pw
, void *node
, bool *is_disabled
)
1716 *is_disabled
= false;
1722 * Test whether or not the given node is "checked"
1724 * This test always fails because the SVG DOM does not have the
1725 * necessary information (it's a UI property).
1727 * \param pw Pointer to the current SVG parser state; unused
1728 * \param node Libdom SVG node to check; unused
1729 * \param is_checked Pointer to the boolean return value
1731 * \return Always returns CSS_OK
1733 css_error
node_is_checked(void *pw
, void *node
, bool *is_checked
)
1737 *is_checked
= false;
1743 * Check if the given node is the "target" of the document URL
1745 * This test always fails because the SVG DOM does not have the
1746 * necessary information (it's a UI property).
1748 * \param pw Pointer to the current SVG parser state; unused
1749 * \param node Libdom SVG node to check; unused
1750 * \param is_target Pointer to the boolean return value
1752 * \return Always returns CSS_OK
1754 css_error
node_is_target(void *pw
, void *node
, bool *is_target
)
1764 * Check if the given node is the given language
1766 * This test is corresponds to the CSS :lang() selector and is not
1767 * fully implemented yet: it looks only for "lang" attributes on the
1768 * given element and its parents, and performs a simple substring
1769 * check. This results in a partial implementation of CSS Level 3 for
1770 * SVG 2.0. In particular, it ignores all "xml:lang" attributes in
1771 * favor of the "lang" attribute that is defined only in SVG 2.0.
1773 * \param pw Pointer to the current SVG parser state; unused
1774 * \param node Libdom SVG node to check
1775 * \param lang The language to match
1776 * \param is_lang Pointer to the boolean return value
1778 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1780 static css_error
node_is_lang(void *pw
, void *node
,
1781 lwc_string
*lang
, bool *is_lang
)
1784 /* SVG2 elements support both "lang" and "xml:lang"
1785 * attributes; earlier versions have only the XML
1786 * attribute. It would not be too hard to add support for
1787 * xml:lang" here. The main difficulty standing in the way of
1788 * a full Level 4 implementation is the complexity of the
1791 * https://www.w3.org/TR/selectors-4/#the-lang-pseudo
1796 dom_exception d_err
;
1797 dom_node
*n
; /* current node */
1798 dom_node
*p
; /* parent node */
1799 bool match
; /* retval from node_has_attribute_substring() */
1801 /* Define the attribute name "lang" that we're looking for.
1802 * We only use a css_qname here because that's what the
1803 * node_has_attribute_substring() takes; the namespace
1804 * portion of it is irrelevant. */
1808 if (lwc_intern_string("lang", 4, &attr
.name
) != lwc_error_ok
) {
1812 *is_lang
= false; /* default to no match */
1813 n
= (dom_node
*)node
;
1815 /* Loop through all parents of the given node looking for a
1816 * substring match */
1818 c_err
= _node_has_attribute_substring(pw
, (void *)n
, &attr
,
1819 lang
, &match
, true);
1820 if (c_err
!= CSS_OK
) {
1821 lwc_string_destroy(attr
.name
);
1825 /* matched this element; we're done */
1826 lwc_string_destroy(attr
.name
);
1831 /* no match on this element, try its parent */
1832 d_err
= dom_node_get_parent_node(n
, &p
);
1833 if (d_err
!= DOM_NO_ERR
) {
1834 lwc_string_destroy(attr
.name
);
1840 /* If we never find a match we may wind up here */
1841 lwc_string_destroy(attr
.name
);
1847 * Return presentational hints for the given node
1849 * Unless an SVG is being rendered from within an HTML document,
1850 * there are no presentational hints. We always return an empty
1853 * \param pw Pointer to the current SVG parser state; unused
1854 * \param node Libdom SVG node whose hints we want; unused
1855 * \param nhints How many hints are returned (return by reference)
1856 * \param hints Array of css_hint structures (return by reference)
1858 * \return Always returns CSS_OK
1860 css_error
node_presentational_hint(void *pw
, void *node
,
1861 uint32_t *nhints
, css_hint
**hints
)
1872 * User-agent defaults for CSS properties
1874 * Ideally we would provide _no_ defaults here, because we don't yet
1875 * support any CSS properties that can use them. However, we run into
1876 * libcss parent/child style composition issues unless these defaults
1877 * are provided. And it's harmless to provide them, so let's do it.
1879 * \param pw Pointer to the current SVG parser state; unused
1880 * \param property LibCSS property identifier; unused
1881 * \param hint Pointer to hint object (a return value); unused
1883 * \return Always returns CSS_INVALID
1885 css_error
ua_default_for_property(void *pw
, uint32_t property
,
1890 case CSS_PROP_COLOR
:
1891 hint
->data
.color
= 0xff000000;
1892 hint
->status
= CSS_COLOR_COLOR
;
1894 case CSS_PROP_FONT_FAMILY
:
1895 hint
->data
.strings
= NULL
;
1896 hint
->status
= CSS_FONT_FAMILY_SANS_SERIF
;
1898 case CSS_PROP_QUOTES
:
1899 hint
->data
.strings
= NULL
;
1900 hint
->status
= CSS_QUOTES_NONE
;
1902 case CSS_PROP_VOICE_FAMILY
:
1903 hint
->data
.strings
= NULL
;
1915 * A "user handler" function that we pass to dom_node_set_user_data()
1916 * in set_libcss_node_data(). The set_libcss_node_data() documentation
1917 * says that if the node (on which data is set) is is deleted or
1918 * cloned, or if its ancestors are modified, then we must call
1919 * css_libcss_node_data_handler() on the user data. This function
1920 * basically just checks to see which DOM event has happened and
1921 * calls css_libcss_node_data_handler() when any of those criteria
1924 static void svgtiny_dom_user_data_handler(dom_node_operation operation
,
1925 dom_string
*key
, void *data
, struct dom_node
*src
,
1926 struct dom_node
*dst
)
1928 /* We only care about the userdata that is identified by our
1929 * userdata key. Unfortunately I see no obvious way to obtain
1930 * the copy of the userdata key that is already interned in
1931 * svgtiny_strings.h; so we duplicate it here (ugh). */
1933 dom_string_create((const uint8_t *)"_libcss_user_data", 17, &str
);
1934 if (dom_string_isequal(str
,key
) == false || data
== NULL
) {
1935 /* Wrong key, or no data */
1939 /* Check the DOM operation, and make the corresponding call to
1940 * css_libcss_node_data_handler(). No error handling is done.
1942 switch (operation
) {
1943 case DOM_NODE_CLONED
:
1944 css_libcss_node_data_handler(&svgtiny_select_handler
,
1946 NULL
, src
, dst
, data
);
1948 case DOM_NODE_RENAMED
:
1949 css_libcss_node_data_handler(&svgtiny_select_handler
,
1951 NULL
, src
, NULL
, data
);
1953 case DOM_NODE_IMPORTED
:
1954 case DOM_NODE_ADOPTED
:
1955 case DOM_NODE_DELETED
:
1956 css_libcss_node_data_handler(&svgtiny_select_handler
,
1958 NULL
, src
, NULL
, data
);
1961 /* Our list of cases should have been exhaustive */
1967 * Store libcss data on a node
1969 * This is part of the libcss select handler API that we need to
1970 * implement. It is essentially a thin dom_node_set_user_data()
1973 * \param pw Pointer to the current SVG parser state
1974 * \param node Libdom SVG node on which to store the data
1975 * \param libcss_node_data Pointer to the data to store
1977 * \return Always returns CSS_OK
1979 css_error
set_libcss_node_data(void *pw
, void *node
,
1980 void *libcss_node_data
)
1982 struct svgtiny_parse_state
*state
;
1985 /* A unique "userdata key" (a string) is used to identify this
1987 state
= (struct svgtiny_parse_state
*)pw
;
1988 dom_node_set_user_data((dom_node
*)node
,
1989 state
->interned_userdata_key
,
1991 svgtiny_dom_user_data_handler
,
1994 /* dom_node_set_user_data() always returns DOM_NO_ERR */
2000 * Retrieve libcss data from a node
2002 * This is part of the libcss select handler API that we need to
2003 * implement. It is essentially a thin dom_node_get_user_data()
2006 * \param pw Pointer to the current SVG parser state
2007 * \param node Libdom SVG node from which to get the data
2008 * \param libcss_node_data Address at which to store a pointer to the data
2010 * \return Always returns CSS_OK
2012 css_error
get_libcss_node_data(void *pw
, void *node
,
2013 void **libcss_node_data
)
2015 struct svgtiny_parse_state
*state
;
2017 /* A unique "userdata key" (a string) is used to identify this
2019 state
= (struct svgtiny_parse_state
*)pw
;
2020 dom_node_get_user_data((dom_node
*)node
,
2021 state
->interned_userdata_key
,
2024 /* dom_node_get_user_data() always returns DOM_NO_ERR */
2030 * The vtable of select handler callbacks passed by libsvgtiny to
2031 * css_select_style().
2033 static css_select_handler svgtiny_select_handler
= {
2034 CSS_SELECT_HANDLER_VERSION_1
,
2038 named_ancestor_node
,
2041 named_generic_sibling_node
,
2048 node_has_attribute_equal
,
2049 node_has_attribute_dashmatch
,
2050 node_has_attribute_includes
,
2051 node_has_attribute_prefix
,
2052 node_has_attribute_suffix
,
2053 node_has_attribute_substring
,
2055 node_count_siblings
,
2067 node_presentational_hint
,
2068 ua_default_for_property
,
2069 set_libcss_node_data
,
2070 get_libcss_node_data
,