]> gitweb.michael.orlitzky.com - libsvgtiny.git/blob - src/svgtiny_css.c
f01c3b40df17ca066611aec5a3458c8d6d104bd2
[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 static css_error node_has_attribute_includes(void *pw, void *node,
34 const css_qname *qname, lwc_string *word,
35 bool *match);
36 static css_error node_has_attribute_prefix(void *pw, void *node,
37 const css_qname *qname, lwc_string *prefix,
38 bool *match);
39 static css_error node_has_attribute_suffix(void *pw, void *node,
40 const css_qname *qname, lwc_string *suffix,
41 bool *match);
42 static css_error node_has_attribute_substring(void *pw, void *node,
43 const css_qname *qname, lwc_string *substring,
44 bool *match);
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);
49 static css_error node_is_link(void *pw, void *node, bool *is_link);
50 static css_error node_is_visited(void *pw, void *node, bool *is_visited);
51 static css_error node_is_hover(void *pw, void *node, bool *is_hover);
52 static css_error node_is_active(void *pw, void *node, bool *is_active);
53 static css_error node_is_focus(void *pw, void *node, bool *is_focus);
54 static css_error node_is_enabled(void *pw, void *node, bool *is_enabled);
55 static css_error node_is_disabled(void *pw, void *node, bool *is_disabled);
56 static css_error node_is_checked(void *pw, void *node, bool *is_checked);
57 static css_error node_is_target(void *pw, void *node, bool *is_target);
58 static css_error node_is_lang(void *pw, void *node,
59 lwc_string *lang, bool *is_lang);
60 static css_error node_presentational_hint(void *pw, void *node,
61 uint32_t *nhints, css_hint **hints);
62 static css_error ua_default_for_property(void *pw, uint32_t property,
63 css_hint *hint);
64 static css_error set_libcss_node_data(void *pw, void *node,
65 void *libcss_node_data);
66 static css_error get_libcss_node_data(void *pw, void *node,
67 void **libcss_node_data);
68
69
70 /**
71 * Resolve a relative URL to an absolute one by doing nothing. This is
72 * the simplest possible implementation of a URL resolver, needed for
73 * parsing CSS.
74 */
75 css_error svgtiny_resolve_url(void *pw,
76 const char *base, lwc_string *rel, lwc_string **abs)
77 {
78 UNUSED(pw);
79 UNUSED(base);
80
81 /* Copy the relative URL to the absolute one (the return
82 value) */
83 *abs = lwc_string_ref(rel);
84 return CSS_OK;
85 }
86
87 /**
88 * Create a stylesheet with the default set of params.
89 *
90 * \param sheet A stylesheet pointer, passed in by reference, that
91 * we use to store the newly-created stylesheet.
92 * \param inline_style True if this stylesheet represents an inline
93 * style, and false otherwise.
94 *
95 * \return The return value from css_stylesheet_create() is returned.
96 */
97 css_error svgtiny_create_stylesheet(css_stylesheet **sheet,
98 bool inline_style)
99 {
100 css_stylesheet_params params;
101
102 params.params_version = CSS_STYLESHEET_PARAMS_VERSION_1;
103 params.level = CSS_LEVEL_DEFAULT;
104 params.charset = NULL;
105 params.url = "";
106 params.title = NULL;
107 params.allow_quirks = false;
108 params.inline_style = inline_style;
109 params.resolve = svgtiny_resolve_url;
110 params.resolve_pw = NULL;
111 params.import = NULL;
112 params.import_pw = NULL;
113 params.color = NULL;
114 params.color_pw = NULL;
115 params.font = NULL;
116 params.font_pw = NULL;
117
118 return css_stylesheet_create(&params, sheet);
119 }
120
121
122 /**************************/
123 /* libcss select handlers */
124 /**************************/
125 /*
126 * From here on we implement the "select handler "API defined in
127 * libcss's include/libcss/select.h and discussed briefly in its
128 * docs/API document.
129 */
130
131
132 /**
133 * Retrieve the given node's name
134 *
135 * \param pw Pointer to the current SVG parser state
136 * \param node Libdom SVG node
137 * \param qname Address at which to store the node name
138 *
139 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
140 */
141 css_error node_name(void *pw, void *node, css_qname *qname)
142 {
143 dom_string *name;
144 dom_exception err;
145 struct svgtiny_parse_state *state;
146
147 err = dom_node_get_node_name((dom_node *)node, &name);
148 if (err != DOM_NO_ERR) {
149 return CSS_NOMEM;
150 }
151
152 state = (struct svgtiny_parse_state *)pw;
153 qname->ns = lwc_string_ref(state->interned_svg_xmlns);
154
155 err = dom_string_intern(name, &qname->name);
156 if (err != DOM_NO_ERR) {
157 dom_string_unref(name);
158 return CSS_NOMEM;
159 }
160
161 dom_string_unref(name);
162
163 return CSS_OK;
164 }
165
166
167 /**
168 * Retrieve the given node's classes
169 *
170 * \param pw Pointer to the current SVG parser state
171 * \param node Libdom SVG node
172 * \param classes Address at which to store the class name array
173 * \param n_classes Address at which to store the length of the class
174 * name array
175 *
176 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
177 *
178 * \note CSS_NOMEM is not possible in practice as of libdom-0.4.1,
179 * because the underlying libdom function never fails
180 */
181 css_error node_classes(void *pw, void *node,
182 lwc_string ***classes, uint32_t *n_classes)
183 {
184 UNUSED(pw);
185 dom_exception err;
186
187 err = dom_element_get_classes((dom_node *)node, classes, n_classes);
188
189 /* The implementation does not do it, but the documentation
190 for dom_element_get_classes() says that a DOM_NO_MEM_ERR is
191 possible here, so we handle it to be on the safe side. */
192 if (err != DOM_NO_ERR) {
193 return CSS_NOMEM;
194 }
195
196 return CSS_OK;
197 }
198
199
200 /**
201 * Retrieve the given node's id
202 *
203 * \param pw Pointer to the current SVG parser state
204 * \param node Libdom SVG node
205 * \param id Address at which to store the id
206 *
207 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
208 */
209 css_error node_id(void *pw, void *node, lwc_string **id)
210 {
211 dom_string *attr;
212 dom_exception err;
213 struct svgtiny_parse_state *state;
214
215 /* Begin with the assumption that this node has no id */
216 *id = NULL;
217
218 state = (struct svgtiny_parse_state *)pw;
219 err = dom_element_get_attribute((dom_node *)node,
220 state->interned_id, &attr);
221 if (err != DOM_NO_ERR) {
222 return CSS_NOMEM;
223 }
224 else if (attr == NULL) {
225 /* The node has no id attribute and our return value
226 is already set to NULL so we're done */
227 return CSS_OK;
228 }
229
230 /* If we found an id attribute (a dom_string), intern it into
231 an lwc_string that we can return, and then cleanup the
232 dom_string. */
233 err = dom_string_intern(attr, id);
234 if (err != DOM_NO_ERR) {
235 dom_string_unref(attr);
236 return CSS_NOMEM;
237 }
238 dom_string_unref(attr);
239 return CSS_OK;
240 }
241
242
243
244 /**
245 * Find the first parent of the given element having the given name
246 *
247 * \param pw Pointer to the current SVG parser state
248 * \param node Libdom SVG node
249 * \param qname Name of the parent node to search for
250 * \param parent Address at which to store the parent node pointer
251 *
252 * \return Always returns CSS_OK
253 *
254 * \post If a suitable element is found, a pointer to it will be
255 * stored at the address pointed to by \a parent; otherwise,
256 * NULL will be stored at the address pointed to by \a parent
257 */
258 css_error named_parent_node(void *pw, void *node,
259 const css_qname *qname, void **parent)
260 {
261 UNUSED(pw);
262 /* dom_element_named_parent_node() was invented to implement
263 * this select handler so there isn't much for us to do except
264 * call it. It's OK if node isn't an element, libdom checks
265 * for it. */
266 dom_element_named_parent_node((dom_element *)node,
267 qname->name,
268 (struct dom_element **)parent);
269
270 /* Implementation detail: dom_element_named_parent_node()
271 * increments the reference count of the parent element before
272 * returning it to us. According to docs/RefCnt in the libdom
273 * repository, this will prevent the parent element from being
274 * destroyed if it is pruned from the DOM. That sounds good,
275 * since we don't want to be using a pointer to an object that
276 * has been destroyed... but we also have no way of later
277 * decrementing the reference count ourselves, and don't want
278 * to make the returned node eternal. Decrementing the
279 * reference counter now allows it to be destroyed when the
280 * DOM no longer needs it, and so long as no other parts of
281 * libsvgtiny are messing with the DOM during parsing, that
282 * shouldn't (ha ha) cause any problems. */
283 dom_node_unref(*parent);
284
285 return CSS_OK;
286 }
287
288
289 /**
290 * Find the "next-sibling" of the given element having the given name
291 *
292 * This search corresponds to the "+ foo" combinator in CSS and will
293 * find only "foo" element nodes that immediately precede the given
294 * node under the same parent in the DOM. In CSS the tree is viewed
295 * top-down and in libdom it is viewed from the bottom-up; as a result
296 * "next" and "previous" are sometimes backwards. This is case-sensitive.
297 *
298 * \param pw Pointer to the current SVG parser state
299 * \param node Libdom SVG node
300 * \param qname Name of the sibling node to search for
301 * \param sibling Address at which to store the sibling node pointer
302 *
303 * \return Always returns CSS_OK
304 *
305 * \post If a suitable element is found, a pointer to it will be
306 * stored at the address pointed to by \a sibling; otherwise,
307 * NULL will be stored at the address pointed to by \a sibling
308 */
309 css_error named_sibling_node(void *pw, void *node,
310 const css_qname *qname, void **sibling)
311 {
312 UNUSED(pw);
313 dom_node *n = node; /* the current node */
314 dom_node *prev; /* the previous node */
315 dom_exception err;
316 dom_node_type type;
317 dom_string *name;
318
319 *sibling = NULL; /* default to nothing found */
320
321 /* Begin the search; the first iteration we do outside of the
322 * loop. Implementation detil: dom_node_get_previous_sibling()
323 * increments the reference counter on the returned node. A
324 * comment within named_parent_node() explains why we
325 * decrement it ASAP. */
326 err = dom_node_get_previous_sibling(n, &n);
327 if (err != DOM_NO_ERR) {
328 return CSS_OK;
329 }
330
331 while (n != NULL) {
332 /* We're looking for the first ELEMENT sibling */
333 err = dom_node_get_node_type(n, &type);
334 if (err != DOM_NO_ERR) {
335 dom_node_unref(n);
336 return CSS_OK;
337 }
338
339 if (type == DOM_ELEMENT_NODE) {
340 /* We found an element node, does it have the
341 * right name? */
342 err = dom_node_get_node_name(n, &name);
343 if (err != DOM_NO_ERR) {
344 dom_node_unref(n);
345 return CSS_OK;
346 }
347
348 if (dom_string_lwc_isequal(name,
349 qname->name)) {
350 /* The name is right, return it */
351 *sibling = n;
352 }
353
354 /* There's only one next-sibling element node
355 * and we've already found it, so if its name
356 * wasn't right, we return the default value
357 * of NULL below */
358 dom_string_unref(name);
359 dom_node_unref(n);
360 return CSS_OK;
361 }
362
363 /* Not an element node, so we move on the the previous
364 * previous sibling */
365 err = dom_node_get_previous_sibling(n, &prev);
366 if (err != DOM_NO_ERR) {
367 dom_node_unref(n);
368 return CSS_OK;
369 }
370
371 dom_node_unref(n);
372 n = prev;
373 }
374
375 return CSS_OK;
376 }
377
378
379 /**
380 * Find the first "subsequent-sibling" of the given element having the
381 * given name
382 *
383 * This search corresponds to the "~ foo" combinator in CSS and will
384 * find only "foo" element nodes that precede the given node (under
385 * the same parent) in the DOM. In CSS the tree is viewed top-down and
386 * in libdom it is viewed from the bottom-up; as a result "next" and
387 * "previous" are sometimes backwards. This is case-sensitive.
388 *
389 * \param pw Pointer to the current SVG parser state
390 * \param node Libdom SVG node
391 * \param qname Name of the sibling node to search for
392 * \param sibling Address at which to store the sibling node pointer
393 *
394 * \return Always returns CSS_OK
395 *
396 * \post If a suitable element is found, a pointer to it will be
397 * stored at the address pointed to by \a sibling; otherwise,
398 * NULL will be stored at the address pointed to by \a sibling
399 */
400 css_error named_generic_sibling_node(void *pw, void *node,
401 const css_qname *qname, void **sibling)
402 {
403 UNUSED(pw);
404 dom_node *n = node; /* the current node */
405 dom_node *prev; /* the previous node */
406 dom_exception err;
407 dom_node_type type;
408 dom_string *name;
409
410
411 *sibling = NULL; /* default to nothing found */
412
413 /* Begin the search; the first iteration we do outside of the
414 * loop. Implementation detil: dom_node_get_previous_sibling()
415 * increments the reference counter on the returned node. A
416 * comment within named_parent_node() explains why we
417 * decrement it ASAP. */
418 err = dom_node_get_previous_sibling(n, &n);
419 if (err != DOM_NO_ERR) {
420 return CSS_OK;
421 }
422
423 while (n != NULL) {
424 err = dom_node_get_node_type(n, &type);
425 if (err != DOM_NO_ERR) {
426 dom_node_unref(n);
427 return CSS_OK;
428 }
429
430 if (type == DOM_ELEMENT_NODE) {
431 /* We only want ELEMENT nodes */
432 err = dom_node_get_node_name(n, &name);
433 if (err != DOM_NO_ERR) {
434 dom_node_unref(n);
435 return CSS_OK;
436 }
437
438 if (dom_string_lwc_isequal(name,
439 qname->name)) {
440 /* Found one. Save it and stop the search */
441 dom_string_unref(name);
442 dom_node_unref(n);
443 *sibling = n;
444 return CSS_OK;
445 }
446
447 dom_string_unref(name);
448 }
449
450 /* This sibling wasn't an element with the desired
451 name, so move on to the previous sibling */
452 err = dom_node_get_previous_sibling(n, &prev);
453 if (err != DOM_NO_ERR) {
454 dom_node_unref(n);
455 return CSS_OK;
456 }
457
458 dom_node_unref(n);
459 n = prev;
460 }
461
462 return CSS_OK;
463 }
464
465
466 /**
467 * Return a pointer to the given node's parent
468 *
469 * \param pw Pointer to the current SVG parser state
470 * \param node Libdom SVG node
471 * \param parent Address at which to store the node's parent pointer
472 *
473 * \return Always returns CSS_OK
474 */
475 css_error parent_node(void *pw, void *node, void **parent)
476 {
477 UNUSED(pw);
478 /* Libdom basically implements this for us */
479 dom_element_parent_node(node, (struct dom_element **)parent);
480
481 /* See the comment in named_parent_node() for why we decrement
482 * this reference counter here. */
483 dom_node_unref(*parent);
484
485 return CSS_OK;
486 }
487
488
489 /**
490 * Find the "next-sibling" of the given element
491 *
492 * This search corresponds "+ *" in CSS and will find the first
493 * element node that immediately precedes the given node under the
494 * same parent in the DOM. In CSS the tree is viewed top-down and in
495 * libdom it is viewed from the bottom-up; as a result "next" and
496 * "previous" are sometimes backwards.
497 *
498 * \param pw Pointer to the current SVG parser state
499 * \param node Libdom SVG node
500 * \param sibling Address at which to store the sibling node pointer
501 *
502 * \return Always returns CSS_OK
503 *
504 * \post If a suitable element is found, a pointer to it will be
505 * stored at the address pointed to by \a sibling; otherwise,
506 * NULL will be stored at the address pointed to by \a sibling
507 */
508 css_error sibling_node(void *pw, void *node, void **sibling)
509 {
510 UNUSED(pw);
511 dom_node *n = node; /* the current node */
512 dom_node *prev; /* the previous node */
513 dom_exception err;
514 dom_node_type type;
515
516 *sibling = NULL; /* default to nothing found */
517
518 /* Begin the search; the first iteration we do outside of the
519 * loop. Implementation detil: dom_node_get_previous_sibling()
520 * increments the reference counter on the returned node. A
521 * comment within named_parent_node() explains why we
522 * decrement it ASAP. */
523 err = dom_node_get_previous_sibling(n, &n);
524 if (err != DOM_NO_ERR) {
525 return CSS_OK;
526 }
527
528 while (n != NULL) {
529 err = dom_node_get_node_type(n, &type);
530 if (err != DOM_NO_ERR) {
531 dom_node_unref(n);
532 return CSS_OK;
533 }
534
535 if (type == DOM_ELEMENT_NODE) {
536 /* We found a sibling node that is also an
537 element and that's all we wanted. */
538 *sibling = n;
539 dom_node_unref(n);
540 return CSS_OK;
541 }
542
543 /* This sibling node was not an element; move on to
544 the previous sibling */
545 err = dom_node_get_previous_sibling(n, &prev);
546 if (err != DOM_NO_ERR) {
547 dom_node_unref(n);
548 return CSS_OK;
549 }
550
551 dom_node_unref(n);
552 n = prev;
553 }
554
555 return CSS_OK;
556 }
557
558
559 /**
560 * Test the given node for the given name
561 *
562 * This will return true (via the "match" pointer) if the libdom node
563 * has the given name or if that name is the universal selector;
564 * otherwise it returns false. The comparison is case-sensitive. It
565 * corresponds to a rule like "body { ... }" in CSS.
566 *
567 * \param pw Pointer to the current SVG parser state
568 * \param node Libdom SVG node to test
569 * \param qname Name to check for
570 * \param match Pointer to the test result
571 *
572 * \return Always returns CSS_OK
573 */
574 css_error node_has_name(void *pw, void *node,
575 const css_qname *qname, bool *match)
576 {
577 struct svgtiny_parse_state *state;
578 dom_string *name;
579 dom_exception err;
580
581 /* Start by checking to see if qname is the universal selector */
582 state = (struct svgtiny_parse_state *)pw;
583 if (lwc_string_isequal(qname->name,
584 state->interned_universal, match) == lwc_error_ok) {
585 if (*match) {
586 /* It's the universal selector. In NetSurf, all node
587 * names match the universal selector, and nothing in
588 * the libcss documentation suggests another approach,
589 * so we follow NetSurf here. */
590 return CSS_OK;
591 }
592 }
593
594 err = dom_node_get_node_name((dom_node *)node, &name);
595 if (err != DOM_NO_ERR) {
596 return CSS_OK;
597 }
598
599 /* Unlike with HTML, SVG element names are case-sensitive */
600 *match = dom_string_lwc_isequal(name, qname->name);
601 dom_string_unref(name);
602
603 return CSS_OK;
604 }
605
606
607 /**
608 * Test the given node for the given class
609 *
610 * This will return true (via the "match" pointer) if the libdom node
611 * has the given class. The comparison is case-sensitive. It
612 * corresponds to node.class in CSS.
613 *
614 * \param pw Pointer to the current SVG parser state
615 * \param node Libdom SVG node to test
616 * \param name Class name to check for
617 * \param match Pointer to the test result
618 *
619 * \return Always returns CSS_OK
620 */
621 css_error node_has_class(void *pw, void *node,
622 lwc_string *name, bool *match)
623 {
624 UNUSED(pw);
625 /* libdom implements this for us and apparently it cannot fail */
626 dom_element_has_class((dom_node *)node, name, match);
627 return CSS_OK;
628 }
629
630
631 /**
632 * Test the given node for the given id
633 *
634 * This will return true (via the "match" pointer) if the libdom node
635 * has the given id. The comparison is case-sensitive. It corresponds
636 * to node#id in CSS.
637 *
638 * \param pw Pointer to the current SVG parser state
639 * \param node Libdom SVG node to test
640 * \param name Id to check for
641 * \param match Pointer to the test result
642 *
643 * \return Always returns CSS_OK
644 */
645 css_error node_has_id(void *pw, void *node,
646 lwc_string *name, bool *match)
647 {
648 dom_string *attr;
649 dom_exception err;
650 struct svgtiny_parse_state *state;
651
652 attr = NULL; /* a priori the "id" attribute may not exist */
653 *match = false; /* default to no match */
654
655 state = (struct svgtiny_parse_state *)pw;
656 err = dom_element_get_attribute((dom_node *)node,
657 state->interned_id, &attr);
658 if (err != DOM_NO_ERR || attr == NULL) {
659 return CSS_OK;
660 }
661
662 *match = dom_string_lwc_isequal(attr, name);
663 dom_string_unref(attr);
664
665 return CSS_OK;
666 }
667
668
669 /**
670 * Test the given node for the given attribute
671 *
672 * This will return true (via the "match" pointer) if the libdom node
673 * has an attribute with the given name. The comparison is
674 * case-sensitive. It corresponds to node[attr] in CSS.
675 *
676 * \param pw Pointer to the current SVG parser state
677 * \param node Libdom SVG node to test
678 * \param qname Attribute name to check for
679 * \param match Pointer to the test result
680 *
681 * \return Returns CSS_OK if successful and CSS_NOMEM if anything
682 * goes wrong
683 */
684 css_error node_has_attribute(void *pw, void *node,
685 const css_qname *qname, bool *match)
686 {
687 UNUSED(pw);
688 dom_string *name;
689 dom_exception err;
690
691 /* intern the attribute name as a dom_string so we can
692 * delegate to dom_element_has_attribute() */
693 err = dom_string_create_interned(
694 (const uint8_t *) lwc_string_data(qname->name),
695 lwc_string_length(qname->name),
696 &name);
697 if (err != DOM_NO_ERR) {
698 return CSS_NOMEM;
699 }
700
701 err = dom_element_has_attribute((dom_node *)node, name, match);
702 if (err != DOM_NO_ERR) {
703 dom_string_unref(name);
704 return CSS_OK;
705 }
706
707 dom_string_unref(name);
708 return CSS_OK;
709 }
710
711
712 /**
713 * Test the given node for an attribute with a specific value
714 *
715 * This will return true (via the "match" pointer) if the libdom node
716 * has an attribute with the given name and value. The comparison is
717 * case-sensitive. It corresponds to node[attr=value] in CSS.
718 *
719 * \param pw Pointer to the current SVG parser state
720 * \param node Libdom SVG node to test
721 * \param qname Attribute name to check for
722 * \param value Attribute value to check for
723 * \param match Pointer to the test result
724 *
725 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
726 * intern the attribute name (which usually indicates memory
727 * exhaustion)
728 */
729 css_error node_has_attribute_equal(void *pw, void *node,
730 const css_qname *qname, lwc_string *value,
731 bool *match)
732 {
733 /* Implementation note: NetSurf always returns "no match" when
734 * the value is empty (length zero). We allow it, because why
735 * not? */
736
737 UNUSED(pw);
738 dom_string *name;
739 dom_string *attr_val;
740 dom_exception err;
741
742 /* Intern the attribute name as a dom_string so we can
743 * use dom_element_get_attribute() */
744 err = dom_string_create_interned(
745 (const uint8_t *) lwc_string_data(qname->name),
746 lwc_string_length(qname->name),
747 &name);
748 if (err != DOM_NO_ERR) {
749 return CSS_NOMEM;
750 }
751
752 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
753 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
754 /* There was an error getting the attribute's value or
755 * the attribute doesn't exist. So, no match? */
756 dom_string_unref(name);
757 *match = false;
758 return CSS_OK;
759 }
760
761 /* Otherwise, we have the attribute value from the given node
762 * and all we need to do is compare. */
763 dom_string_unref(name);
764 *match = dom_string_lwc_isequal(attr_val, value);
765 dom_string_unref(attr_val);
766
767 return CSS_OK;
768 }
769
770
771 /**
772 * Test the given node for an attribute with a specific value,
773 * possibly followed by a single hyphen
774 *
775 * This will return true (via the "match" pointer) if the libdom node
776 * has an attribute with the given name and value or with the given
777 * name and a value that is followed by exactly one hyphen. The
778 * comparison is case-sensitive. This corresponds to [attr|=value]
779 * in CSS.
780 *
781 * \param pw Pointer to the current SVG parser state
782 * \param node Libdom SVG node to test
783 * \param qname Attribute name to check for
784 * \param value Attribute value to check for
785 * \param match Pointer to the test result
786 *
787 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
788 * intern the attribute name (which usually indicates memory
789 * exhaustion)
790 */
791 css_error node_has_attribute_dashmatch(void *pw, void *node,
792 const css_qname *qname, lwc_string *value,
793 bool *match)
794 {
795 /* Implementation note: NetSurf always returns "no match" when
796 * the value is empty (length zero). We allow it, because why
797 * not? */
798
799 UNUSED(pw);
800 dom_string *name;
801 dom_string *attr_val;
802 dom_exception err;
803
804 const char *vdata; /* to hold the data underlying "value" */
805 size_t vdata_len;
806 const char *avdata; /* to hold the found attribute value data */
807 size_t avdata_len;
808
809 /* Intern the attribute name as a dom_string so we can
810 * use dom_element_get_attribute() */
811 err = dom_string_create_interned(
812 (const uint8_t *) lwc_string_data(qname->name),
813 lwc_string_length(qname->name),
814 &name);
815 if (err != DOM_NO_ERR) {
816 return CSS_NOMEM;
817 }
818
819 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
820 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
821 /* There was an error getting the attribute's value or
822 * the attribute doesn't exist. So, no match? */
823 dom_string_unref(name);
824 *match = false;
825 return CSS_OK;
826 }
827
828 /* Otherwise, we have the attribute value from the given node
829 * and all we need to do is compare. */
830 dom_string_unref(name);
831 *match = dom_string_lwc_isequal(attr_val, value);
832 if (*match) {
833 /* Exact match, we're done */
834 dom_string_unref(attr_val);
835 return CSS_OK;
836 }
837
838 /* No exact match, try it with a hyphen on the end */
839 vdata = lwc_string_data(value); /* needle */
840 vdata_len = lwc_string_length(value);
841 avdata = dom_string_data(attr_val); /* haystack */
842 avdata_len = dom_string_byte_length(attr_val);
843 dom_string_unref(attr_val);
844
845 if (avdata_len > vdata_len && avdata[vdata_len] == '-') {
846 if (strncasecmp(avdata, vdata, vdata_len) == 0) {
847 /* If there's a hyphen in the right position,
848 * it suffices to compare the strings only up
849 * to the hyphen */
850 *match = true;
851 }
852 }
853
854 return CSS_OK;
855 }
856
857
858 /**
859 * Test the given node for an attribute whose value is a
860 * space-separated list of words, one of which is the given word
861 *
862 * This will return true (via the "match" pointer) if the libdom node
863 * has an attribute with the given name and whose value when
864 * considered as a space-separated list of words contains the given
865 * word. The comparison is case-sensitive. This corresponds to
866 * [attr~=value] in CSS.
867 *
868 * \param pw Pointer to the current SVG parser state
869 * \param node Libdom SVG node to test
870 * \param qname Attribute name to check for
871 * \param word Value word to check for
872 * \param match Pointer to the test result
873 *
874 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
875 * intern the attribute name (which usually indicates memory
876 * exhaustion)
877 */
878 css_error node_has_attribute_includes(void *pw, void *node,
879 const css_qname *qname, lwc_string *word,
880 bool *match)
881 {
882 UNUSED(pw);
883
884 dom_string *name;
885 dom_string *attr_val;
886 dom_exception err;
887 size_t wordlen; /* length of "word" */
888
889 /* pointers used to parse a space-separated list of words */
890 const char *p;
891 const char *start;
892 const char *end;
893
894 *match = false; /* default to no match */
895
896 wordlen = lwc_string_length(word);
897 if (wordlen == 0) {
898 /* In this case, the spec says that "if 'val' is the
899 * empty string, it will never represent anything." */
900 return CSS_OK;
901 }
902
903 /* Intern the attribute name as a dom_string so we can
904 * use dom_element_get_attribute() */
905 err = dom_string_create_interned(
906 (const uint8_t *) lwc_string_data(qname->name),
907 lwc_string_length(qname->name),
908 &name);
909 if (err != DOM_NO_ERR) {
910 return CSS_NOMEM;
911 }
912
913 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
914 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
915 /* There was an error getting the attribute's value or
916 * the attribute doesn't exist. So, no match? */
917 dom_string_unref(name);
918 return CSS_OK;
919 }
920
921 /* Parse the list comparing each word against "word" */
922 start = dom_string_data(attr_val);
923 end = start + dom_string_byte_length(attr_val);
924 dom_string_unref(attr_val);
925
926 for (p = start; p <= end; p++) {
927 /* Move forward until we find the end of the first word */
928 if (*p == ' ' || *p == '\0') {
929 /* If the length of that word is the length of the
930 * word we're looking for, do the comparison. */
931 if ((size_t) (p - start) == wordlen &&
932 strncasecmp(start,
933 lwc_string_data(word),
934 wordlen) == 0) {
935 *match = true;
936 break;
937 }
938 /* No match? Set "start" to the beginning of
939 * the next word and loop. */
940 start = p + 1;
941 }
942 }
943
944 return CSS_OK;
945 }
946
947
948 /**
949 * Test the given node for an attribute whose value begins with the
950 * given prefix
951 *
952 * This will return true (via the "match" pointer) if the libdom node
953 * has an attribute with the given name and whose value begins with
954 * the given prefix string. The comparison is case-sensitive. This
955 * corresponds to [attr^=value] in CSS.
956 *
957 * \param pw Pointer to the current SVG parser state
958 * \param node Libdom SVG node to test
959 * \param qname Attribute name to check for
960 * \param prefix Value prefix to check for
961 * \param match Pointer to the test result
962 *
963 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
964 * intern the attribute name (which usually indicates memory
965 * exhaustion)
966 */
967 css_error node_has_attribute_prefix(void *pw, void *node,
968 const css_qname *qname, lwc_string *prefix,
969 bool *match)
970 {
971 UNUSED(pw);
972 dom_string *name;
973 dom_string *attr_val;
974 dom_exception err;
975 const char *avdata; /* attribute value data */
976 size_t avdata_len; /* length of that attribute value data */
977 size_t prefixlen; /* length of "prefix" */
978
979 prefixlen = lwc_string_length(prefix);
980 if (prefixlen == 0) {
981 /* In this case, the spec says that "if 'val' is the
982 * empty string, it will never represent anything." */
983 return CSS_OK;
984 }
985
986 /* Intern the attribute name as a dom_string so we can
987 * use dom_element_get_attribute() */
988 err = dom_string_create_interned(
989 (const uint8_t *) lwc_string_data(qname->name),
990 lwc_string_length(qname->name),
991 &name);
992 if (err != DOM_NO_ERR) {
993 return CSS_NOMEM;
994 }
995
996 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
997 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
998 /* There was an error getting the attribute's value or
999 * the attribute doesn't exist. So, no match? */
1000 dom_string_unref(name);
1001 *match = false;
1002 return CSS_OK;
1003 }
1004
1005 /* Otherwise, we have the attribute value from the given node,
1006 * and the first thing we want to do is check to see if the
1007 * whole thing matches the prefix. */
1008 dom_string_unref(name);
1009 *match = dom_string_lwc_isequal(attr_val, prefix);
1010
1011 /* If not, check to see if an, uh, prefix matches the
1012 * prefix */
1013 if (*match == false) {
1014 avdata = dom_string_data(attr_val);
1015 avdata_len = dom_string_byte_length(attr_val);
1016 if ((avdata_len >= prefixlen) &&
1017 (strncasecmp(avdata,
1018 lwc_string_data(prefix),
1019 prefixlen) == 0)) {
1020 /* Use strncasecmp to compare only the first
1021 * "n" characters, where "n" is the length of
1022 * the prefix. */
1023 *match = true;
1024 }
1025 }
1026
1027 dom_string_unref(attr_val);
1028
1029 return CSS_OK;
1030 }
1031
1032
1033 /**
1034 * Test the given node for an attribute whose value end with the
1035 * given suffix
1036 *
1037 * This will return true (via the "match" pointer) if the libdom node
1038 * has an attribute with the given name and whose value ends with
1039 * the given suffix string. The comparison is case-sensitive. This
1040 * corresponds to [attr$=value] in CSS.
1041 *
1042 * \param pw Pointer to the current SVG parser state
1043 * \param node Libdom SVG node to test
1044 * \param qname Attribute name to check for
1045 * \param suffix Value suffix to check for
1046 * \param match Pointer to the test result
1047 *
1048 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1049 * intern the attribute name (which usually indicates memory
1050 * exhaustion)
1051 */
1052 css_error node_has_attribute_suffix(void *pw, void *node,
1053 const css_qname *qname, lwc_string *suffix,
1054 bool *match)
1055 {
1056 UNUSED(pw);
1057 dom_string *name;
1058 dom_string *attr_val;
1059 dom_exception err;
1060 const char *avdata; /* attribute value data */
1061 size_t avdata_len; /* length of that attribute value data */
1062 size_t suffixlen; /* length of "suffix" */
1063
1064 /* convenience pointer we'll use when matching the suffix */
1065 const char *suffix_start;
1066
1067 suffixlen = lwc_string_length(suffix);
1068 if (suffixlen == 0) {
1069 /* In this case, the spec says that "if 'val' is the
1070 * empty string, it will never represent anything." */
1071 return CSS_OK;
1072 }
1073
1074 /* Intern the attribute name as a dom_string so we can
1075 * use dom_element_get_attribute() */
1076 err = dom_string_create_interned(
1077 (const uint8_t *) lwc_string_data(qname->name),
1078 lwc_string_length(qname->name),
1079 &name);
1080 if (err != DOM_NO_ERR) {
1081 return CSS_NOMEM;
1082 }
1083
1084 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
1085 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
1086 /* There was an error getting the attribute's value or
1087 * the attribute doesn't exist. So, no match? */
1088 dom_string_unref(name);
1089 *match = false;
1090 return CSS_OK;
1091 }
1092
1093 /* Otherwise, we have the attribute value from the given node,
1094 * and the first thing we want to do is check to see if the
1095 * whole thing matches the suffix. */
1096 dom_string_unref(name);
1097 *match = dom_string_lwc_isequal(attr_val, suffix);
1098
1099 /* If not, check to see if an, uh, suffix matches the
1100 * suffix */
1101 if (*match == false) {
1102 avdata = dom_string_data(attr_val);
1103 avdata_len = dom_string_byte_length(attr_val);
1104
1105 suffix_start = (char *)(avdata + avdata_len - suffixlen);
1106
1107 if ((avdata_len >= suffixlen) &&
1108 (strncasecmp(suffix_start,
1109 lwc_string_data(suffix),
1110 suffixlen) == 0)) {
1111 /* Use strncasecmp to compare only the last
1112 * "n" characters, where "n" is the length of
1113 * the suffix. */
1114 *match = true;
1115 }
1116 }
1117
1118 dom_string_unref(attr_val);
1119
1120 return CSS_OK;
1121 }
1122
1123
1124 /**
1125 * Implement node_has_attribute_substring() with optional case-
1126 * insensitivity. This corresponds to [attr*=value i] in CSS and is
1127 * not supported by libcss yet, but it allows us to factor out some
1128 * common code.
1129 */
1130 static css_error _node_has_attribute_substring(void *pw, void *node,
1131 const css_qname *qname, lwc_string *substring,
1132 bool *match, bool insensitive)
1133 {
1134 UNUSED(pw);
1135 dom_string *name;
1136 dom_string *attr_val;
1137 dom_exception err;
1138 size_t attr_len; /* length of attr_val */
1139 size_t substrlen; /* length of "substring" */
1140
1141 /* Convenience pointers we use when comparing substrings */
1142 const char *p;
1143 const char *p_max;
1144
1145 substrlen = lwc_string_length(substring);
1146 if (substrlen == 0) {
1147 /* In this case, the spec says that "if 'val' is the
1148 * empty string, it will never represent anything." */
1149 return CSS_OK;
1150 }
1151
1152 /* Intern the attribute name as a dom_string so we can
1153 * use dom_element_get_attribute() */
1154 err = dom_string_create_interned(
1155 (const uint8_t *) lwc_string_data(qname->name),
1156 lwc_string_length(qname->name),
1157 &name);
1158 if (err != DOM_NO_ERR) {
1159 return CSS_NOMEM;
1160 }
1161
1162 err = dom_element_get_attribute((dom_node *)node, name, &attr_val);
1163 if ((err != DOM_NO_ERR) || (attr_val == NULL)) {
1164 /* There was an error getting the attribute's value or
1165 * the attribute doesn't exist. So, no match? */
1166 dom_string_unref(name);
1167 *match = false;
1168 return CSS_OK;
1169 }
1170
1171 /* Otherwise, we have the attribute value from the given node,
1172 * and the first thing we want to do is check to see if the
1173 * whole thing matches the substring. */
1174 dom_string_unref(name);
1175
1176 if (insensitive) {
1177 *match = dom_string_caseless_lwc_isequal(attr_val, substring);
1178 }
1179 else {
1180 *match = dom_string_lwc_isequal(attr_val, substring);
1181 }
1182
1183 /* If not, check to see if an, uh, substring matches the
1184 * substring */
1185 if (*match == false) {
1186 p = dom_string_data(attr_val);
1187
1188 /* Check every long-enough suffix for a prefix match */
1189 attr_len = dom_string_byte_length(attr_val);
1190 if (attr_len >= substrlen) {
1191 p_max = p + attr_len - substrlen;
1192 while (p <= p_max) {
1193 if (strncasecmp(p,
1194 lwc_string_data(substring),
1195 substrlen) == 0) {
1196 *match = true;
1197 break;
1198 }
1199 p++;
1200 }
1201 }
1202 }
1203
1204 dom_string_unref(attr_val);
1205
1206 return CSS_OK;
1207 }
1208
1209 /**
1210 * Test the given node for an attribute whose value contains the
1211 * given substring
1212 *
1213 * This will return true (via the "match" pointer) if the libdom node
1214 * has an attribute with the given name and whose value contains the
1215 * given substring. The comparison is case-sensitive. This corresponds
1216 * to [attr*=value] in CSS.
1217 *
1218 * \param pw Pointer to the current SVG parser state
1219 * \param node Libdom SVG node to test
1220 * \param qname Attribute name to check for
1221 * \param substring Value substring to check for
1222 * \param match Pointer to the test result
1223 *
1224 * \return Returns CSS_OK if successful and CSS_NOMEM if we cannot
1225 * intern the attribute name (which usually indicates memory
1226 * exhaustion)
1227 */
1228 css_error node_has_attribute_substring(void *pw, void *node,
1229 const css_qname *qname, lwc_string *substring,
1230 bool *match)
1231 {
1232 return _node_has_attribute_substring(pw, node, qname, substring,
1233 match, false);
1234 }
1235
1236
1237 /**
1238 * Test whether or not the given node is the document's root element
1239 * This corresponds to the CSS :root pseudo-selector.
1240 *
1241 * \param pw Pointer to the current SVG parser state
1242 * \param node Libdom SVG node to test
1243 * \param match Pointer to the test result
1244 *
1245 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1246 */
1247 css_error node_is_root(void *pw, void *node, bool *match)
1248 {
1249 UNUSED(pw);
1250 dom_node *parent;
1251 dom_node_type type;
1252 dom_exception err;
1253
1254 err = dom_node_get_parent_node((dom_node *)node, &parent);
1255 if (err != DOM_NO_ERR) {
1256 return CSS_NOMEM;
1257 }
1258
1259 /* It's the root element if it doesn't have a parent element */
1260 if (parent != NULL) {
1261 err = dom_node_get_node_type(parent, &type);
1262 dom_node_unref(parent);
1263 if (err != DOM_NO_ERR) {
1264 return CSS_NOMEM;
1265 }
1266 if (type != DOM_DOCUMENT_NODE) {
1267 /* DOM_DOCUMENT_NODE is the only allowable
1268 * type of parent node for the root element */
1269 *match = false;
1270 return CSS_OK;
1271 }
1272 }
1273
1274 *match = true;
1275 return CSS_OK;
1276 }
1277
1278
1279 /**
1280 * Used internally in node_count_siblings() to "count" the given
1281 * sibling node. It factors out the node type and name checks.
1282 */
1283 static int node_count_siblings_check(dom_node *dnode,
1284 bool check_name,
1285 dom_string *name)
1286 {
1287 int ret;
1288 dom_node_type type;
1289 dom_exception exc;
1290 dom_string *dnode_name;
1291
1292 /* We flip this to 1 if/when we count this node */
1293 ret = 0;
1294
1295 if (dnode == NULL) {
1296 return ret;
1297 }
1298
1299 exc = dom_node_get_node_type(dnode, &type);
1300 if ((exc != DOM_NO_ERR) || (type != DOM_ELEMENT_NODE)) {
1301 /* We only count element siblings */
1302 return ret;
1303 }
1304
1305 /* ... with the right name */
1306 if (check_name) {
1307 dnode_name = NULL;
1308 exc = dom_node_get_node_name(dnode, &dnode_name);
1309
1310 if ((exc == DOM_NO_ERR) && (dnode_name != NULL)) {
1311 if (dom_string_isequal(name,
1312 dnode_name)) {
1313 ret = 1;
1314 }
1315 dom_string_unref(dnode_name);
1316 }
1317 }
1318 else {
1319 ret = 1;
1320 }
1321
1322 return ret;
1323 }
1324
1325 /**
1326 * Count the given node's sibling elements
1327 *
1328 * This counts the given node's sibling elements in one direction,
1329 * either forwards or backwards, in the DOM. Keep in mind that the
1330 * libdom tree is upside-down compared to the CSS one; so "next" and
1331 * "previous" are actually reversed; the default is to count preceding
1332 * libdom siblings which correspond to subsequent CSS siblings.
1333 *
1334 * This operation is central to the CSS :first-child, :nth-child, and
1335 * :last-child (et cetera) pseudo-selectors.
1336 *
1337 * If same_name is true, then only nodes having the same
1338 * (case-sensitive) name as the given node are counted.
1339 *
1340 * \param pw Pointer to the current SVG parser state
1341 * \param node Libdom SVG node whose siblings we're counting
1342 * \param same_name Whether or not to count only siblings having
1343 * the same name as the given node
1344 * \param after Count subsequent siblings rather than precedent
1345 * ones (the default)
1346 * \param count Pointer to the return value, the number of sibling
1347 * elements
1348 *
1349 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1350 */
1351 css_error node_count_siblings(void *pw, void *node,
1352 bool same_name, bool after, int32_t *count)
1353 {
1354 UNUSED(pw);
1355 dom_exception exc;
1356 dom_node *dnode; /* node, but with the right type */
1357 dom_string *dnode_name;
1358 dom_node *next; /* "next" sibling (depends on direction) */
1359
1360 /* Pointer to the "next sibling" function */
1361 dom_exception (*next_func)(dom_node *, dom_node **);
1362
1363 *count = 0;
1364
1365 dnode_name = NULL;
1366 dnode = (dom_node *)node;
1367 if (same_name) {
1368 exc = dom_node_get_node_name(dnode, &dnode_name);
1369 if ((exc != DOM_NO_ERR) || (dnode_name == NULL)) {
1370 return CSS_NOMEM;
1371 }
1372 }
1373
1374 /* Increment the reference counter for dnode for as long as
1375 * we retain a reference to it. */
1376 dnode = dom_node_ref(dnode);
1377
1378 next_func = dom_node_get_previous_sibling;
1379 if (after) {
1380 next_func = dom_node_get_next_sibling;
1381 }
1382
1383 do {
1384 exc = next_func(dnode, &next);
1385 if (exc != DOM_NO_ERR) {
1386 break;
1387 }
1388
1389 /* If next_func worked, we're about to swap "next"
1390 * with "dnode" meaning that we will no longer retain
1391 * a reference to the current dnode. */
1392 dom_node_unref(dnode);
1393 dnode = next;
1394
1395 *count += node_count_siblings_check(dnode,
1396 same_name,
1397 dnode_name);
1398 } while (dnode != NULL);
1399
1400 if (dnode_name != NULL) {
1401 dom_string_unref(dnode_name);
1402 }
1403
1404 return CSS_OK;
1405 }
1406
1407
1408 /**
1409 * Determine whether or not the given element is empty
1410 *
1411 * An element is "nonempty" if it has a child that is either an
1412 * element node or a text node.
1413 *
1414 * \param pw Pointer to the current SVG parser state
1415 * \param node Libdom SVG node to check for emptiness
1416 * \param is_empty Pointer to the return value
1417 *
1418 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1419 */
1420 css_error node_is_empty(void *pw, void *node, bool *is_empty)
1421 {
1422 UNUSED(pw);
1423 dom_node *child; /* current child node pointer */
1424 dom_node *next; /* next child node pointer */
1425 dom_node_type type; /* what type of node is "child" */
1426 dom_exception err;
1427
1428 /* Assume that it's empty by default */
1429 *is_empty = true;
1430
1431 /* Get the given node's first child. Implementation detail:
1432 * this increments the reference counter on the child node. */
1433 err = dom_node_get_first_child((dom_node *)node, &child);
1434 if (err != DOM_NO_ERR) {
1435 return CSS_NOMEM;
1436 }
1437
1438 /* And now loop through all children looking for a
1439 * text/element node. If we find one, the original
1440 * node is "nonempty" */
1441 while (child != NULL) {
1442 err = dom_node_get_node_type(child, &type);
1443 if (err != DOM_NO_ERR) {
1444 dom_node_unref(child);
1445 return CSS_NOMEM;
1446 }
1447
1448 if (type == DOM_ELEMENT_NODE || type == DOM_TEXT_NODE) {
1449 *is_empty = false;
1450 dom_node_unref(child);
1451 return CSS_OK;
1452 }
1453
1454 err = dom_node_get_next_sibling(child, &next);
1455 if (err != DOM_NO_ERR) {
1456 dom_node_unref(child);
1457 return CSS_NOMEM;
1458 }
1459
1460 /* If we're moving to the next node, we can release
1461 * the reference to the current one */
1462 dom_node_unref(child);
1463 child = next;
1464 }
1465
1466 return CSS_OK;
1467 }
1468
1469
1470 /**
1471 * Determine whether or not the given node is a link
1472 *
1473 * A node is a link if it is an element node whose name is "a" and if
1474 * it has an "href" attribute (case-sensitive). This selector
1475 * corresponds to node:link pseudo-class in CSS.
1476 *
1477 * This pseudo-class is a bit awkward because the two standards (HTML5
1478 * and CSS) disagree on what it means, and because libsvgtiny does not
1479 * have enough information to determine if a link has been "visited"
1480 * yet -- that's a UI property. CSS says that :link is for unvisited
1481 * links, which we can't determine. HTML5 says that each link must
1482 * be either a :link or :visited. Since we can't decide either way,
1483 * It seems less wrong to declare that all links are unvisited; i.e.
1484 * that they match :link.
1485 *
1486 * \param pw Pointer to the current SVG parser state
1487 * \param node Libdom SVG node to check
1488 * \param is_link Pointer to the boolean return value
1489 *
1490 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1491 */
1492 css_error node_is_link(void *pw, void *node, bool *is_link)
1493 {
1494 dom_exception exc;
1495 dom_node *dnode; /* node, but with the right type */
1496 dom_string *dnode_name;
1497 bool has_href;
1498 struct svgtiny_parse_state* state;
1499
1500 dnode = (dom_node *)node;
1501 dnode_name = NULL;
1502 has_href = false; /* assume no href attribute */
1503 *is_link = false; /* assume that it's not a link */
1504
1505 exc = dom_node_get_node_name(dnode, &dnode_name);
1506 if ((exc != DOM_NO_ERR) || (dnode_name == NULL)) {
1507 return CSS_NOMEM;
1508 }
1509
1510 state = (struct svgtiny_parse_state *)pw;
1511 if (dom_string_isequal(dnode_name, state->interned_a)) {
1512 exc = dom_element_has_attribute(node,
1513 state->interned_href,
1514 &has_href);
1515 if (exc == DOM_NO_ERR && has_href) {
1516 *is_link = true;
1517 }
1518 }
1519
1520 dom_string_unref(dnode_name);
1521 return CSS_OK;
1522 }
1523
1524 /**
1525 * Check if the given node is a link that has been visited already
1526 *
1527 * This check always fails because the SVG DOM does not have the
1528 * necessary information (it's a UI property).
1529 *
1530 * \param pw Pointer to the current SVG parser state; unused
1531 * \param node Libdom SVG node to check; unused
1532 * \param is_visited Pointer to the boolean return value
1533 *
1534 * \return Always returns CSS_OK
1535 */
1536 css_error node_is_visited(void *pw, void *node, bool *is_visited)
1537 {
1538 UNUSED(pw);
1539 UNUSED(node);
1540 *is_visited = false;
1541 return CSS_OK;
1542 }
1543
1544
1545 /**
1546 * Check if the given node is being "hovered" over
1547 *
1548 * This check always fails because the SVG DOM does not have the
1549 * necessary information (it's a UI property).
1550 *
1551 * \param pw Pointer to the current SVG parser state; unused
1552 * \param node Libdom SVG node to check; unused
1553 * \param is_hover Pointer to the boolean return value
1554 *
1555 * \return Always returns CSS_OK
1556 */
1557 css_error node_is_hover(void *pw, void *node, bool *is_hover)
1558 {
1559 UNUSED(pw);
1560 UNUSED(node);
1561 *is_hover = false;
1562 return CSS_OK;
1563 }
1564
1565
1566 /**
1567 * Check if the given node is "active"
1568 *
1569 * This check always fails because the SVG DOM does not have the
1570 * necessary information (it's a UI property).
1571 *
1572 * \param pw Pointer to the current SVG parser state; unused
1573 * \param node Libdom SVG node to check; unused
1574 * \param is_active Pointer to the boolean return value
1575 *
1576 * \return Always returns CSS_OK
1577 */
1578 css_error node_is_active(void *pw, void *node, bool *is_active)
1579 {
1580 UNUSED(pw);
1581 UNUSED(node);
1582 *is_active = false;
1583 return CSS_OK;
1584 }
1585
1586
1587 /**
1588 * Check if the given node has the focus
1589 *
1590 * This check always fails because the SVG DOM does not have the
1591 * necessary information (it's a UI property).
1592 *
1593 * \param pw Pointer to the current SVG parser state; unused
1594 * \param node Libdom SVG node to check; unused
1595 * \param is_focus Pointer to the boolean return value
1596 *
1597 * \return Always returns CSS_OK
1598 */
1599 css_error node_is_focus(void *pw, void *node, bool *is_focus)
1600 {
1601 UNUSED(pw);
1602 UNUSED(node);
1603 *is_focus = false;
1604 return CSS_OK;
1605 }
1606
1607
1608 /**
1609 * Check if the given node is enabled
1610 *
1611 * This check always fails because the SVG DOM does not have the
1612 * necessary information (it's a UI property).
1613 *
1614 * \param pw Pointer to the current SVG parser state; unused
1615 * \param node Libdom SVG node to check; unused
1616 * \param is_enabled Pointer to the boolean return value
1617 *
1618 * \return Always returns CSS_OK
1619 */
1620 css_error node_is_enabled(void *pw, void *node, bool *is_enabled)
1621 {
1622 UNUSED(pw);
1623 UNUSED(node);
1624 *is_enabled = false;
1625 return CSS_OK;
1626 }
1627
1628
1629 /**
1630 * Check if the given node is disabled
1631 *
1632 * This check always fails because the SVG DOM does not have the
1633 * necessary information (it's a UI property). Beware, until they are
1634 * implemented, this is NOT the logical negation of node_is_enabled!
1635 *
1636 * \param pw Pointer to the current SVG parser state; unused
1637 * \param node Libdom SVG node to check; unused
1638 * \param is_disabled Pointer to the boolean return value
1639 *
1640 * \return Always returns CSS_OK
1641 */
1642 css_error node_is_disabled(void *pw, void *node, bool *is_disabled)
1643 {
1644 UNUSED(pw);
1645 UNUSED(node);
1646 *is_disabled = false;
1647 return CSS_OK;
1648 }
1649
1650
1651 /**
1652 * Test whether or not the given node is "checked"
1653 *
1654 * This test always fails because the SVG DOM does not have the
1655 * necessary information (it's a UI property).
1656 *
1657 * \param pw Pointer to the current SVG parser state; unused
1658 * \param node Libdom SVG node to check; unused
1659 * \param is_checked Pointer to the boolean return value
1660 *
1661 * \return Always returns CSS_OK
1662 */
1663 css_error node_is_checked(void *pw, void *node, bool *is_checked)
1664 {
1665 UNUSED(pw);
1666 UNUSED(node);
1667 *is_checked = false;
1668 return CSS_OK;
1669 }
1670
1671
1672 /**
1673 * Check if the given node is the "target" of the document URL
1674 *
1675 * This test always fails because the SVG DOM does not have the
1676 * necessary information (it's a UI property).
1677 *
1678 * \param pw Pointer to the current SVG parser state; unused
1679 * \param node Libdom SVG node to check; unused
1680 * \param is_target Pointer to the boolean return value
1681 *
1682 * \return Always returns CSS_OK
1683 */
1684 css_error node_is_target(void *pw, void *node, bool *is_target)
1685 {
1686 UNUSED(pw);
1687 UNUSED(node);
1688 *is_target = false;
1689 return CSS_OK;
1690 }
1691
1692
1693 /**
1694 * Check if the given node is the given language
1695 *
1696 * This test is corresponds to the CSS :lang() selector and is not
1697 * fully implemented yet: it looks only for "lang" attributes on the
1698 * given element and its parents, and performs a simple substring
1699 * check. This results in a partial implementation of CSS Level 3 for
1700 * SVG 2.0. In particular, it ignores all "xml:lang" attributes in
1701 * favor of the "lang" attribute that is defined only in SVG 2.0.
1702 *
1703 * \param pw Pointer to the current SVG parser state; unused
1704 * \param node Libdom SVG node to check
1705 * \param lang The language to match
1706 * \param is_lang Pointer to the boolean return value
1707 *
1708 * \return CSS_OK on success, or CSS_NOMEM if anything goes wrong
1709 */
1710 static css_error node_is_lang(void *pw, void *node,
1711 lwc_string *lang, bool *is_lang)
1712 {
1713 UNUSED(pw);
1714 /* SVG2 elements support both "lang" and "xml:lang"
1715 * attributes; earlier versions have only the XML
1716 * attribute. It would not be too hard to add support for
1717 * xml:lang" here. The main difficulty standing in the way of
1718 * a full Level 4 implementation is the complexity of the
1719 * :lang() selector:
1720 *
1721 * https://www.w3.org/TR/selectors-4/#the-lang-pseudo
1722 *
1723 */
1724
1725 css_error c_err;
1726 dom_exception d_err;
1727 dom_node *n; /* current node */
1728 dom_node *p; /* parent node */
1729 bool match; /* retval from node_has_attribute_substring() */
1730
1731 /* Define the attribute name "lang" that we're looking for.
1732 * We only use a css_qname here because that's what the
1733 * node_has_attribute_substring() takes; the namespace
1734 * portion of it is irrelevant. */
1735 css_qname attr;
1736 attr.ns = NULL;
1737
1738 if (lwc_intern_string("lang", 4, &attr.name) != lwc_error_ok) {
1739 return CSS_NOMEM;
1740 }
1741
1742 *is_lang = false; /* default to no match */
1743 n = (dom_node *)node;
1744
1745 /* Loop through all parents of the given node looking for a
1746 * substring match */
1747 while (n != NULL) {
1748 c_err = _node_has_attribute_substring(pw, (void *)n, &attr,
1749 lang, &match, true);
1750 if (c_err != CSS_OK) {
1751 lwc_string_destroy(attr.name);
1752 return c_err;
1753 }
1754 if (match) {
1755 /* matched this element; we're done */
1756 lwc_string_destroy(attr.name);
1757 *is_lang = true;
1758 return CSS_OK;
1759 }
1760
1761 /* no match on this element, try its parent */
1762 d_err = dom_node_get_parent_node(n, &p);
1763 if (d_err != DOM_NO_ERR) {
1764 lwc_string_destroy(attr.name);
1765 return CSS_NOMEM;
1766 }
1767 n = p;
1768 }
1769
1770 /* If we never find a match we may wind up here */
1771 lwc_string_destroy(attr.name);
1772 return CSS_OK;
1773 }
1774
1775
1776 /**
1777 * Return presentational hints for the given node
1778 *
1779 * Unless an SVG is being rendered from within an HTML document,
1780 * there are no presentational hints. We always return an empty
1781 * array (no hints).
1782 *
1783 * \param pw Pointer to the current SVG parser state; unused
1784 * \param node Libdom SVG node whose hints we want; unused
1785 * \param nhints How many hints are returned (return by reference)
1786 * \param hints Array of css_hint structures (return by reference)
1787 *
1788 * \return Always returns CSS_OK
1789 */
1790 css_error node_presentational_hint(void *pw, void *node,
1791 uint32_t *nhints, css_hint **hints)
1792 {
1793 UNUSED(pw);
1794 UNUSED(node);
1795 *nhints = 0;
1796 *hints = NULL;
1797 return CSS_OK;
1798 }
1799
1800
1801 /**
1802 * User-agent defaults for CSS properties
1803 *
1804 * For the moment, we provide no defaults, because libsvgtiny does not
1805 * yet support any CSS properties that might need them.
1806 *
1807 * \param pw Pointer to the current SVG parser state; unused
1808 * \param property LibCSS property identifier; unused
1809 * \param hint Pointer to hint object (a return value); unused
1810 *
1811 * \return Always returns CSS_INVALID
1812 */
1813 css_error ua_default_for_property(void *pw, uint32_t property,
1814 css_hint *hint)
1815 {
1816 UNUSED(pw);
1817 UNUSED(property);
1818 UNUSED(hint);
1819 return CSS_INVALID;
1820 }
1821
1822
1823 /**
1824 * Store libcss data on a node
1825 *
1826 * This is part of the libcss select handler API that we need to
1827 * implement. It is essentially a thin dom_node_set_user_data()
1828 * wrapper.
1829 *
1830 * \param pw Pointer to the current SVG parser state
1831 * \param node Libdom SVG node on which to store the data
1832 * \param libcss_node_data Pointer to the data to store
1833 *
1834 * \return Always returns CSS_OK
1835 */
1836 css_error set_libcss_node_data(void *pw, void *node,
1837 void *libcss_node_data)
1838 {
1839 struct svgtiny_parse_state *state;
1840 void *old_data;
1841
1842 /* A unique "userdata key" (a string) is used to identify this
1843 * data. The fourth parameter (NULL) is a "user handler
1844 * function." We will eventually have one, but for now we set
1845 * it to NULL to avoid a circular reference mess that would
1846 * break the build temporarily. */
1847 state = (struct svgtiny_parse_state *)pw;
1848 dom_node_set_user_data((dom_node *)node,
1849 state->interned_userdata_key,
1850 libcss_node_data,
1851 NULL,
1852 &old_data);
1853
1854 /* dom_node_set_user_data() always returns DOM_NO_ERR */
1855 return CSS_OK;
1856 }
1857
1858
1859 /**
1860 * Retrieve libcss data from a node
1861 *
1862 * This is part of the libcss select handler API that we need to
1863 * implement. It is essentially a thin dom_node_get_user_data()
1864 * wrapper.
1865 *
1866 * \param pw Pointer to the current SVG parser state
1867 * \param node Libdom SVG node from which to get the data
1868 * \param libcss_node_data Address at which to store a pointer to the data
1869 *
1870 * \return Always returns CSS_OK
1871 */
1872 css_error get_libcss_node_data(void *pw, void *node,
1873 void **libcss_node_data)
1874 {
1875 struct svgtiny_parse_state *state;
1876
1877 /* A unique "userdata key" (a string) is used to identify this
1878 * data. */
1879 state = (struct svgtiny_parse_state *)pw;
1880 dom_node_get_user_data((dom_node *)node,
1881 state->interned_userdata_key,
1882 libcss_node_data);
1883
1884 /* dom_node_get_user_data() always returns DOM_NO_ERR */
1885 return CSS_OK;
1886 }