]> gitweb.michael.orlitzky.com - libsvgtiny.git/blob - src/svgtiny_css.c
src/svgtiny_css.c: implement node_has_attribute_dashmatch() select handler
[libsvgtiny.git] / src / svgtiny_css.c
1 #include <libcss/libcss.h>
2 #include <strings.h> /* strncasecmp */
3
4 #include "svgtiny.h"
5 #include "svgtiny_internal.h"
6
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,
29 bool *match);
30 static css_error node_has_attribute_dashmatch(void *pw, void *node,
31 const css_qname *qname, lwc_string *value,
32 bool *match);
33
34
35 /**
36 * Resolve a relative URL to an absolute one by doing nothing. This is
37 * the simplest possible implementation of a URL resolver, needed for
38 * parsing CSS.
39 */
40 css_error svgtiny_resolve_url(void *pw,
41 const char *base, lwc_string *rel, lwc_string **abs)
42 {
43 UNUSED(pw);
44 UNUSED(base);
45
46 /* Copy the relative URL to the absolute one (the return
47 value) */
48 *abs = lwc_string_ref(rel);
49 return CSS_OK;
50 }
51
52 /**
53 * Create a stylesheet with the default set of params.
54 *
55 * \param sheet A stylesheet pointer, passed in by reference, that
56 * we use to store the newly-created stylesheet.
57 * \param inline_style True if this stylesheet represents an inline
58 * style, and false otherwise.
59 *
60 * \return The return value from css_stylesheet_create() is returned.
61 */
62 css_error svgtiny_create_stylesheet(css_stylesheet **sheet,
63 bool inline_style)
64 {
65 css_stylesheet_params params;
66
67 params.params_version = CSS_STYLESHEET_PARAMS_VERSION_1;
68 params.level = CSS_LEVEL_DEFAULT;
69 params.charset = NULL;
70 params.url = "";
71 params.title = NULL;
72 params.allow_quirks = false;
73 params.inline_style = inline_style;
74 params.resolve = svgtiny_resolve_url;
75 params.resolve_pw = NULL;
76 params.import = NULL;
77 params.import_pw = NULL;
78 params.color = NULL;
79 params.color_pw = NULL;
80 params.font = NULL;
81 params.font_pw = NULL;
82
83 return css_stylesheet_create(&params, sheet);
84 }
85
86
87 /**************************/
88 /* libcss select handlers */
89 /**************************/
90 /*
91 * From here on we implement the "select handler "API defined in
92 * libcss's include/libcss/select.h and discussed briefly in its
93 * docs/API document.
94 */
95
96
97 /**
98 * Retrieve the given node's name
99 *
100 * \param pw Pointer to the current SVG parser state
101 * \param node Libdom SVG node
102 * \param qname Address at which to store the node name
103 *
104 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
105 */
106 css_error node_name(void *pw, void *node, css_qname *qname)
107 {
108 dom_string *name;
109 dom_exception err;
110 struct svgtiny_parse_state *state;
111
112 err = dom_node_get_node_name((dom_node *)node, &name);
113 if (err != DOM_NO_ERR) {
114 return CSS_NOMEM;
115 }
116
117 state = (struct svgtiny_parse_state *)pw;
118 qname->ns = lwc_string_ref(state->interned_svg_xmlns);
119
120 err = dom_string_intern(name, &qname->name);
121 if (err != DOM_NO_ERR) {
122 dom_string_unref(name);
123 return CSS_NOMEM;
124 }
125
126 dom_string_unref(name);
127
128 return CSS_OK;
129 }
130
131
132 /**
133 * Retrieve the given node's classes
134 *
135 * \param pw Pointer to the current SVG parser state
136 * \param node Libdom SVG node
137 * \param classes Address at which to store the class name array
138 * \param n_classes Address at which to store the length of the class
139 * name array
140 *
141 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
142 *
143 * \note CSS_NOMEM is not possible in practice as of libdom-0.4.1,
144 * because the underlying libdom function never fails
145 */
146 css_error node_classes(void *pw, void *node,
147 lwc_string ***classes, uint32_t *n_classes)
148 {
149 UNUSED(pw);
150 dom_exception err;
151
152 err = dom_element_get_classes((dom_node *)node, classes, n_classes);
153
154 /* The implementation does not do it, but the documentation
155 for dom_element_get_classes() says that a DOM_NO_MEM_ERR is
156 possible here, so we handle it to be on the safe side. */
157 if (err != DOM_NO_ERR) {
158 return CSS_NOMEM;
159 }
160
161 return CSS_OK;
162 }
163
164
165 /**
166 * Retrieve the given node's id
167 *
168 * \param pw Pointer to the current SVG parser state
169 * \param node Libdom SVG node
170 * \param id Address at which to store the id
171 *
172 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
173 */
174 css_error node_id(void *pw, void *node, lwc_string **id)
175 {
176 dom_string *attr;
177 dom_exception err;
178 struct svgtiny_parse_state *state;
179
180 /* Begin with the assumption that this node has no id */
181 *id = NULL;
182
183 state = (struct svgtiny_parse_state *)pw;
184 err = dom_element_get_attribute((dom_node *)node,
185 state->interned_id, &attr);
186 if (err != DOM_NO_ERR) {
187 return CSS_NOMEM;
188 }
189 else if (attr == NULL) {
190 /* The node has no id attribute and our return value
191 is already set to NULL so we're done */
192 return CSS_OK;
193 }
194
195 /* If we found an id attribute (a dom_string), intern it into
196 an lwc_string that we can return, and then cleanup the
197 dom_string. */
198 err = dom_string_intern(attr, id);
199 if (err != DOM_NO_ERR) {
200 dom_string_unref(attr);
201 return CSS_NOMEM;
202 }
203 dom_string_unref(attr);
204 return CSS_OK;
205 }
206
207
208
209 /**
210 * Find the first parent of the given element having the given name
211 *
212 * \param pw Pointer to the current SVG parser state
213 * \param node Libdom SVG node
214 * \param qname Name of the parent node to search for
215 * \param parent Address at which to store the parent node pointer
216 *
217 * \return Always returns CSS_OK
218 *
219 * \post If a suitable element is found, a pointer to it will be
220 * stored at the address pointed to by \a parent; otherwise,
221 * NULL will be stored at the address pointed to by \a parent
222 */
223 css_error named_parent_node(void *pw, void *node,
224 const css_qname *qname, void **parent)
225 {
226 UNUSED(pw);
227 /* dom_element_named_parent_node() was invented to implement
228 * this select handler so there isn't much for us to do except
229 * call it. It's OK if node isn't an element, libdom checks
230 * for it. */
231 dom_element_named_parent_node((dom_element *)node,
232 qname->name,
233 (struct dom_element **)parent);
234
235 /* Implementation detail: dom_element_named_parent_node()
236 * increments the reference count of the parent element before
237 * returning it to us. According to docs/RefCnt in the libdom
238 * repository, this will prevent the parent element from being
239 * destroyed if it is pruned from the DOM. That sounds good,
240 * since we don't want to be using a pointer to an object that
241 * has been destroyed... but we also have no way of later
242 * decrementing the reference count ourselves, and don't want
243 * to make the returned node eternal. Decrementing the
244 * reference counter now allows it to be destroyed when the
245 * DOM no longer needs it, and so long as no other parts of
246 * libsvgtiny are messing with the DOM during parsing, that
247 * shouldn't (ha ha) cause any problems. */
248 dom_node_unref(*parent);
249
250 return CSS_OK;
251 }
252
253
254 /**
255 * Find the "next-sibling" of the given element having the given name
256 *
257 * This search corresponds to the "+ foo" combinator in CSS and will
258 * find only "foo" element nodes that immediately precede the given
259 * node under the same parent in the DOM. In CSS the tree is viewed
260 * top-down and in libdom it is viewed from the bottom-up; as a result
261 * "next" and "previous" are sometimes backwards. This is case-sensitive.
262 *
263 * \param pw Pointer to the current SVG parser state
264 * \param node Libdom SVG node
265 * \param qname Name of the sibling node to search for
266 * \param sibling Address at which to store the sibling node pointer
267 *
268 * \return Always returns CSS_OK
269 *
270 * \post If a suitable element is found, a pointer to it will be
271 * stored at the address pointed to by \a sibling; otherwise,
272 * NULL will be stored at the address pointed to by \a sibling
273 */
274 css_error named_sibling_node(void *pw, void *node,
275 const css_qname *qname, void **sibling)
276 {
277 UNUSED(pw);
278 dom_node *n = node; /* the current node */
279 dom_node *prev; /* the previous node */
280 dom_exception err;
281 dom_node_type type;
282 dom_string *name;
283
284 *sibling = NULL; /* default to nothing found */
285
286 /* Begin the search; the first iteration we do outside of the
287 * loop. Implementation detil: dom_node_get_previous_sibling()
288 * increments the reference counter on the returned node. A
289 * comment within named_parent_node() explains why we
290 * decrement it ASAP. */
291 err = dom_node_get_previous_sibling(n, &n);
292 if (err != DOM_NO_ERR) {
293 return CSS_OK;
294 }
295
296 while (n != NULL) {
297 /* We're looking for the first ELEMENT sibling */
298 err = dom_node_get_node_type(n, &type);
299 if (err != DOM_NO_ERR) {
300 dom_node_unref(n);
301 return CSS_OK;
302 }
303
304 if (type == DOM_ELEMENT_NODE) {
305 /* We found an element node, does it have the
306 * right name? */
307 err = dom_node_get_node_name(n, &name);
308 if (err != DOM_NO_ERR) {
309 dom_node_unref(n);
310 return CSS_OK;
311 }
312
313 if (dom_string_lwc_isequal(name,
314 qname->name)) {
315 /* The name is right, return it */
316 *sibling = n;
317 }
318
319 /* There's only one next-sibling element node
320 * and we've already found it, so if its name
321 * wasn't right, we return the default value
322 * of NULL below */
323 dom_string_unref(name);
324 dom_node_unref(n);
325 return CSS_OK;
326 }
327
328 /* Not an element node, so we move on the the previous
329 * previous sibling */
330 err = dom_node_get_previous_sibling(n, &prev);
331 if (err != DOM_NO_ERR) {
332 dom_node_unref(n);
333 return CSS_OK;
334 }
335
336 dom_node_unref(n);
337 n = prev;
338 }
339
340 return CSS_OK;
341 }
342
343
344 /**
345 * Find the first "subsequent-sibling" of the given element having the
346 * given name
347 *
348 * This search corresponds to the "~ foo" combinator in CSS and will
349 * find only "foo" element nodes that precede the given node (under
350 * the same parent) in the DOM. In CSS the tree is viewed top-down and
351 * in libdom it is viewed from the bottom-up; as a result "next" and
352 * "previous" are sometimes backwards. This is case-sensitive.
353 *
354 * \param pw Pointer to the current SVG parser state
355 * \param node Libdom SVG node
356 * \param qname Name of the sibling node to search for
357 * \param sibling Address at which to store the sibling node pointer
358 *
359 * \return Always returns CSS_OK
360 *
361 * \post If a suitable element is found, a pointer to it will be
362 * stored at the address pointed to by \a sibling; otherwise,
363 * NULL will be stored at the address pointed to by \a sibling
364 */
365 css_error named_generic_sibling_node(void *pw, void *node,
366 const css_qname *qname, void **sibling)
367 {
368 UNUSED(pw);
369 dom_node *n = node; /* the current node */
370 dom_node *prev; /* the previous node */
371 dom_exception err;
372 dom_node_type type;
373 dom_string *name;
374
375
376 *sibling = NULL; /* default to nothing found */
377
378 /* Begin the search; the first iteration we do outside of the
379 * loop. Implementation detil: dom_node_get_previous_sibling()
380 * increments the reference counter on the returned node. A
381 * comment within named_parent_node() explains why we
382 * decrement it ASAP. */
383 err = dom_node_get_previous_sibling(n, &n);
384 if (err != DOM_NO_ERR) {
385 return CSS_OK;
386 }
387
388 while (n != NULL) {
389 err = dom_node_get_node_type(n, &type);
390 if (err != DOM_NO_ERR) {
391 dom_node_unref(n);
392 return CSS_OK;
393 }
394
395 if (type == DOM_ELEMENT_NODE) {
396 /* We only want ELEMENT nodes */
397 err = dom_node_get_node_name(n, &name);
398 if (err != DOM_NO_ERR) {
399 dom_node_unref(n);
400 return CSS_OK;
401 }
402
403 if (dom_string_lwc_isequal(name,
404 qname->name)) {
405 /* Found one. Save it and stop the search */
406 dom_string_unref(name);
407 dom_node_unref(n);
408 *sibling = n;
409 return CSS_OK;
410 }
411
412 dom_string_unref(name);
413 }
414
415 /* This sibling wasn't an element with the desired
416 name, so move on to the previous sibling */
417 err = dom_node_get_previous_sibling(n, &prev);
418 if (err != DOM_NO_ERR) {
419 dom_node_unref(n);
420 return CSS_OK;
421 }
422
423 dom_node_unref(n);
424 n = prev;
425 }
426
427 return CSS_OK;
428 }
429
430
431 /**
432 * Return a pointer to the given node's parent
433 *
434 * \param pw Pointer to the current SVG parser state
435 * \param node Libdom SVG node
436 * \param parent Address at which to store the node's parent pointer
437 *
438 * \return Always returns CSS_OK
439 */
440 css_error parent_node(void *pw, void *node, void **parent)
441 {
442 UNUSED(pw);
443 /* Libdom basically implements this for us */
444 dom_element_parent_node(node, (struct dom_element **)parent);
445
446 /* See the comment in named_parent_node() for why we decrement
447 * this reference counter here. */
448 dom_node_unref(*parent);
449
450 return CSS_OK;
451 }
452
453
454 /**
455 * Find the "next-sibling" of the given element
456 *
457 * This search corresponds "+ *" in CSS and will find the first
458 * element node that immediately precedes the given node under the
459 * same parent in the DOM. In CSS the tree is viewed top-down and in
460 * libdom it is viewed from the bottom-up; as a result "next" and
461 * "previous" are sometimes backwards.
462 *
463 * \param pw Pointer to the current SVG parser state
464 * \param node Libdom SVG node
465 * \param sibling Address at which to store the sibling node pointer
466 *
467 * \return Always returns CSS_OK
468 *
469 * \post If a suitable element is found, a pointer to it will be
470 * stored at the address pointed to by \a sibling; otherwise,
471 * NULL will be stored at the address pointed to by \a sibling
472 */
473 css_error sibling_node(void *pw, void *node, void **sibling)
474 {
475 UNUSED(pw);
476 dom_node *n = node; /* the current node */
477 dom_node *prev; /* the previous node */
478 dom_exception err;
479 dom_node_type type;
480
481 *sibling = NULL; /* default to nothing found */
482
483 /* Begin the search; the first iteration we do outside of the
484 * loop. Implementation detil: dom_node_get_previous_sibling()
485 * increments the reference counter on the returned node. A
486 * comment within named_parent_node() explains why we
487 * decrement it ASAP. */
488 err = dom_node_get_previous_sibling(n, &n);
489 if (err != DOM_NO_ERR) {
490 return CSS_OK;
491 }
492
493 while (n != NULL) {
494 err = dom_node_get_node_type(n, &type);
495 if (err != DOM_NO_ERR) {
496 dom_node_unref(n);
497 return CSS_OK;
498 }
499
500 if (type == DOM_ELEMENT_NODE) {
501 /* We found a sibling node that is also an
502 element and that's all we wanted. */
503 *sibling = n;
504 dom_node_unref(n);
505 return CSS_OK;
506 }
507
508 /* This sibling node was not an element; move on to
509 the previous sibling */
510 err = dom_node_get_previous_sibling(n, &prev);
511 if (err != DOM_NO_ERR) {
512 dom_node_unref(n);
513 return CSS_OK;
514 }
515
516 dom_node_unref(n);
517 n = prev;
518 }
519
520 return CSS_OK;
521 }
522
523
524 /**
525 * Test the given node for the given name
526 *
527 * This will return true (via the "match" pointer) if the libdom node
528 * has the given name or if that name is the universal selector;
529 * otherwise it returns false. The comparison is case-sensitive. It
530 * corresponds to a rule like "body { ... }" in CSS.
531 *
532 * \param pw Pointer to the current SVG parser state
533 * \param node Libdom SVG node to test
534 * \param qname Name to check for
535 * \param match Pointer to the test result
536 *
537 * \return Always returns CSS_OK
538 */
539 css_error node_has_name(void *pw, void *node,
540 const css_qname *qname, bool *match)
541 {
542 struct svgtiny_parse_state *state;
543 dom_string *name;
544 dom_exception err;
545
546 /* Start by checking to see if qname is the universal selector */
547 state = (struct svgtiny_parse_state *)pw;
548 if (lwc_string_isequal(qname->name,
549 state->interned_universal, match) == lwc_error_ok) {
550 if (*match) {
551 /* It's the universal selector. In NetSurf, all node
552 * names match the universal selector, and nothing in
553 * the libcss documentation suggests another approach,
554 * so we follow NetSurf here. */
555 return CSS_OK;
556 }
557 }
558
559 err = dom_node_get_node_name((dom_node *)node, &name);
560 if (err != DOM_NO_ERR) {
561 return CSS_OK;
562 }
563
564 /* Unlike with HTML, SVG element names are case-sensitive */
565 *match = dom_string_lwc_isequal(name, qname->name);
566 dom_string_unref(name);
567
568 return CSS_OK;
569 }
570
571
572 /**
573 * Test the given node for the given class
574 *
575 * This will return true (via the "match" pointer) if the libdom node
576 * has the given class. The comparison is case-sensitive. It
577 * corresponds to node.class in CSS.
578 *
579 * \param pw Pointer to the current SVG parser state
580 * \param node Libdom SVG node to test
581 * \param name Class name to check for
582 * \param match Pointer to the test result
583 *
584 * \return Always returns CSS_OK
585 */
586 css_error node_has_class(void *pw, void *node,
587 lwc_string *name, bool *match)
588 {
589 UNUSED(pw);
590 /* libdom implements this for us and apparently it cannot fail */
591 dom_element_has_class((dom_node *)node, name, match);
592 return CSS_OK;
593 }
594
595
596 /**
597 * Test the given node for the given id
598 *
599 * This will return true (via the "match" pointer) if the libdom node
600 * has the given id. The comparison is case-sensitive. It corresponds
601 * to node#id in CSS.
602 *
603 * \param pw Pointer to the current SVG parser state
604 * \param node Libdom SVG node to test
605 * \param name Id to check for
606 * \param match Pointer to the test result
607 *
608 * \return Always returns CSS_OK
609 */
610 css_error node_has_id(void *pw, void *node,
611 lwc_string *name, bool *match)
612 {
613 dom_string *attr;
614 dom_exception err;
615 struct svgtiny_parse_state *state;
616
617 attr = NULL; /* a priori the "id" attribute may not exist */
618 *match = false; /* default to no match */
619
620 state = (struct svgtiny_parse_state *)pw;
621 err = dom_element_get_attribute((dom_node *)node,
622 state->interned_id, &attr);
623 if (err != DOM_NO_ERR || attr == NULL) {
624 return CSS_OK;
625 }
626
627 *match = dom_string_lwc_isequal(attr, name);
628 dom_string_unref(attr);
629
630 return CSS_OK;
631 }
632
633
634 /**
635 * Test the given node for the given attribute
636 *
637 * This will return true (via the "match" pointer) if the libdom node
638 * has an attribute with the given name. The comparison is
639 * case-sensitive. It corresponds to node[attr] in CSS.
640 *
641 * \param pw Pointer to the current SVG parser state
642 * \param node Libdom SVG node to test
643 * \param qname Attribute name to check for
644 * \param match Pointer to the test result
645 *
646 * \return Returns CSS_OK if successful and CSS_NOMEM if anything
647 * goes wrong
648 */
649 css_error node_has_attribute(void *pw, void *node,
650 const css_qname *qname, bool *match)
651 {
652 UNUSED(pw);
653 dom_string *name;
654 dom_exception err;
655
656 /* intern the attribute name as a dom_string so we can
657 * delegate to dom_element_has_attribute() */
658 err = dom_string_create_interned(
659 (const uint8_t *) lwc_string_data(qname->name),
660 lwc_string_length(qname->name),
661 &name);
662 if (err != DOM_NO_ERR) {
663 return CSS_NOMEM;
664 }
665
666 err = dom_element_has_attribute((dom_node *)node, name, match);
667 if (err != DOM_NO_ERR) {
668 dom_string_unref(name);
669 return CSS_OK;
670 }
671
672 dom_string_unref(name);
673 return CSS_OK;
674 }
675
676
677 /**
678 * Test the given node for an attribute with a specific value
679 *
680 * This will return true (via the "match" pointer) if the libdom node
681 * has an attribute with the given name and value. The comparison is
682 * case-sensitive. It corresponds to node[attr=value] in CSS.
683 *
684 * \param pw Pointer to the current SVG parser state
685 * \param node Libdom SVG node to test
686 * \param qname Attribute name to check for
687 * \param value Attribute value to check for
688 * \param match Pointer to the test result
689 *
690 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
691 * intern the attribute name (which usually indicates memory
692 * exhaustion)
693 */
694 css_error node_has_attribute_equal(void *pw, void *node,
695 const css_qname *qname, lwc_string *value,
696 bool *match)
697 {
698 /* Implementation note: NetSurf always returns "no match" when
699 * the value is empty (length zero). We allow it, because why
700 * not? */
701
702 UNUSED(pw);
703 dom_string *name;
704 dom_string *attr_val;
705 dom_exception err;
706
707 /* Intern the attribute name as a dom_string so we can
708 * use dom_element_get_attribute() */
709 err = dom_string_create_interned(
710 (const uint8_t *) lwc_string_data(qname->name),
711 lwc_string_length(qname->name),
712 &name);
713 if (err != DOM_NO_ERR) {
714 return CSS_NOMEM;
715 }
716
717 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
718 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
719 /* There was an error getting the attribute's value or
720 * the attribute doesn't exist. So, no match? */
721 dom_string_unref(name);
722 *match = false;
723 return CSS_OK;
724 }
725
726 /* Otherwise, we have the attribute value from the given node
727 * and all we need to do is compare. */
728 dom_string_unref(name);
729 *match = dom_string_lwc_isequal(attr_val, value);
730 dom_string_unref(attr_val);
731
732 return CSS_OK;
733 }
734
735
736 /**
737 * Test the given node for an attribute with a specific value,
738 * possibly followed by a single hyphen
739 *
740 * This will return true (via the "match" pointer) if the libdom node
741 * has an attribute with the given name and value or with the given
742 * name and a value that is followed by exactly one hyphen. The
743 * comparison is case-sensitive. This corresponds to [attr|=value]
744 * in CSS.
745 *
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 value Attribute value to check for
750 * \param match Pointer to the test result
751 *
752 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
753 * intern the attribute name (which usually indicates memory
754 * exhaustion)
755 */
756 css_error node_has_attribute_dashmatch(void *pw, void *node,
757 const css_qname *qname, lwc_string *value,
758 bool *match)
759 {
760 /* Implementation note: NetSurf always returns "no match" when
761 * the value is empty (length zero). We allow it, because why
762 * not? */
763
764 UNUSED(pw);
765 dom_string *name;
766 dom_string *attr_val;
767 dom_exception err;
768
769 const char *vdata; /* to hold the data underlying "value" */
770 size_t vdata_len;
771 const char *avdata; /* to hold the found attribute value data */
772 size_t avdata_len;
773
774 /* Intern the attribute name as a dom_string so we can
775 * use dom_element_get_attribute() */
776 err = dom_string_create_interned(
777 (const uint8_t *) lwc_string_data(qname->name),
778 lwc_string_length(qname->name),
779 &name);
780 if (err != DOM_NO_ERR) {
781 return CSS_NOMEM;
782 }
783
784 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
785 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
786 /* There was an error getting the attribute's value or
787 * the attribute doesn't exist. So, no match? */
788 dom_string_unref(name);
789 *match = false;
790 return CSS_OK;
791 }
792
793 /* Otherwise, we have the attribute value from the given node
794 * and all we need to do is compare. */
795 dom_string_unref(name);
796 *match = dom_string_lwc_isequal(attr_val, value);
797 if (*match) {
798 /* Exact match, we're done */
799 dom_string_unref(attr_val);
800 return CSS_OK;
801 }
802
803 /* No exact match, try it with a hyphen on the end */
804 vdata = lwc_string_data(value); /* needle */
805 vdata_len = lwc_string_length(value);
806 avdata = dom_string_data(attr_val); /* haystack */
807 avdata_len = dom_string_byte_length(attr_val);
808 dom_string_unref(attr_val);
809
810 if (avdata_len > vdata_len && avdata[vdata_len] == '-') {
811 if (strncasecmp(avdata, vdata, vdata_len) == 0) {
812 /* If there's a hyphen in the right position,
813 * it suffices to compare the strings only up
814 * to the hyphen */
815 *match = true;
816 }
817 }
818
819 return CSS_OK;
820 }