1 #include <libcss/libcss.h>
2 #include <strings.h> /* strncasecmp */
5 #include "svgtiny_internal.h"
7 static css_error
node_name(void *pw
, void *node
, css_qname
*qname
);
8 static css_error
node_classes(void *pw
, void *node
,
9 lwc_string
***classes
, uint32_t *n_classes
);
10 static css_error
node_id(void *pw
, void *node
, lwc_string
**id
);
11 static css_error
named_parent_node(void *pw
, void *node
,
12 const css_qname
*qname
, void **parent
);
13 static css_error
named_sibling_node(void *pw
, void *node
,
14 const css_qname
*qname
, void **sibling
);
15 static css_error
named_generic_sibling_node(void *pw
, void *node
,
16 const css_qname
*qname
, void **sibling
);
17 static css_error
parent_node(void *pw
, void *node
, void **parent
);
18 static css_error
sibling_node(void *pw
, void *node
, void **sibling
);
19 static css_error
node_has_name(void *pw
, void *node
,
20 const css_qname
*qname
, bool *match
);
21 static css_error
node_has_class(void *pw
, void *node
,
22 lwc_string
*name
, bool *match
);
23 static css_error
node_has_id(void *pw
, void *node
,
24 lwc_string
*name
, bool *match
);
25 static css_error
node_has_attribute(void *pw
, void *node
,
26 const css_qname
*qname
, bool *match
);
27 static css_error
node_has_attribute_equal(void *pw
, void *node
,
28 const css_qname
*qname
, lwc_string
*value
,
30 static css_error
node_has_attribute_dashmatch(void *pw
, void *node
,
31 const css_qname
*qname
, lwc_string
*value
,
33 static css_error
node_has_attribute_includes(void *pw
, void *node
,
34 const css_qname
*qname
, lwc_string
*word
,
36 static css_error
node_has_attribute_prefix(void *pw
, void *node
,
37 const css_qname
*qname
, lwc_string
*prefix
,
39 static css_error
node_has_attribute_suffix(void *pw
, void *node
,
40 const css_qname
*qname
, lwc_string
*suffix
,
42 static css_error
node_has_attribute_substring(void *pw
, void *node
,
43 const css_qname
*qname
, lwc_string
*substring
,
45 static css_error
node_is_root(void *pw
, void *node
, bool *match
);
46 static css_error
node_count_siblings(void *pw
, void *node
,
47 bool same_name
, bool after
, int32_t *count
);
48 static css_error
node_is_empty(void *pw
, void *node
, bool *is_empty
);
52 * Resolve a relative URL to an absolute one by doing nothing. This is
53 * the simplest possible implementation of a URL resolver, needed for
56 css_error
svgtiny_resolve_url(void *pw
,
57 const char *base
, lwc_string
*rel
, lwc_string
**abs
)
62 /* Copy the relative URL to the absolute one (the return
64 *abs
= lwc_string_ref(rel
);
69 * Create a stylesheet with the default set of params.
71 * \param sheet A stylesheet pointer, passed in by reference, that
72 * we use to store the newly-created stylesheet.
73 * \param inline_style True if this stylesheet represents an inline
74 * style, and false otherwise.
76 * \return The return value from css_stylesheet_create() is returned.
78 css_error
svgtiny_create_stylesheet(css_stylesheet
**sheet
,
81 css_stylesheet_params params
;
83 params
.params_version
= CSS_STYLESHEET_PARAMS_VERSION_1
;
84 params
.level
= CSS_LEVEL_DEFAULT
;
85 params
.charset
= NULL
;
88 params
.allow_quirks
= false;
89 params
.inline_style
= inline_style
;
90 params
.resolve
= svgtiny_resolve_url
;
91 params
.resolve_pw
= NULL
;
93 params
.import_pw
= NULL
;
95 params
.color_pw
= NULL
;
97 params
.font_pw
= NULL
;
99 return css_stylesheet_create(¶ms
, sheet
);
103 /**************************/
104 /* libcss select handlers */
105 /**************************/
107 * From here on we implement the "select handler "API defined in
108 * libcss's include/libcss/select.h and discussed briefly in its
114 * Retrieve the given node's name
116 * \param pw Pointer to the current SVG parser state
117 * \param node Libdom SVG node
118 * \param qname Address at which to store the node name
120 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
122 css_error
node_name(void *pw
, void *node
, css_qname
*qname
)
126 struct svgtiny_parse_state
*state
;
128 err
= dom_node_get_node_name((dom_node
*)node
, &name
);
129 if (err
!= DOM_NO_ERR
) {
133 state
= (struct svgtiny_parse_state
*)pw
;
134 qname
->ns
= lwc_string_ref(state
->interned_svg_xmlns
);
136 err
= dom_string_intern(name
, &qname
->name
);
137 if (err
!= DOM_NO_ERR
) {
138 dom_string_unref(name
);
142 dom_string_unref(name
);
149 * Retrieve the given node's classes
151 * \param pw Pointer to the current SVG parser state
152 * \param node Libdom SVG node
153 * \param classes Address at which to store the class name array
154 * \param n_classes Address at which to store the length of the class
157 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
159 * \note CSS_NOMEM is not possible in practice as of libdom-0.4.1,
160 * because the underlying libdom function never fails
162 css_error
node_classes(void *pw
, void *node
,
163 lwc_string
***classes
, uint32_t *n_classes
)
168 err
= dom_element_get_classes((dom_node
*)node
, classes
, n_classes
);
170 /* The implementation does not do it, but the documentation
171 for dom_element_get_classes() says that a DOM_NO_MEM_ERR is
172 possible here, so we handle it to be on the safe side. */
173 if (err
!= DOM_NO_ERR
) {
182 * Retrieve the given node's id
184 * \param pw Pointer to the current SVG parser state
185 * \param node Libdom SVG node
186 * \param id Address at which to store the id
188 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
190 css_error
node_id(void *pw
, void *node
, lwc_string
**id
)
194 struct svgtiny_parse_state
*state
;
196 /* Begin with the assumption that this node has no id */
199 state
= (struct svgtiny_parse_state
*)pw
;
200 err
= dom_element_get_attribute((dom_node
*)node
,
201 state
->interned_id
, &attr
);
202 if (err
!= DOM_NO_ERR
) {
205 else if (attr
== NULL
) {
206 /* The node has no id attribute and our return value
207 is already set to NULL so we're done */
211 /* If we found an id attribute (a dom_string), intern it into
212 an lwc_string that we can return, and then cleanup the
214 err
= dom_string_intern(attr
, id
);
215 if (err
!= DOM_NO_ERR
) {
216 dom_string_unref(attr
);
219 dom_string_unref(attr
);
226 * Find the first parent of the given element having the given name
228 * \param pw Pointer to the current SVG parser state
229 * \param node Libdom SVG node
230 * \param qname Name of the parent node to search for
231 * \param parent Address at which to store the parent node pointer
233 * \return Always returns CSS_OK
235 * \post If a suitable element is found, a pointer to it will be
236 * stored at the address pointed to by \a parent; otherwise,
237 * NULL will be stored at the address pointed to by \a parent
239 css_error
named_parent_node(void *pw
, void *node
,
240 const css_qname
*qname
, void **parent
)
243 /* dom_element_named_parent_node() was invented to implement
244 * this select handler so there isn't much for us to do except
245 * call it. It's OK if node isn't an element, libdom checks
247 dom_element_named_parent_node((dom_element
*)node
,
249 (struct dom_element
**)parent
);
251 /* Implementation detail: dom_element_named_parent_node()
252 * increments the reference count of the parent element before
253 * returning it to us. According to docs/RefCnt in the libdom
254 * repository, this will prevent the parent element from being
255 * destroyed if it is pruned from the DOM. That sounds good,
256 * since we don't want to be using a pointer to an object that
257 * has been destroyed... but we also have no way of later
258 * decrementing the reference count ourselves, and don't want
259 * to make the returned node eternal. Decrementing the
260 * reference counter now allows it to be destroyed when the
261 * DOM no longer needs it, and so long as no other parts of
262 * libsvgtiny are messing with the DOM during parsing, that
263 * shouldn't (ha ha) cause any problems. */
264 dom_node_unref(*parent
);
271 * Find the "next-sibling" of the given element having the given name
273 * This search corresponds to the "+ foo" combinator in CSS and will
274 * find only "foo" element nodes that immediately precede the given
275 * node under the same parent in the DOM. In CSS the tree is viewed
276 * top-down and in libdom it is viewed from the bottom-up; as a result
277 * "next" and "previous" are sometimes backwards. This is case-sensitive.
279 * \param pw Pointer to the current SVG parser state
280 * \param node Libdom SVG node
281 * \param qname Name of the sibling node to search for
282 * \param sibling Address at which to store the sibling node pointer
284 * \return Always returns CSS_OK
286 * \post If a suitable element is found, a pointer to it will be
287 * stored at the address pointed to by \a sibling; otherwise,
288 * NULL will be stored at the address pointed to by \a sibling
290 css_error
named_sibling_node(void *pw
, void *node
,
291 const css_qname
*qname
, void **sibling
)
294 dom_node
*n
= node
; /* the current node */
295 dom_node
*prev
; /* the previous node */
300 *sibling
= NULL
; /* default to nothing found */
302 /* Begin the search; the first iteration we do outside of the
303 * loop. Implementation detil: dom_node_get_previous_sibling()
304 * increments the reference counter on the returned node. A
305 * comment within named_parent_node() explains why we
306 * decrement it ASAP. */
307 err
= dom_node_get_previous_sibling(n
, &n
);
308 if (err
!= DOM_NO_ERR
) {
313 /* We're looking for the first ELEMENT sibling */
314 err
= dom_node_get_node_type(n
, &type
);
315 if (err
!= DOM_NO_ERR
) {
320 if (type
== DOM_ELEMENT_NODE
) {
321 /* We found an element node, does it have the
323 err
= dom_node_get_node_name(n
, &name
);
324 if (err
!= DOM_NO_ERR
) {
329 if (dom_string_lwc_isequal(name
,
331 /* The name is right, return it */
335 /* There's only one next-sibling element node
336 * and we've already found it, so if its name
337 * wasn't right, we return the default value
339 dom_string_unref(name
);
344 /* Not an element node, so we move on the the previous
345 * previous sibling */
346 err
= dom_node_get_previous_sibling(n
, &prev
);
347 if (err
!= DOM_NO_ERR
) {
361 * Find the first "subsequent-sibling" of the given element having the
364 * This search corresponds to the "~ foo" combinator in CSS and will
365 * find only "foo" element nodes that precede the given node (under
366 * the same parent) in the DOM. In CSS the tree is viewed top-down and
367 * in libdom it is viewed from the bottom-up; as a result "next" and
368 * "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_generic_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 */
392 *sibling
= NULL
; /* default to nothing found */
394 /* Begin the search; the first iteration we do outside of the
395 * loop. Implementation detil: dom_node_get_previous_sibling()
396 * increments the reference counter on the returned node. A
397 * comment within named_parent_node() explains why we
398 * decrement it ASAP. */
399 err
= dom_node_get_previous_sibling(n
, &n
);
400 if (err
!= DOM_NO_ERR
) {
405 err
= dom_node_get_node_type(n
, &type
);
406 if (err
!= DOM_NO_ERR
) {
411 if (type
== DOM_ELEMENT_NODE
) {
412 /* We only want ELEMENT nodes */
413 err
= dom_node_get_node_name(n
, &name
);
414 if (err
!= DOM_NO_ERR
) {
419 if (dom_string_lwc_isequal(name
,
421 /* Found one. Save it and stop the search */
422 dom_string_unref(name
);
428 dom_string_unref(name
);
431 /* This sibling wasn't an element with the desired
432 name, so move on to the previous sibling */
433 err
= dom_node_get_previous_sibling(n
, &prev
);
434 if (err
!= DOM_NO_ERR
) {
448 * Return a pointer to the given node's parent
450 * \param pw Pointer to the current SVG parser state
451 * \param node Libdom SVG node
452 * \param parent Address at which to store the node's parent pointer
454 * \return Always returns CSS_OK
456 css_error
parent_node(void *pw
, void *node
, void **parent
)
459 /* Libdom basically implements this for us */
460 dom_element_parent_node(node
, (struct dom_element
**)parent
);
462 /* See the comment in named_parent_node() for why we decrement
463 * this reference counter here. */
464 dom_node_unref(*parent
);
471 * Find the "next-sibling" of the given element
473 * This search corresponds "+ *" in CSS and will find the first
474 * element node that immediately precedes the given node under the
475 * same parent in the DOM. In CSS the tree is viewed top-down and in
476 * libdom it is viewed from the bottom-up; as a result "next" and
477 * "previous" are sometimes backwards.
479 * \param pw Pointer to the current SVG parser state
480 * \param node Libdom SVG node
481 * \param sibling Address at which to store the sibling node pointer
483 * \return Always returns CSS_OK
485 * \post If a suitable element is found, a pointer to it will be
486 * stored at the address pointed to by \a sibling; otherwise,
487 * NULL will be stored at the address pointed to by \a sibling
489 css_error
sibling_node(void *pw
, void *node
, void **sibling
)
492 dom_node
*n
= node
; /* the current node */
493 dom_node
*prev
; /* the previous node */
497 *sibling
= NULL
; /* default to nothing found */
499 /* Begin the search; the first iteration we do outside of the
500 * loop. Implementation detil: dom_node_get_previous_sibling()
501 * increments the reference counter on the returned node. A
502 * comment within named_parent_node() explains why we
503 * decrement it ASAP. */
504 err
= dom_node_get_previous_sibling(n
, &n
);
505 if (err
!= DOM_NO_ERR
) {
510 err
= dom_node_get_node_type(n
, &type
);
511 if (err
!= DOM_NO_ERR
) {
516 if (type
== DOM_ELEMENT_NODE
) {
517 /* We found a sibling node that is also an
518 element and that's all we wanted. */
524 /* This sibling node was not an element; move on to
525 the previous sibling */
526 err
= dom_node_get_previous_sibling(n
, &prev
);
527 if (err
!= DOM_NO_ERR
) {
541 * Test the given node for the given name
543 * This will return true (via the "match" pointer) if the libdom node
544 * has the given name or if that name is the universal selector;
545 * otherwise it returns false. The comparison is case-sensitive. It
546 * corresponds to a rule like "body { ... }" in CSS.
548 * \param pw Pointer to the current SVG parser state
549 * \param node Libdom SVG node to test
550 * \param qname Name to check for
551 * \param match Pointer to the test result
553 * \return Always returns CSS_OK
555 css_error
node_has_name(void *pw
, void *node
,
556 const css_qname
*qname
, bool *match
)
558 struct svgtiny_parse_state
*state
;
562 /* Start by checking to see if qname is the universal selector */
563 state
= (struct svgtiny_parse_state
*)pw
;
564 if (lwc_string_isequal(qname
->name
,
565 state
->interned_universal
, match
) == lwc_error_ok
) {
567 /* It's the universal selector. In NetSurf, all node
568 * names match the universal selector, and nothing in
569 * the libcss documentation suggests another approach,
570 * so we follow NetSurf here. */
575 err
= dom_node_get_node_name((dom_node
*)node
, &name
);
576 if (err
!= DOM_NO_ERR
) {
580 /* Unlike with HTML, SVG element names are case-sensitive */
581 *match
= dom_string_lwc_isequal(name
, qname
->name
);
582 dom_string_unref(name
);
589 * Test the given node for the given class
591 * This will return true (via the "match" pointer) if the libdom node
592 * has the given class. The comparison is case-sensitive. It
593 * corresponds to node.class in CSS.
595 * \param pw Pointer to the current SVG parser state
596 * \param node Libdom SVG node to test
597 * \param name Class name to check for
598 * \param match Pointer to the test result
600 * \return Always returns CSS_OK
602 css_error
node_has_class(void *pw
, void *node
,
603 lwc_string
*name
, bool *match
)
606 /* libdom implements this for us and apparently it cannot fail */
607 dom_element_has_class((dom_node
*)node
, name
, match
);
613 * Test the given node for the given id
615 * This will return true (via the "match" pointer) if the libdom node
616 * has the given id. The comparison is case-sensitive. It corresponds
619 * \param pw Pointer to the current SVG parser state
620 * \param node Libdom SVG node to test
621 * \param name Id to check for
622 * \param match Pointer to the test result
624 * \return Always returns CSS_OK
626 css_error
node_has_id(void *pw
, void *node
,
627 lwc_string
*name
, bool *match
)
631 struct svgtiny_parse_state
*state
;
633 attr
= NULL
; /* a priori the "id" attribute may not exist */
634 *match
= false; /* default to no match */
636 state
= (struct svgtiny_parse_state
*)pw
;
637 err
= dom_element_get_attribute((dom_node
*)node
,
638 state
->interned_id
, &attr
);
639 if (err
!= DOM_NO_ERR
|| attr
== NULL
) {
643 *match
= dom_string_lwc_isequal(attr
, name
);
644 dom_string_unref(attr
);
651 * Test the given node for the given attribute
653 * This will return true (via the "match" pointer) if the libdom node
654 * has an attribute with the given name. The comparison is
655 * case-sensitive. It corresponds to node[attr] in CSS.
657 * \param pw Pointer to the current SVG parser state
658 * \param node Libdom SVG node to test
659 * \param qname Attribute name to check for
660 * \param match Pointer to the test result
662 * \return Returns CSS_OK if successful and CSS_NOMEM if anything
665 css_error
node_has_attribute(void *pw
, void *node
,
666 const css_qname
*qname
, bool *match
)
672 /* intern the attribute name as a dom_string so we can
673 * delegate to dom_element_has_attribute() */
674 err
= dom_string_create_interned(
675 (const uint8_t *) lwc_string_data(qname
->name
),
676 lwc_string_length(qname
->name
),
678 if (err
!= DOM_NO_ERR
) {
682 err
= dom_element_has_attribute((dom_node
*)node
, name
, match
);
683 if (err
!= DOM_NO_ERR
) {
684 dom_string_unref(name
);
688 dom_string_unref(name
);
694 * Test the given node for an attribute with a specific value
696 * This will return true (via the "match" pointer) if the libdom node
697 * has an attribute with the given name and value. The comparison is
698 * case-sensitive. It corresponds to node[attr=value] in CSS.
700 * \param pw Pointer to the current SVG parser state
701 * \param node Libdom SVG node to test
702 * \param qname Attribute name to check for
703 * \param value Attribute value to check for
704 * \param match Pointer to the test result
706 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
707 * intern the attribute name (which usually indicates memory
710 css_error
node_has_attribute_equal(void *pw
, void *node
,
711 const css_qname
*qname
, lwc_string
*value
,
714 /* Implementation note: NetSurf always returns "no match" when
715 * the value is empty (length zero). We allow it, because why
720 dom_string
*attr_val
;
723 /* Intern the attribute name as a dom_string so we can
724 * use dom_element_get_attribute() */
725 err
= dom_string_create_interned(
726 (const uint8_t *) lwc_string_data(qname
->name
),
727 lwc_string_length(qname
->name
),
729 if (err
!= DOM_NO_ERR
) {
733 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
734 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
735 /* There was an error getting the attribute's value or
736 * the attribute doesn't exist. So, no match? */
737 dom_string_unref(name
);
742 /* Otherwise, we have the attribute value from the given node
743 * and all we need to do is compare. */
744 dom_string_unref(name
);
745 *match
= dom_string_lwc_isequal(attr_val
, value
);
746 dom_string_unref(attr_val
);
753 * Test the given node for an attribute with a specific value,
754 * possibly followed by a single hyphen
756 * This will return true (via the "match" pointer) if the libdom node
757 * has an attribute with the given name and value or with the given
758 * name and a value that is followed by exactly one hyphen. The
759 * comparison is case-sensitive. This corresponds to [attr|=value]
762 * \param pw Pointer to the current SVG parser state
763 * \param node Libdom SVG node to test
764 * \param qname Attribute name to check for
765 * \param value Attribute value to check for
766 * \param match Pointer to the test result
768 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
769 * intern the attribute name (which usually indicates memory
772 css_error
node_has_attribute_dashmatch(void *pw
, void *node
,
773 const css_qname
*qname
, lwc_string
*value
,
776 /* Implementation note: NetSurf always returns "no match" when
777 * the value is empty (length zero). We allow it, because why
782 dom_string
*attr_val
;
785 const char *vdata
; /* to hold the data underlying "value" */
787 const char *avdata
; /* to hold the found attribute value data */
790 /* Intern the attribute name as a dom_string so we can
791 * use dom_element_get_attribute() */
792 err
= dom_string_create_interned(
793 (const uint8_t *) lwc_string_data(qname
->name
),
794 lwc_string_length(qname
->name
),
796 if (err
!= DOM_NO_ERR
) {
800 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
801 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
802 /* There was an error getting the attribute's value or
803 * the attribute doesn't exist. So, no match? */
804 dom_string_unref(name
);
809 /* Otherwise, we have the attribute value from the given node
810 * and all we need to do is compare. */
811 dom_string_unref(name
);
812 *match
= dom_string_lwc_isequal(attr_val
, value
);
814 /* Exact match, we're done */
815 dom_string_unref(attr_val
);
819 /* No exact match, try it with a hyphen on the end */
820 vdata
= lwc_string_data(value
); /* needle */
821 vdata_len
= lwc_string_length(value
);
822 avdata
= dom_string_data(attr_val
); /* haystack */
823 avdata_len
= dom_string_byte_length(attr_val
);
824 dom_string_unref(attr_val
);
826 if (avdata_len
> vdata_len
&& avdata
[vdata_len
] == '-') {
827 if (strncasecmp(avdata
, vdata
, vdata_len
) == 0) {
828 /* If there's a hyphen in the right position,
829 * it suffices to compare the strings only up
840 * Test the given node for an attribute whose value is a
841 * space-separated list of words, one of which is the given word
843 * This will return true (via the "match" pointer) if the libdom node
844 * has an attribute with the given name and whose value when
845 * considered as a space-separated list of words contains the given
846 * word. The comparison is case-sensitive. This corresponds to
847 * [attr~=value] in CSS.
849 * \param pw Pointer to the current SVG parser state
850 * \param node Libdom SVG node to test
851 * \param qname Attribute name to check for
852 * \param word Value word to check for
853 * \param match Pointer to the test result
855 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
856 * intern the attribute name (which usually indicates memory
859 css_error
node_has_attribute_includes(void *pw
, void *node
,
860 const css_qname
*qname
, lwc_string
*word
,
866 dom_string
*attr_val
;
868 size_t wordlen
; /* length of "word" */
870 /* pointers used to parse a space-separated list of words */
875 *match
= false; /* default to no match */
877 wordlen
= lwc_string_length(word
);
879 /* In this case, the spec says that "if 'val' is the
880 * empty string, it will never represent anything." */
884 /* Intern the attribute name as a dom_string so we can
885 * use dom_element_get_attribute() */
886 err
= dom_string_create_interned(
887 (const uint8_t *) lwc_string_data(qname
->name
),
888 lwc_string_length(qname
->name
),
890 if (err
!= DOM_NO_ERR
) {
894 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
895 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
896 /* There was an error getting the attribute's value or
897 * the attribute doesn't exist. So, no match? */
898 dom_string_unref(name
);
902 /* Parse the list comparing each word against "word" */
903 start
= dom_string_data(attr_val
);
904 end
= start
+ dom_string_byte_length(attr_val
);
905 dom_string_unref(attr_val
);
907 for (p
= start
; p
<= end
; p
++) {
908 /* Move forward until we find the end of the first word */
909 if (*p
== ' ' || *p
== '\0') {
910 /* If the length of that word is the length of the
911 * word we're looking for, do the comparison. */
912 if ((size_t) (p
- start
) == wordlen
&&
914 lwc_string_data(word
),
919 /* No match? Set "start" to the beginning of
920 * the next word and loop. */
930 * Test the given node for an attribute whose value begins with the
933 * This will return true (via the "match" pointer) if the libdom node
934 * has an attribute with the given name and whose value begins with
935 * the given prefix string. The comparison is case-sensitive. This
936 * corresponds to [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 prefix Value prefix 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_prefix(void *pw
, void *node
,
949 const css_qname
*qname
, lwc_string
*prefix
,
954 dom_string
*attr_val
;
956 const char *avdata
; /* attribute value data */
957 size_t avdata_len
; /* length of that attribute value data */
958 size_t prefixlen
; /* length of "prefix" */
960 prefixlen
= lwc_string_length(prefix
);
961 if (prefixlen
== 0) {
962 /* In this case, the spec says that "if 'val' is the
963 * empty string, it will never represent anything." */
967 /* Intern the attribute name as a dom_string so we can
968 * use dom_element_get_attribute() */
969 err
= dom_string_create_interned(
970 (const uint8_t *) lwc_string_data(qname
->name
),
971 lwc_string_length(qname
->name
),
973 if (err
!= DOM_NO_ERR
) {
977 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
978 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
979 /* There was an error getting the attribute's value or
980 * the attribute doesn't exist. So, no match? */
981 dom_string_unref(name
);
986 /* Otherwise, we have the attribute value from the given node,
987 * and the first thing we want to do is check to see if the
988 * whole thing matches the prefix. */
989 dom_string_unref(name
);
990 *match
= dom_string_lwc_isequal(attr_val
, prefix
);
992 /* If not, check to see if an, uh, prefix matches the
994 if (*match
== false) {
995 avdata
= dom_string_data(attr_val
);
996 avdata_len
= dom_string_byte_length(attr_val
);
997 if ((avdata_len
>= prefixlen
) &&
999 lwc_string_data(prefix
),
1001 /* Use strncasecmp to compare only the first
1002 * "n" characters, where "n" is the length of
1008 dom_string_unref(attr_val
);
1015 * Test the given node for an attribute whose value end with the
1018 * This will return true (via the "match" pointer) if the libdom node
1019 * has an attribute with the given name and whose value ends with
1020 * the given suffix string. The comparison is case-sensitive. This
1021 * corresponds to [attr$=value] in CSS.
1023 * \param pw Pointer to the current SVG parser state
1024 * \param node Libdom SVG node to test
1025 * \param qname Attribute name to check for
1026 * \param suffix Value suffix to check for
1027 * \param match Pointer to the test result
1029 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1030 * intern the attribute name (which usually indicates memory
1033 css_error
node_has_attribute_suffix(void *pw
, void *node
,
1034 const css_qname
*qname
, lwc_string
*suffix
,
1039 dom_string
*attr_val
;
1041 const char *avdata
; /* attribute value data */
1042 size_t avdata_len
; /* length of that attribute value data */
1043 size_t suffixlen
; /* length of "suffix" */
1045 /* convenience pointer we'll use when matching the suffix */
1046 const char *suffix_start
;
1048 suffixlen
= lwc_string_length(suffix
);
1049 if (suffixlen
== 0) {
1050 /* In this case, the spec says that "if 'val' is the
1051 * empty string, it will never represent anything." */
1055 /* Intern the attribute name as a dom_string so we can
1056 * use dom_element_get_attribute() */
1057 err
= dom_string_create_interned(
1058 (const uint8_t *) lwc_string_data(qname
->name
),
1059 lwc_string_length(qname
->name
),
1061 if (err
!= DOM_NO_ERR
) {
1065 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
1066 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
1067 /* There was an error getting the attribute's value or
1068 * the attribute doesn't exist. So, no match? */
1069 dom_string_unref(name
);
1074 /* Otherwise, we have the attribute value from the given node,
1075 * and the first thing we want to do is check to see if the
1076 * whole thing matches the suffix. */
1077 dom_string_unref(name
);
1078 *match
= dom_string_lwc_isequal(attr_val
, suffix
);
1080 /* If not, check to see if an, uh, suffix matches the
1082 if (*match
== false) {
1083 avdata
= dom_string_data(attr_val
);
1084 avdata_len
= dom_string_byte_length(attr_val
);
1086 suffix_start
= (char *)(avdata
+ avdata_len
- suffixlen
);
1088 if ((avdata_len
>= suffixlen
) &&
1089 (strncasecmp(suffix_start
,
1090 lwc_string_data(suffix
),
1092 /* Use strncasecmp to compare only the last
1093 * "n" characters, where "n" is the length of
1099 dom_string_unref(attr_val
);
1106 * Test the given node for an attribute whose value contains the
1109 * This will return true (via the "match" pointer) if the libdom node
1110 * has an attribute with the given name and whose value contains the
1111 * given substring. The comparison is case-sensitive. This corresponds
1112 * to [attr*=value] in CSS.
1114 * \param pw Pointer to the current SVG parser state
1115 * \param node Libdom SVG node to test
1116 * \param qname Attribute name to check for
1117 * \param substring Value substring to check for
1118 * \param match Pointer to the test result
1120 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1121 * intern the attribute name (which usually indicates memory
1124 css_error
node_has_attribute_substring(void *pw
, void *node
,
1125 const css_qname
*qname
, lwc_string
*substring
,
1130 dom_string
*attr_val
;
1132 size_t attr_len
; /* length of attr_val */
1133 size_t substrlen
; /* length of "substring" */
1135 /* Convenience pointers we use when comparing substrings */
1139 substrlen
= lwc_string_length(substring
);
1140 if (substrlen
== 0) {
1141 /* In this case, the spec says that "if 'val' is the
1142 * empty string, it will never represent anything." */
1146 /* Intern the attribute name as a dom_string so we can
1147 * use dom_element_get_attribute() */
1148 err
= dom_string_create_interned(
1149 (const uint8_t *) lwc_string_data(qname
->name
),
1150 lwc_string_length(qname
->name
),
1152 if (err
!= DOM_NO_ERR
) {
1156 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
1157 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
1158 /* There was an error getting the attribute's value or
1159 * the attribute doesn't exist. So, no match? */
1160 dom_string_unref(name
);
1165 /* Otherwise, we have the attribute value from the given node,
1166 * and the first thing we want to do is check to see if the
1167 * whole thing matches the substring. */
1168 dom_string_unref(name
);
1169 *match
= dom_string_lwc_isequal(attr_val
, substring
);
1171 /* If not, check to see if an, uh, substring matches the
1173 if (*match
== false) {
1174 p
= dom_string_data(attr_val
);
1176 /* Check every long-enough suffix for a prefix match */
1177 attr_len
= dom_string_byte_length(attr_val
);
1178 if (attr_len
>= substrlen
) {
1179 p_max
= p
+ attr_len
- substrlen
;
1180 while (p
<= p_max
) {
1182 lwc_string_data(substring
),
1192 dom_string_unref(attr_val
);
1199 * Test whether or not the given node is the document's root element
1200 * This corresponds to the CSS :root pseudo-selector.
1202 * \param pw Pointer to the current SVG parser state
1203 * \param node Libdom SVG node to test
1204 * \param match Pointer to the test result
1206 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1208 css_error
node_is_root(void *pw
, void *node
, bool *match
)
1215 err
= dom_node_get_parent_node((dom_node
*)node
, &parent
);
1216 if (err
!= DOM_NO_ERR
) {
1220 /* It's the root element if it doesn't have a parent element */
1221 if (parent
!= NULL
) {
1222 err
= dom_node_get_node_type(parent
, &type
);
1223 dom_node_unref(parent
);
1224 if (err
!= DOM_NO_ERR
) {
1227 if (type
!= DOM_DOCUMENT_NODE
) {
1228 /* DOM_DOCUMENT_NODE is the only allowable
1229 * type of parent node for the root element */
1241 * Used internally in node_count_siblings() to "count" the given
1242 * sibling node. It factors out the node type and name checks.
1244 static int node_count_siblings_check(dom_node
*dnode
,
1251 dom_string
*dnode_name
;
1253 /* We flip this to 1 if/when we count this node */
1256 if (dnode
== NULL
) {
1260 exc
= dom_node_get_node_type(dnode
, &type
);
1261 if ((exc
!= DOM_NO_ERR
) || (type
!= DOM_ELEMENT_NODE
)) {
1262 /* We only count element siblings */
1266 /* ... with the right name */
1269 exc
= dom_node_get_node_name(dnode
, &dnode_name
);
1271 if ((exc
== DOM_NO_ERR
) && (dnode_name
!= NULL
)) {
1272 if (dom_string_isequal(name
,
1276 dom_string_unref(dnode_name
);
1287 * Count the given node's sibling elements
1289 * This counts the given node's sibling elements in one direction,
1290 * either forwards or backwards, in the DOM. Keep in mind that the
1291 * libdom tree is upside-down compared to the CSS one; so "next" and
1292 * "previous" are actually reversed; the default is to count preceding
1293 * libdom siblings which correspond to subsequent CSS siblings.
1295 * This operation is central to the CSS :first-child, :nth-child, and
1296 * :last-child (et cetera) pseudo-selectors.
1298 * If same_name is true, then only nodes having the same
1299 * (case-sensitive) name as the given node are counted.
1301 * \param pw Pointer to the current SVG parser state
1302 * \param node Libdom SVG node whose siblings we're counting
1303 * \param same_name Whether or not to count only siblings having
1304 * the same name as the given node
1305 * \param after Count subsequent siblings rather than precedent
1306 * ones (the default)
1307 * \param count Pointer to the return value, the number of sibling
1310 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1312 css_error
node_count_siblings(void *pw
, void *node
,
1313 bool same_name
, bool after
, int32_t *count
)
1317 dom_node
*dnode
; /* node, but with the right type */
1318 dom_string
*dnode_name
;
1319 dom_node
*next
; /* "next" sibling (depends on direction) */
1321 /* Pointer to the "next sibling" function */
1322 dom_exception (*next_func
)(dom_node
*, dom_node
**);
1327 dnode
= (dom_node
*)node
;
1329 exc
= dom_node_get_node_name(dnode
, &dnode_name
);
1330 if ((exc
!= DOM_NO_ERR
) || (dnode_name
== NULL
)) {
1335 /* Increment the reference counter for dnode for as long as
1336 * we retain a reference to it. */
1337 dnode
= dom_node_ref(dnode
);
1339 next_func
= dom_node_get_previous_sibling
;
1341 next_func
= dom_node_get_next_sibling
;
1345 exc
= next_func(dnode
, &next
);
1346 if (exc
!= DOM_NO_ERR
) {
1350 /* If next_func worked, we're about to swap "next"
1351 * with "dnode" meaning that we will no longer retain
1352 * a reference to the current dnode. */
1353 dom_node_unref(dnode
);
1356 *count
+= node_count_siblings_check(dnode
,
1359 } while (dnode
!= NULL
);
1361 if (dnode_name
!= NULL
) {
1362 dom_string_unref(dnode_name
);
1370 * Determine whether or not the given element is empty
1372 * An element is "nonempty" if it has a child that is either an
1373 * element node or a text node.
1375 * \param pw Pointer to the current SVG parser state
1376 * \param node Libdom SVG node to check for emptiness
1377 * \param is_empty Pointer to the return value
1379 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1381 css_error
node_is_empty(void *pw
, void *node
, bool *is_empty
)
1384 dom_node
*child
; /* current child node pointer */
1385 dom_node
*next
; /* next child node pointer */
1386 dom_node_type type
; /* what type of node is "child" */
1389 /* Assume that it's empty by default */
1392 /* Get the given node's first child. Implementation detail:
1393 * this increments the reference counter on the child node. */
1394 err
= dom_node_get_first_child((dom_node
*)node
, &child
);
1395 if (err
!= DOM_NO_ERR
) {
1399 /* And now loop through all children looking for a
1400 * text/element node. If we find one, the original
1401 * node is "nonempty" */
1402 while (child
!= NULL
) {
1403 err
= dom_node_get_node_type(child
, &type
);
1404 if (err
!= DOM_NO_ERR
) {
1405 dom_node_unref(child
);
1409 if (type
== DOM_ELEMENT_NODE
|| type
== DOM_TEXT_NODE
) {
1411 dom_node_unref(child
);
1415 err
= dom_node_get_next_sibling(child
, &next
);
1416 if (err
!= DOM_NO_ERR
) {
1417 dom_node_unref(child
);
1421 /* If we're moving to the next node, we can release
1422 * the reference to the current one */
1423 dom_node_unref(child
);