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
,
48 * Resolve a relative URL to an absolute one by doing nothing. This is
49 * the simplest possible implementation of a URL resolver, needed for
52 css_error
svgtiny_resolve_url(void *pw
,
53 const char *base
, lwc_string
*rel
, lwc_string
**abs
)
58 /* Copy the relative URL to the absolute one (the return
60 *abs
= lwc_string_ref(rel
);
65 * Create a stylesheet with the default set of params.
67 * \param sheet A stylesheet pointer, passed in by reference, that
68 * we use to store the newly-created stylesheet.
69 * \param inline_style True if this stylesheet represents an inline
70 * style, and false otherwise.
72 * \return The return value from css_stylesheet_create() is returned.
74 css_error
svgtiny_create_stylesheet(css_stylesheet
**sheet
,
77 css_stylesheet_params params
;
79 params
.params_version
= CSS_STYLESHEET_PARAMS_VERSION_1
;
80 params
.level
= CSS_LEVEL_DEFAULT
;
81 params
.charset
= NULL
;
84 params
.allow_quirks
= false;
85 params
.inline_style
= inline_style
;
86 params
.resolve
= svgtiny_resolve_url
;
87 params
.resolve_pw
= NULL
;
89 params
.import_pw
= NULL
;
91 params
.color_pw
= NULL
;
93 params
.font_pw
= NULL
;
95 return css_stylesheet_create(¶ms
, sheet
);
99 /**************************/
100 /* libcss select handlers */
101 /**************************/
103 * From here on we implement the "select handler "API defined in
104 * libcss's include/libcss/select.h and discussed briefly in its
110 * Retrieve the given node's name
112 * \param pw Pointer to the current SVG parser state
113 * \param node Libdom SVG node
114 * \param qname Address at which to store the node name
116 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
118 css_error
node_name(void *pw
, void *node
, css_qname
*qname
)
122 struct svgtiny_parse_state
*state
;
124 err
= dom_node_get_node_name((dom_node
*)node
, &name
);
125 if (err
!= DOM_NO_ERR
) {
129 state
= (struct svgtiny_parse_state
*)pw
;
130 qname
->ns
= lwc_string_ref(state
->interned_svg_xmlns
);
132 err
= dom_string_intern(name
, &qname
->name
);
133 if (err
!= DOM_NO_ERR
) {
134 dom_string_unref(name
);
138 dom_string_unref(name
);
145 * Retrieve the given node's classes
147 * \param pw Pointer to the current SVG parser state
148 * \param node Libdom SVG node
149 * \param classes Address at which to store the class name array
150 * \param n_classes Address at which to store the length of the class
153 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
155 * \note CSS_NOMEM is not possible in practice as of libdom-0.4.1,
156 * because the underlying libdom function never fails
158 css_error
node_classes(void *pw
, void *node
,
159 lwc_string
***classes
, uint32_t *n_classes
)
164 err
= dom_element_get_classes((dom_node
*)node
, classes
, n_classes
);
166 /* The implementation does not do it, but the documentation
167 for dom_element_get_classes() says that a DOM_NO_MEM_ERR is
168 possible here, so we handle it to be on the safe side. */
169 if (err
!= DOM_NO_ERR
) {
178 * Retrieve the given node's id
180 * \param pw Pointer to the current SVG parser state
181 * \param node Libdom SVG node
182 * \param id Address at which to store the id
184 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
186 css_error
node_id(void *pw
, void *node
, lwc_string
**id
)
190 struct svgtiny_parse_state
*state
;
192 /* Begin with the assumption that this node has no id */
195 state
= (struct svgtiny_parse_state
*)pw
;
196 err
= dom_element_get_attribute((dom_node
*)node
,
197 state
->interned_id
, &attr
);
198 if (err
!= DOM_NO_ERR
) {
201 else if (attr
== NULL
) {
202 /* The node has no id attribute and our return value
203 is already set to NULL so we're done */
207 /* If we found an id attribute (a dom_string), intern it into
208 an lwc_string that we can return, and then cleanup the
210 err
= dom_string_intern(attr
, id
);
211 if (err
!= DOM_NO_ERR
) {
212 dom_string_unref(attr
);
215 dom_string_unref(attr
);
222 * Find the first parent of the given element having the given name
224 * \param pw Pointer to the current SVG parser state
225 * \param node Libdom SVG node
226 * \param qname Name of the parent node to search for
227 * \param parent Address at which to store the parent node pointer
229 * \return Always returns CSS_OK
231 * \post If a suitable element is found, a pointer to it will be
232 * stored at the address pointed to by \a parent; otherwise,
233 * NULL will be stored at the address pointed to by \a parent
235 css_error
named_parent_node(void *pw
, void *node
,
236 const css_qname
*qname
, void **parent
)
239 /* dom_element_named_parent_node() was invented to implement
240 * this select handler so there isn't much for us to do except
241 * call it. It's OK if node isn't an element, libdom checks
243 dom_element_named_parent_node((dom_element
*)node
,
245 (struct dom_element
**)parent
);
247 /* Implementation detail: dom_element_named_parent_node()
248 * increments the reference count of the parent element before
249 * returning it to us. According to docs/RefCnt in the libdom
250 * repository, this will prevent the parent element from being
251 * destroyed if it is pruned from the DOM. That sounds good,
252 * since we don't want to be using a pointer to an object that
253 * has been destroyed... but we also have no way of later
254 * decrementing the reference count ourselves, and don't want
255 * to make the returned node eternal. Decrementing the
256 * reference counter now allows it to be destroyed when the
257 * DOM no longer needs it, and so long as no other parts of
258 * libsvgtiny are messing with the DOM during parsing, that
259 * shouldn't (ha ha) cause any problems. */
260 dom_node_unref(*parent
);
267 * Find the "next-sibling" of the given element having the given name
269 * This search corresponds to the "+ foo" combinator in CSS and will
270 * find only "foo" element nodes that immediately precede the given
271 * node under the same parent in the DOM. In CSS the tree is viewed
272 * top-down and in libdom it is viewed from the bottom-up; as a result
273 * "next" and "previous" are sometimes backwards. This is case-sensitive.
275 * \param pw Pointer to the current SVG parser state
276 * \param node Libdom SVG node
277 * \param qname Name of the sibling node to search for
278 * \param sibling Address at which to store the sibling node pointer
280 * \return Always returns CSS_OK
282 * \post If a suitable element is found, a pointer to it will be
283 * stored at the address pointed to by \a sibling; otherwise,
284 * NULL will be stored at the address pointed to by \a sibling
286 css_error
named_sibling_node(void *pw
, void *node
,
287 const css_qname
*qname
, void **sibling
)
290 dom_node
*n
= node
; /* the current node */
291 dom_node
*prev
; /* the previous node */
296 *sibling
= NULL
; /* default to nothing found */
298 /* Begin the search; the first iteration we do outside of the
299 * loop. Implementation detil: dom_node_get_previous_sibling()
300 * increments the reference counter on the returned node. A
301 * comment within named_parent_node() explains why we
302 * decrement it ASAP. */
303 err
= dom_node_get_previous_sibling(n
, &n
);
304 if (err
!= DOM_NO_ERR
) {
309 /* We're looking for the first ELEMENT sibling */
310 err
= dom_node_get_node_type(n
, &type
);
311 if (err
!= DOM_NO_ERR
) {
316 if (type
== DOM_ELEMENT_NODE
) {
317 /* We found an element node, does it have the
319 err
= dom_node_get_node_name(n
, &name
);
320 if (err
!= DOM_NO_ERR
) {
325 if (dom_string_lwc_isequal(name
,
327 /* The name is right, return it */
331 /* There's only one next-sibling element node
332 * and we've already found it, so if its name
333 * wasn't right, we return the default value
335 dom_string_unref(name
);
340 /* Not an element node, so we move on the the previous
341 * previous sibling */
342 err
= dom_node_get_previous_sibling(n
, &prev
);
343 if (err
!= DOM_NO_ERR
) {
357 * Find the first "subsequent-sibling" of the given element having the
360 * This search corresponds to the "~ foo" combinator in CSS and will
361 * find only "foo" element nodes that precede the given node (under
362 * the same parent) in the DOM. In CSS the tree is viewed top-down and
363 * in libdom it is viewed from the bottom-up; as a result "next" and
364 * "previous" are sometimes backwards. This is case-sensitive.
366 * \param pw Pointer to the current SVG parser state
367 * \param node Libdom SVG node
368 * \param qname Name of the sibling node to search for
369 * \param sibling Address at which to store the sibling node pointer
371 * \return Always returns CSS_OK
373 * \post If a suitable element is found, a pointer to it will be
374 * stored at the address pointed to by \a sibling; otherwise,
375 * NULL will be stored at the address pointed to by \a sibling
377 css_error
named_generic_sibling_node(void *pw
, void *node
,
378 const css_qname
*qname
, void **sibling
)
381 dom_node
*n
= node
; /* the current node */
382 dom_node
*prev
; /* the previous node */
388 *sibling
= NULL
; /* default to nothing found */
390 /* Begin the search; the first iteration we do outside of the
391 * loop. Implementation detil: dom_node_get_previous_sibling()
392 * increments the reference counter on the returned node. A
393 * comment within named_parent_node() explains why we
394 * decrement it ASAP. */
395 err
= dom_node_get_previous_sibling(n
, &n
);
396 if (err
!= DOM_NO_ERR
) {
401 err
= dom_node_get_node_type(n
, &type
);
402 if (err
!= DOM_NO_ERR
) {
407 if (type
== DOM_ELEMENT_NODE
) {
408 /* We only want ELEMENT nodes */
409 err
= dom_node_get_node_name(n
, &name
);
410 if (err
!= DOM_NO_ERR
) {
415 if (dom_string_lwc_isequal(name
,
417 /* Found one. Save it and stop the search */
418 dom_string_unref(name
);
424 dom_string_unref(name
);
427 /* This sibling wasn't an element with the desired
428 name, so move on to the previous sibling */
429 err
= dom_node_get_previous_sibling(n
, &prev
);
430 if (err
!= DOM_NO_ERR
) {
444 * Return a pointer to the given node's parent
446 * \param pw Pointer to the current SVG parser state
447 * \param node Libdom SVG node
448 * \param parent Address at which to store the node's parent pointer
450 * \return Always returns CSS_OK
452 css_error
parent_node(void *pw
, void *node
, void **parent
)
455 /* Libdom basically implements this for us */
456 dom_element_parent_node(node
, (struct dom_element
**)parent
);
458 /* See the comment in named_parent_node() for why we decrement
459 * this reference counter here. */
460 dom_node_unref(*parent
);
467 * Find the "next-sibling" of the given element
469 * This search corresponds "+ *" in CSS and will find the first
470 * element node that immediately precedes the given node under the
471 * same parent in the DOM. In CSS the tree is viewed top-down and in
472 * libdom it is viewed from the bottom-up; as a result "next" and
473 * "previous" are sometimes backwards.
475 * \param pw Pointer to the current SVG parser state
476 * \param node Libdom SVG node
477 * \param sibling Address at which to store the sibling node pointer
479 * \return Always returns CSS_OK
481 * \post If a suitable element is found, a pointer to it will be
482 * stored at the address pointed to by \a sibling; otherwise,
483 * NULL will be stored at the address pointed to by \a sibling
485 css_error
sibling_node(void *pw
, void *node
, void **sibling
)
488 dom_node
*n
= node
; /* the current node */
489 dom_node
*prev
; /* the previous node */
493 *sibling
= NULL
; /* default to nothing found */
495 /* Begin the search; the first iteration we do outside of the
496 * loop. Implementation detil: dom_node_get_previous_sibling()
497 * increments the reference counter on the returned node. A
498 * comment within named_parent_node() explains why we
499 * decrement it ASAP. */
500 err
= dom_node_get_previous_sibling(n
, &n
);
501 if (err
!= DOM_NO_ERR
) {
506 err
= dom_node_get_node_type(n
, &type
);
507 if (err
!= DOM_NO_ERR
) {
512 if (type
== DOM_ELEMENT_NODE
) {
513 /* We found a sibling node that is also an
514 element and that's all we wanted. */
520 /* This sibling node was not an element; move on to
521 the previous sibling */
522 err
= dom_node_get_previous_sibling(n
, &prev
);
523 if (err
!= DOM_NO_ERR
) {
537 * Test the given node for the given name
539 * This will return true (via the "match" pointer) if the libdom node
540 * has the given name or if that name is the universal selector;
541 * otherwise it returns false. The comparison is case-sensitive. It
542 * corresponds to a rule like "body { ... }" in CSS.
544 * \param pw Pointer to the current SVG parser state
545 * \param node Libdom SVG node to test
546 * \param qname Name to check for
547 * \param match Pointer to the test result
549 * \return Always returns CSS_OK
551 css_error
node_has_name(void *pw
, void *node
,
552 const css_qname
*qname
, bool *match
)
554 struct svgtiny_parse_state
*state
;
558 /* Start by checking to see if qname is the universal selector */
559 state
= (struct svgtiny_parse_state
*)pw
;
560 if (lwc_string_isequal(qname
->name
,
561 state
->interned_universal
, match
) == lwc_error_ok
) {
563 /* It's the universal selector. In NetSurf, all node
564 * names match the universal selector, and nothing in
565 * the libcss documentation suggests another approach,
566 * so we follow NetSurf here. */
571 err
= dom_node_get_node_name((dom_node
*)node
, &name
);
572 if (err
!= DOM_NO_ERR
) {
576 /* Unlike with HTML, SVG element names are case-sensitive */
577 *match
= dom_string_lwc_isequal(name
, qname
->name
);
578 dom_string_unref(name
);
585 * Test the given node for the given class
587 * This will return true (via the "match" pointer) if the libdom node
588 * has the given class. The comparison is case-sensitive. It
589 * corresponds to node.class in CSS.
591 * \param pw Pointer to the current SVG parser state
592 * \param node Libdom SVG node to test
593 * \param name Class name to check for
594 * \param match Pointer to the test result
596 * \return Always returns CSS_OK
598 css_error
node_has_class(void *pw
, void *node
,
599 lwc_string
*name
, bool *match
)
602 /* libdom implements this for us and apparently it cannot fail */
603 dom_element_has_class((dom_node
*)node
, name
, match
);
609 * Test the given node for the given id
611 * This will return true (via the "match" pointer) if the libdom node
612 * has the given id. The comparison is case-sensitive. It corresponds
615 * \param pw Pointer to the current SVG parser state
616 * \param node Libdom SVG node to test
617 * \param name Id to check for
618 * \param match Pointer to the test result
620 * \return Always returns CSS_OK
622 css_error
node_has_id(void *pw
, void *node
,
623 lwc_string
*name
, bool *match
)
627 struct svgtiny_parse_state
*state
;
629 attr
= NULL
; /* a priori the "id" attribute may not exist */
630 *match
= false; /* default to no match */
632 state
= (struct svgtiny_parse_state
*)pw
;
633 err
= dom_element_get_attribute((dom_node
*)node
,
634 state
->interned_id
, &attr
);
635 if (err
!= DOM_NO_ERR
|| attr
== NULL
) {
639 *match
= dom_string_lwc_isequal(attr
, name
);
640 dom_string_unref(attr
);
647 * Test the given node for the given attribute
649 * This will return true (via the "match" pointer) if the libdom node
650 * has an attribute with the given name. The comparison is
651 * case-sensitive. It corresponds to node[attr] in CSS.
653 * \param pw Pointer to the current SVG parser state
654 * \param node Libdom SVG node to test
655 * \param qname Attribute name to check for
656 * \param match Pointer to the test result
658 * \return Returns CSS_OK if successful and CSS_NOMEM if anything
661 css_error
node_has_attribute(void *pw
, void *node
,
662 const css_qname
*qname
, bool *match
)
668 /* intern the attribute name as a dom_string so we can
669 * delegate to dom_element_has_attribute() */
670 err
= dom_string_create_interned(
671 (const uint8_t *) lwc_string_data(qname
->name
),
672 lwc_string_length(qname
->name
),
674 if (err
!= DOM_NO_ERR
) {
678 err
= dom_element_has_attribute((dom_node
*)node
, name
, match
);
679 if (err
!= DOM_NO_ERR
) {
680 dom_string_unref(name
);
684 dom_string_unref(name
);
690 * Test the given node for an attribute with a specific value
692 * This will return true (via the "match" pointer) if the libdom node
693 * has an attribute with the given name and value. The comparison is
694 * case-sensitive. It corresponds to node[attr=value] in CSS.
696 * \param pw Pointer to the current SVG parser state
697 * \param node Libdom SVG node to test
698 * \param qname Attribute name to check for
699 * \param value Attribute value to check for
700 * \param match Pointer to the test result
702 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
703 * intern the attribute name (which usually indicates memory
706 css_error
node_has_attribute_equal(void *pw
, void *node
,
707 const css_qname
*qname
, lwc_string
*value
,
710 /* Implementation note: NetSurf always returns "no match" when
711 * the value is empty (length zero). We allow it, because why
716 dom_string
*attr_val
;
719 /* Intern the attribute name as a dom_string so we can
720 * use dom_element_get_attribute() */
721 err
= dom_string_create_interned(
722 (const uint8_t *) lwc_string_data(qname
->name
),
723 lwc_string_length(qname
->name
),
725 if (err
!= DOM_NO_ERR
) {
729 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
730 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
731 /* There was an error getting the attribute's value or
732 * the attribute doesn't exist. So, no match? */
733 dom_string_unref(name
);
738 /* Otherwise, we have the attribute value from the given node
739 * and all we need to do is compare. */
740 dom_string_unref(name
);
741 *match
= dom_string_lwc_isequal(attr_val
, value
);
742 dom_string_unref(attr_val
);
749 * Test the given node for an attribute with a specific value,
750 * possibly followed by a single hyphen
752 * This will return true (via the "match" pointer) if the libdom node
753 * has an attribute with the given name and value or with the given
754 * name and a value that is followed by exactly one hyphen. The
755 * comparison is case-sensitive. This corresponds to [attr|=value]
758 * \param pw Pointer to the current SVG parser state
759 * \param node Libdom SVG node to test
760 * \param qname Attribute name to check for
761 * \param value Attribute value to check for
762 * \param match Pointer to the test result
764 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
765 * intern the attribute name (which usually indicates memory
768 css_error
node_has_attribute_dashmatch(void *pw
, void *node
,
769 const css_qname
*qname
, lwc_string
*value
,
772 /* Implementation note: NetSurf always returns "no match" when
773 * the value is empty (length zero). We allow it, because why
778 dom_string
*attr_val
;
781 const char *vdata
; /* to hold the data underlying "value" */
783 const char *avdata
; /* to hold the found attribute value data */
786 /* Intern the attribute name as a dom_string so we can
787 * use dom_element_get_attribute() */
788 err
= dom_string_create_interned(
789 (const uint8_t *) lwc_string_data(qname
->name
),
790 lwc_string_length(qname
->name
),
792 if (err
!= DOM_NO_ERR
) {
796 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
797 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
798 /* There was an error getting the attribute's value or
799 * the attribute doesn't exist. So, no match? */
800 dom_string_unref(name
);
805 /* Otherwise, we have the attribute value from the given node
806 * and all we need to do is compare. */
807 dom_string_unref(name
);
808 *match
= dom_string_lwc_isequal(attr_val
, value
);
810 /* Exact match, we're done */
811 dom_string_unref(attr_val
);
815 /* No exact match, try it with a hyphen on the end */
816 vdata
= lwc_string_data(value
); /* needle */
817 vdata_len
= lwc_string_length(value
);
818 avdata
= dom_string_data(attr_val
); /* haystack */
819 avdata_len
= dom_string_byte_length(attr_val
);
820 dom_string_unref(attr_val
);
822 if (avdata_len
> vdata_len
&& avdata
[vdata_len
] == '-') {
823 if (strncasecmp(avdata
, vdata
, vdata_len
) == 0) {
824 /* If there's a hyphen in the right position,
825 * it suffices to compare the strings only up
836 * Test the given node for an attribute whose value is a
837 * space-separated list of words, one of which is the given word
839 * This will return true (via the "match" pointer) if the libdom node
840 * has an attribute with the given name and whose value when
841 * considered as a space-separated list of words contains the given
842 * word. The comparison is case-sensitive. This corresponds to
843 * [attr~=value] in CSS.
845 * \param pw Pointer to the current SVG parser state
846 * \param node Libdom SVG node to test
847 * \param qname Attribute name to check for
848 * \param word Value word to check for
849 * \param match Pointer to the test result
851 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
852 * intern the attribute name (which usually indicates memory
855 css_error
node_has_attribute_includes(void *pw
, void *node
,
856 const css_qname
*qname
, lwc_string
*word
,
862 dom_string
*attr_val
;
864 size_t wordlen
; /* length of "word" */
866 /* pointers used to parse a space-separated list of words */
871 *match
= false; /* default to no match */
873 wordlen
= lwc_string_length(word
);
875 /* In this case, the spec says that "if 'val' is the
876 * empty string, it will never represent anything." */
880 /* Intern the attribute name as a dom_string so we can
881 * use dom_element_get_attribute() */
882 err
= dom_string_create_interned(
883 (const uint8_t *) lwc_string_data(qname
->name
),
884 lwc_string_length(qname
->name
),
886 if (err
!= DOM_NO_ERR
) {
890 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
891 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
892 /* There was an error getting the attribute's value or
893 * the attribute doesn't exist. So, no match? */
894 dom_string_unref(name
);
898 /* Parse the list comparing each word against "word" */
899 start
= dom_string_data(attr_val
);
900 end
= start
+ dom_string_byte_length(attr_val
);
901 dom_string_unref(attr_val
);
903 for (p
= start
; p
<= end
; p
++) {
904 /* Move forward until we find the end of the first word */
905 if (*p
== ' ' || *p
== '\0') {
906 /* If the length of that word is the length of the
907 * word we're looking for, do the comparison. */
908 if ((size_t) (p
- start
) == wordlen
&&
910 lwc_string_data(word
),
915 /* No match? Set "start" to the beginning of
916 * the next word and loop. */
926 * Test the given node for an attribute whose value begins with the
929 * This will return true (via the "match" pointer) if the libdom node
930 * has an attribute with the given name and whose value begins with
931 * the given prefix string. The comparison is case-sensitive. This
932 * corresponds to [attr^=value] in CSS.
934 * \param pw Pointer to the current SVG parser state
935 * \param node Libdom SVG node to test
936 * \param qname Attribute name to check for
937 * \param prefix Value prefix to check for
938 * \param match Pointer to the test result
940 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
941 * intern the attribute name (which usually indicates memory
944 css_error
node_has_attribute_prefix(void *pw
, void *node
,
945 const css_qname
*qname
, lwc_string
*prefix
,
950 dom_string
*attr_val
;
952 const char *avdata
; /* attribute value data */
953 size_t avdata_len
; /* length of that attribute value data */
954 size_t prefixlen
; /* length of "prefix" */
956 prefixlen
= lwc_string_length(prefix
);
957 if (prefixlen
== 0) {
958 /* In this case, the spec says that "if 'val' is the
959 * empty string, it will never represent anything." */
963 /* Intern the attribute name as a dom_string so we can
964 * use dom_element_get_attribute() */
965 err
= dom_string_create_interned(
966 (const uint8_t *) lwc_string_data(qname
->name
),
967 lwc_string_length(qname
->name
),
969 if (err
!= DOM_NO_ERR
) {
973 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
974 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
975 /* There was an error getting the attribute's value or
976 * the attribute doesn't exist. So, no match? */
977 dom_string_unref(name
);
982 /* Otherwise, we have the attribute value from the given node,
983 * and the first thing we want to do is check to see if the
984 * whole thing matches the prefix. */
985 dom_string_unref(name
);
986 *match
= dom_string_lwc_isequal(attr_val
, prefix
);
988 /* If not, check to see if an, uh, prefix matches the
990 if (*match
== false) {
991 avdata
= dom_string_data(attr_val
);
992 avdata_len
= dom_string_byte_length(attr_val
);
993 if ((avdata_len
>= prefixlen
) &&
995 lwc_string_data(prefix
),
997 /* Use strncasecmp to compare only the first
998 * "n" characters, where "n" is the length of
1004 dom_string_unref(attr_val
);
1011 * Test the given node for an attribute whose value end with the
1014 * This will return true (via the "match" pointer) if the libdom node
1015 * has an attribute with the given name and whose value ends with
1016 * the given suffix string. The comparison is case-sensitive. This
1017 * corresponds to [attr$=value] in CSS.
1019 * \param pw Pointer to the current SVG parser state
1020 * \param node Libdom SVG node to test
1021 * \param qname Attribute name to check for
1022 * \param suffix Value suffix to check for
1023 * \param match Pointer to the test result
1025 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1026 * intern the attribute name (which usually indicates memory
1029 css_error
node_has_attribute_suffix(void *pw
, void *node
,
1030 const css_qname
*qname
, lwc_string
*suffix
,
1035 dom_string
*attr_val
;
1037 const char *avdata
; /* attribute value data */
1038 size_t avdata_len
; /* length of that attribute value data */
1039 size_t suffixlen
; /* length of "suffix" */
1041 /* convenience pointer we'll use when matching the suffix */
1042 const char *suffix_start
;
1044 suffixlen
= lwc_string_length(suffix
);
1045 if (suffixlen
== 0) {
1046 /* In this case, the spec says that "if 'val' is the
1047 * empty string, it will never represent anything." */
1051 /* Intern the attribute name as a dom_string so we can
1052 * use dom_element_get_attribute() */
1053 err
= dom_string_create_interned(
1054 (const uint8_t *) lwc_string_data(qname
->name
),
1055 lwc_string_length(qname
->name
),
1057 if (err
!= DOM_NO_ERR
) {
1061 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
1062 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
1063 /* There was an error getting the attribute's value or
1064 * the attribute doesn't exist. So, no match? */
1065 dom_string_unref(name
);
1070 /* Otherwise, we have the attribute value from the given node,
1071 * and the first thing we want to do is check to see if the
1072 * whole thing matches the suffix. */
1073 dom_string_unref(name
);
1074 *match
= dom_string_lwc_isequal(attr_val
, suffix
);
1076 /* If not, check to see if an, uh, suffix matches the
1078 if (*match
== false) {
1079 avdata
= dom_string_data(attr_val
);
1080 avdata_len
= dom_string_byte_length(attr_val
);
1082 suffix_start
= (char *)(avdata
+ avdata_len
- suffixlen
);
1084 if ((avdata_len
>= suffixlen
) &&
1085 (strncasecmp(suffix_start
,
1086 lwc_string_data(suffix
),
1088 /* Use strncasecmp to compare only the last
1089 * "n" characters, where "n" is the length of
1095 dom_string_unref(attr_val
);
1102 * Test the given node for an attribute whose value contains the
1105 * This will return true (via the "match" pointer) if the libdom node
1106 * has an attribute with the given name and whose value contains the
1107 * given substring. The comparison is case-sensitive. This corresponds
1108 * to [attr*=value] in CSS.
1110 * \param pw Pointer to the current SVG parser state
1111 * \param node Libdom SVG node to test
1112 * \param qname Attribute name to check for
1113 * \param substring Value substring to check for
1114 * \param match Pointer to the test result
1116 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1117 * intern the attribute name (which usually indicates memory
1120 css_error
node_has_attribute_substring(void *pw
, void *node
,
1121 const css_qname
*qname
, lwc_string
*substring
,
1126 dom_string
*attr_val
;
1128 size_t attr_len
; /* length of attr_val */
1129 size_t substrlen
; /* length of "substring" */
1131 /* Convenience pointers we use when comparing substrings */
1135 substrlen
= lwc_string_length(substring
);
1136 if (substrlen
== 0) {
1137 /* In this case, the spec says that "if 'val' is the
1138 * empty string, it will never represent anything." */
1142 /* Intern the attribute name as a dom_string so we can
1143 * use dom_element_get_attribute() */
1144 err
= dom_string_create_interned(
1145 (const uint8_t *) lwc_string_data(qname
->name
),
1146 lwc_string_length(qname
->name
),
1148 if (err
!= DOM_NO_ERR
) {
1152 err
= dom_element_get_attribute((dom_node
*)node
, name
, &attr_val
);
1153 if ((err
!= DOM_NO_ERR
) || (attr_val
== NULL
)) {
1154 /* There was an error getting the attribute's value or
1155 * the attribute doesn't exist. So, no match? */
1156 dom_string_unref(name
);
1161 /* Otherwise, we have the attribute value from the given node,
1162 * and the first thing we want to do is check to see if the
1163 * whole thing matches the substring. */
1164 dom_string_unref(name
);
1165 *match
= dom_string_lwc_isequal(attr_val
, substring
);
1167 /* If not, check to see if an, uh, substring matches the
1169 if (*match
== false) {
1170 p
= dom_string_data(attr_val
);
1172 /* Check every long-enough suffix for a prefix match */
1173 attr_len
= dom_string_byte_length(attr_val
);
1174 if (attr_len
>= substrlen
) {
1175 p_max
= p
+ attr_len
- substrlen
;
1176 while (p
<= p_max
) {
1178 lwc_string_data(substring
),
1188 dom_string_unref(attr_val
);