libcss
Loading...
Searching...
No Matches
select.c
Go to the documentation of this file.
1#include <ctype.h>
2#include <inttypes.h>
3#include <stdbool.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7
8#include <libcss/libcss.h>
9#include <libcss/computed.h>
10#include <libcss/select.h>
11#include <libcss/stylesheet.h>
12
13#include "utils/utils.h"
14
15#include "dump_computed.h"
16#include "testutils.h"
17
18typedef struct attribute {
19 lwc_string *name;
20 lwc_string *value;
22
23typedef struct node {
24 lwc_string *name;
25
26 uint32_t n_classes;
27 lwc_string **classes;
28
29 uint32_t n_attrs;
31
34
35 struct node *parent;
36 struct node *next;
37 struct node *prev;
38 struct node *children;
41
47
48typedef struct line_ctx {
49 size_t explen;
50 size_t expused;
51 char *exp;
52
53 bool intree;
54 bool insheet;
55 bool inerrors;
56 bool inexp;
57
60 uint32_t depth;
61
62 uint32_t n_sheets;
64
68
69 lwc_string *attr_class;
70 lwc_string *attr_id;
72
73static css_error node_name(void *pw, void *n, css_qname *qname)
74{
75 node *node = n;
76
77 UNUSED(pw);
78
79 qname->name = lwc_string_ref(node->name);
80
81 return CSS_OK;
82}
83
84static css_error node_classes(void *pw, void *n,
85 lwc_string ***classes, uint32_t *n_classes)
86{
87 unsigned int i;
88 node *node = n;
89 UNUSED(pw);
90
91 *classes = node->classes;
92 *n_classes = node->n_classes;
93
94 for (i = 0; i < *n_classes; i++)
95 (*classes)[i] = lwc_string_ref(node->classes[i]);
96
97 return CSS_OK;
98
99}
100
101static css_error node_id(void *pw, void *n,
102 lwc_string **id)
103{
104 node *node = n;
105 uint32_t i;
106 line_ctx *lc = pw;
107
108 for (i = 0; i < node->n_attrs; i++) {
109 bool amatch = false;
110 assert(lwc_string_caseless_isequal(
111 node->attrs[i].name, lc->attr_id, &amatch) ==
112 lwc_error_ok);
113 if (amatch == true)
114 break;
115 }
116
117 if (i != node->n_attrs)
118 *id = lwc_string_ref(node->attrs[i].value);
119 else
120 *id = NULL;
121
122 return CSS_OK;
123}
124
125static css_error named_ancestor_node(void *pw, void *n,
126 const css_qname *qname,
127 void **ancestor)
128{
129 node *node = n;
130 UNUSED(pw);
131
132 for (node = node->parent; node != NULL; node = node->parent) {
133 bool match = false;
134 assert(lwc_string_caseless_isequal(
135 qname->name, node->name,
136 &match) == lwc_error_ok);
137 if (match == true)
138 break;
139 }
140
141 *ancestor = (void *) node;
142
143 return CSS_OK;
144}
145
146static css_error named_parent_node(void *pw, void *n,
147 const css_qname *qname,
148 void **parent)
149{
150 node *node = n;
151 UNUSED(pw);
152
153 *parent = NULL;
154 if (node->parent != NULL) {
155 bool match = false;
156 assert(lwc_string_caseless_isequal(
157 qname->name, node->parent->name, &match) ==
158 lwc_error_ok);
159 if (match == true)
160 *parent = (void *) node->parent;
161 }
162
163 return CSS_OK;
164}
165
166static css_error named_sibling_node(void *pw, void *n,
167 const css_qname *qname,
168 void **sibling)
169{
170 node *node = n;
171 UNUSED(pw);
172
173 *sibling = NULL;
174 if (node->prev != NULL) {
175 bool match = false;
176 assert(lwc_string_caseless_isequal(
177 qname->name, node->prev->name, &match) ==
178 lwc_error_ok);
179 if (match == true)
180 *sibling = (void *) node->prev;
181 }
182
183 return CSS_OK;
184}
185
186static css_error named_generic_sibling_node(void *pw, void *n,
187 const css_qname *qname,
188 void **sibling)
189{
190 node *node = n;
191 UNUSED(pw);
192
193 for (node = node->prev; node != NULL; node = node->prev) {
194 bool match = false;
195 assert(lwc_string_caseless_isequal(
196 qname->name, node->name,
197 &match) == lwc_error_ok);
198 if (match == true)
199 break;
200 }
201
202 *sibling = (void *) node;
203
204 return CSS_OK;
205}
206
207static css_error parent_node(void *pw, void *n, void **parent)
208{
209 node *node = n;
210
211 UNUSED(pw);
212
213 *parent = (void *) node->parent;
214
215 return CSS_OK;
216}
217
218static css_error sibling_node(void *pw, void *n, void **sibling)
219{
220 node *node = n;
221
222 UNUSED(pw);
223
224 *sibling = (void *) node->prev;
225
226 return CSS_OK;
227}
228
229static css_error node_has_name(void *pw, void *n,
230 const css_qname *qname,
231 bool *match)
232{
233 node *node = n;
234 UNUSED(pw);
235
236 if (lwc_string_length(qname->name) == 1 &&
237 lwc_string_data(qname->name)[0] == '*')
238 *match = true;
239 else
240 assert(lwc_string_caseless_isequal(node->name,
241 qname->name, match) == lwc_error_ok);
242
243 return CSS_OK;
244}
245
246static css_error node_has_class(void *pw, void *n,
247 lwc_string *name,
248 bool *match)
249{
250 node *node = n;
251 uint32_t i;
252 line_ctx *ctx = pw;
253
254 for (i = 0; i < node->n_attrs; i++) {
255 bool amatch = false;
256 assert(lwc_string_caseless_isequal(
257 node->attrs[i].name, ctx->attr_class,
258 &amatch) == lwc_error_ok);
259 if (amatch == true)
260 break;
261 }
262
263 /* Classes are case-sensitive in HTML */
264 if (i != node->n_attrs && name == node->attrs[i].value)
265 *match = true;
266 else
267 *match = false;
268
269 return CSS_OK;
270}
271
272static css_error node_has_id(void *pw, void *n,
273 lwc_string *name,
274 bool *match)
275{
276 node *node = n;
277 uint32_t i;
278 line_ctx *ctx = pw;
279
280 for (i = 0; i < node->n_attrs; i++) {
281 bool amatch = false;
282 assert(lwc_string_caseless_isequal(
283 node->attrs[i].name, ctx->attr_id, &amatch) ==
284 lwc_error_ok);
285 if (amatch == true)
286 break;
287 }
288
289 /* IDs are case-sensitive in HTML */
290 if (i != node->n_attrs && name == node->attrs[i].value)
291 *match = true;
292 else
293 *match = false;
294
295 return CSS_OK;
296}
297
298static css_error node_has_attribute(void *pw, void *n,
299 const css_qname *qname,
300 bool *match)
301{
302 node *node = n;
303 uint32_t i;
304 UNUSED(pw);
305
306 *match = false;
307 for (i = 0; i < node->n_attrs; i++) {
308 assert(lwc_string_caseless_isequal(
309 node->attrs[i].name, qname->name, match) ==
310 lwc_error_ok);
311 if (*match == true)
312 break;
313 }
314
315 return CSS_OK;
316}
317
318static css_error node_has_attribute_equal(void *pw, void *n,
319 const css_qname *qname,
320 lwc_string *value,
321 bool *match)
322{
323 node *node = n;
324 uint32_t i;
325 UNUSED(pw);
326
327 *match = false;
328
329 for (i = 0; i < node->n_attrs; i++) {
330 assert(lwc_string_caseless_isequal(
331 node->attrs[i].name, qname->name, match) ==
332 lwc_error_ok);
333 if (*match == true)
334 break;
335 }
336
337 if (*match == true) {
338 assert(lwc_string_caseless_isequal(
339 node->attrs[i].name, value, match) ==
340 lwc_error_ok);
341 }
342
343 return CSS_OK;
344}
345
346static css_error node_has_attribute_includes(void *pw, void *n,
347 const css_qname *qname,
348 lwc_string *value,
349 bool *match)
350{
351 node *node = n;
352 uint32_t i;
353 size_t vlen = lwc_string_length(value);
354 UNUSED(pw);
355
356 *match = false;
357
358 for (i = 0; i < node->n_attrs; i++) {
359 assert(lwc_string_caseless_isequal(
360 node->attrs[i].name, qname->name, match) ==
361 lwc_error_ok);
362 if (*match == true)
363 break;
364 }
365
366 if (*match == true) {
367 const char *p;
368 const char *start = lwc_string_data(node->attrs[i].value);
369 const char *end = start +
370 lwc_string_length(node->attrs[i].value);
371
372 *match = false;
373
374 for (p = start; p < end; p++) {
375 if (*p == ' ') {
376 if ((size_t) (p - start) == vlen &&
377 strncasecmp(start,
378 lwc_string_data(value),
379 vlen) == 0) {
380 *match = true;
381 break;
382 }
383
384 start = p + 1;
385 }
386 }
387 }
388
389 return CSS_OK;
390}
391
392static css_error node_has_attribute_dashmatch(void *pw, void *n,
393 const css_qname *qname,
394 lwc_string *value,
395 bool *match)
396{
397 node *node = n;
398 uint32_t i;
399 size_t vlen = lwc_string_length(value);
400 UNUSED(pw);
401
402 *match = false;
403
404 for (i = 0; i < node->n_attrs; i++) {
405 assert(lwc_string_caseless_isequal(
406 node->attrs[i].name, qname->name, match) ==
407 lwc_error_ok);
408 if (*match == true)
409 break;
410 }
411
412 if (*match == true) {
413 const char *p;
414 const char *start = lwc_string_data(node->attrs[i].value);
415 const char *end = start +
416 lwc_string_length(node->attrs[i].value);
417
418 *match = false;
419
420 for (p = start; p < end; p++) {
421 if (*p == '-') {
422 if ((size_t) (p - start) == vlen &&
423 strncasecmp(start,
424 lwc_string_data(value),
425 vlen) == 0) {
426 *match = true;
427 break;
428 }
429
430 start = p + 1;
431 }
432 }
433 }
434
435 return CSS_OK;
436}
437
438static css_error node_has_attribute_prefix(void *pw, void *n,
439 const css_qname *qname,
440 lwc_string *value,
441 bool *match)
442{
443 node *node = n;
444 uint32_t i;
445 UNUSED(pw);
446
447 *match = false;
448
449 for (i = 0; i < node->n_attrs; i++) {
450 assert(lwc_string_caseless_isequal(
451 node->attrs[i].name, qname->name, match) ==
452 lwc_error_ok);
453 if (*match == true)
454 break;
455 }
456
457 if (*match == true) {
458 size_t len = lwc_string_length(node->attrs[i].value);
459 const char *data = lwc_string_data(node->attrs[i].value);
460
461 size_t vlen = lwc_string_length(value);
462 const char *vdata = lwc_string_data(value);
463
464 if (len < vlen)
465 *match = false;
466 else
467 *match = (strncasecmp(data, vdata, vlen) == 0);
468 }
469
470 return CSS_OK;
471}
472
473static css_error node_has_attribute_suffix(void *pw, void *n,
474 const css_qname *qname,
475 lwc_string *value,
476 bool *match)
477{
478 node *node = n;
479 uint32_t i;
480 UNUSED(pw);
481
482 *match = false;
483
484 for (i = 0; i < node->n_attrs; i++) {
485 assert(lwc_string_caseless_isequal(
486 node->attrs[i].name, qname->name, match) ==
487 lwc_error_ok);
488 if (*match == true)
489 break;
490 }
491
492 if (*match == true) {
493 size_t len = lwc_string_length(node->attrs[i].value);
494 const char *data = lwc_string_data(node->attrs[i].value);
495
496 size_t vlen = lwc_string_length(value);
497 const char *vdata = lwc_string_data(value);
498
499 size_t suffix_start = len - vlen;
500
501 if (len < vlen)
502 *match = false;
503 else {
504 *match = (strncasecmp(data + suffix_start,
505 vdata, vlen) == 0);
506 }
507 }
508
509 return CSS_OK;
510}
511
512static css_error node_has_attribute_substring(void *pw, void *n,
513 const css_qname *qname,
514 lwc_string *value,
515 bool *match)
516{
517 node *node = n;
518 uint32_t i;
519 UNUSED(pw);
520
521 *match = false;
522
523 for (i = 0; i < node->n_attrs; i++) {
524 assert(lwc_string_caseless_isequal(
525 node->attrs[i].name, qname->name, match) ==
526 lwc_error_ok);
527 if (*match == true)
528 break;
529 }
530
531 if (*match == true) {
532 size_t len = lwc_string_length(node->attrs[i].value);
533 const char *data = lwc_string_data(node->attrs[i].value);
534
535 size_t vlen = lwc_string_length(value);
536 const char *vdata = lwc_string_data(value);
537
538 const char *last_start = data + len - vlen;
539
540 if (len < vlen)
541 *match = false;
542 else {
543 while (data <= last_start) {
544 if (strncasecmp(data, vdata, vlen) == 0) {
545 *match = true;
546 break;
547 }
548
549 data++;
550 }
551
552 if (data > last_start)
553 *match = false;
554 }
555 }
556
557 return CSS_OK;
558}
559
560static css_error node_is_root(void *pw, void *n, bool *match)
561{
562 node *node = n;
563 UNUSED(pw);
564
565 *match = (node->parent == NULL);
566
567 return CSS_OK;
568}
569
570static css_error node_count_siblings(void *pw, void *n,
571 bool same_name, bool after, int32_t *count)
572{
573 int32_t cnt = 0;
574 bool match = false;
575 node *node = n;
576 lwc_string *name = node->name;
577 UNUSED(pw);
578
579 if (after) {
580 while (node->next != NULL) {
581 if (same_name) {
582 assert(lwc_string_caseless_isequal(
583 name, node->next->name, &match) ==
584 lwc_error_ok);
585
586 if (match)
587 cnt++;
588 } else {
589 cnt++;
590 }
591
592 node = node->next;
593 }
594 } else {
595 while (node->prev != NULL) {
596 if (same_name) {
597 assert(lwc_string_caseless_isequal(
598 name, node->prev->name, &match) ==
599 lwc_error_ok);
600
601 if (match)
602 cnt++;
603 } else {
604 cnt++;
605 }
606
607 node = node->prev;
608 }
609 }
610
611 *count = cnt;
612
613 return CSS_OK;
614}
615
616static css_error node_is_empty(void *pw, void *n, bool *match)
617{
618 node *node = n;
619 UNUSED(pw);
620
621 *match = (node->children == NULL);
622
623 return CSS_OK;
624}
625
626static css_error node_is_link(void *pw, void *n, bool *match)
627{
628 node *node = n;
629
630 UNUSED(pw);
631 UNUSED(node);
632
633 *match = false;
634
635 return CSS_OK;
636}
637
638static css_error node_is_visited(void *pw, void *n, bool *match)
639{
640 node *node = n;
641
642 UNUSED(pw);
643 UNUSED(node);
644
645 *match = false;
646
647 return CSS_OK;
648}
649
650static css_error node_is_hover(void *pw, void *n, bool *match)
651{
652 node *node = n;
653
654 UNUSED(pw);
655 UNUSED(node);
656
657 *match = false;
658
659 return CSS_OK;
660}
661
662static css_error node_is_active(void *pw, void *n, bool *match)
663{
664 node *node = n;
665
666 UNUSED(pw);
667 UNUSED(node);
668
669 *match = false;
670
671 return CSS_OK;
672}
673
674static css_error node_is_focus(void *pw, void *n, bool *match)
675{
676 node *node = n;
677
678 UNUSED(pw);
679 UNUSED(node);
680
681 *match = false;
682
683 return CSS_OK;
684}
685
686static css_error node_is_enabled(void *pw, void *n, bool *match)
687{
688 node *node = n;
689
690 UNUSED(pw);
691 UNUSED(node);
692
693 *match = false;
694
695 return CSS_OK;
696}
697
698static css_error node_is_disabled(void *pw, void *n, bool *match)
699{
700 node *node = n;
701
702 UNUSED(pw);
703 UNUSED(node);
704
705 *match = false;
706
707 return CSS_OK;
708}
709
710static css_error node_is_checked(void *pw, void *n, bool *match)
711{
712 node *node = n;
713
714 UNUSED(pw);
715 UNUSED(node);
716
717 *match = false;
718
719 return CSS_OK;
720}
721
722static css_error node_is_target(void *pw, void *n, bool *match)
723{
724 node *node = n;
725
726 UNUSED(pw);
727 UNUSED(node);
728
729 *match = false;
730
731 return CSS_OK;
732}
733
734static css_error node_is_lang(void *pw, void *n,
735 lwc_string *lang,
736 bool *match)
737{
738 node *node = n;
739
740 UNUSED(pw);
741 UNUSED(node);
742 UNUSED(lang);
743
744 *match = false;
745
746 return CSS_OK;
747}
748
749static css_error node_presentational_hint(void *pw, void *node,
750 uint32_t *nhints, css_hint **hints)
751{
752 UNUSED(pw);
753 UNUSED(node);
754
755 *nhints = 0;
756 *hints = NULL;
757
758 return CSS_OK;
759}
760
761static css_error ua_default_for_property(void *pw, uint32_t property, css_hint *hint)
762{
763 UNUSED(pw);
764
765 if (property == CSS_PROP_COLOR) {
766 hint->data.color = 0xff000000;
767 hint->status = CSS_COLOR_COLOR;
768 } else if (property == CSS_PROP_FONT_FAMILY) {
769 hint->data.strings = NULL;
771 } else if (property == CSS_PROP_QUOTES) {
772 /* Not exactly useful :) */
773 hint->data.strings = NULL;
774 hint->status = CSS_QUOTES_NONE;
775 } else if (property == CSS_PROP_VOICE_FAMILY) {
777 hint->data.strings = NULL;
778 hint->status = 0;
779 } else {
780 return CSS_INVALID;
781 }
782
783 return CSS_OK;
784}
785
786static css_error set_libcss_node_data(void *pw, void *n,
787 void *libcss_node_data)
788{
789 node *node = n;
790 UNUSED(pw);
791
792 node->libcss_node_data = libcss_node_data;
793
794 return CSS_OK;
795}
796
797static css_error get_libcss_node_data(void *pw, void *n,
798 void **libcss_node_data)
799{
800 node *node = n;
801 UNUSED(pw);
802
803 /* Pass any node data back to libcss */
804 *libcss_node_data = node->libcss_node_data;
805
806 return CSS_OK;
807}
808
809static css_unit_ctx unit_ctx = {
810 .font_size_default = 16 * (1 << CSS_RADIX_POINT),
811 .device_dpi = 96 * (1 << CSS_RADIX_POINT),
812};
813
814static css_select_handler select_handler = {
816
817 node_name,
818 node_classes,
819 node_id,
820 named_ancestor_node,
821 named_parent_node,
822 named_sibling_node,
823 named_generic_sibling_node,
824 parent_node,
825 sibling_node,
826 node_has_name,
827 node_has_class,
828 node_has_id,
829 node_has_attribute,
830 node_has_attribute_equal,
831 node_has_attribute_dashmatch,
832 node_has_attribute_includes,
833 node_has_attribute_prefix,
834 node_has_attribute_suffix,
835 node_has_attribute_substring,
836 node_is_root,
837 node_count_siblings,
838 node_is_empty,
839 node_is_link,
840 node_is_visited,
841 node_is_hover,
842 node_is_active,
843 node_is_focus,
844 node_is_enabled,
845 node_is_disabled,
846 node_is_checked,
847 node_is_target,
848 node_is_lang,
849 node_presentational_hint,
850 ua_default_for_property,
851
852 set_libcss_node_data,
853 get_libcss_node_data,
854};
855
856static css_error resolve_url(void *pw,
857 const char *base, lwc_string *rel, lwc_string **abs)
858{
859 UNUSED(pw);
860 UNUSED(base);
861
862 /* About as useless as possible */
863 *abs = lwc_string_ref(rel);
864
865 return CSS_OK;
866}
867
868static bool fail_because_lwc_leaked = false;
869
870static void
871printing_lwc_iterator(lwc_string *str, void *pw)
872{
873 UNUSED(pw);
874
875 printf(" DICT: %*s\n", (int)(lwc_string_length(str)), lwc_string_data(str));
876 fail_because_lwc_leaked = true;
877}
878
879static css_error css_font_resolution_func(void *pw, lwc_string *name,
880 css_system_font *system_font)
881{
882 lwc_error err;
883
884 if (system_font == NULL) {
885 return CSS_BADPARM;
886 }
887
888 (void)(pw);
889
890 if (strncmp(lwc_string_data(name), "special-system-font",
891 lwc_string_length(name)) != 0) {
892 return CSS_INVALID;
893 }
894
895 system_font->style = CSS_FONT_STYLE_NORMAL;
896 system_font->variant = CSS_FONT_VARIANT_NORMAL;
897 system_font->weight = CSS_FONT_WEIGHT_NORMAL;
898 system_font->size.size = INTTOFIX(22);
899 system_font->size.unit = CSS_UNIT_PT;
900 system_font->line_height.size = INTTOFIX(33);
901 system_font->line_height.unit = CSS_UNIT_EM;
902 err = lwc_intern_string("special-system-font",
903 strlen("special-system-font"),
904 &system_font->family);
905 if (err != lwc_error_ok) {
906 return CSS_NOMEM;
907 }
908
909 return CSS_OK;
910}
911
912static void css__parse_sheet(line_ctx *ctx, const char *data, size_t len)
913{
915 const char *p;
916 const char *end = data + len;
918 css_stylesheet *sheet;
919 sheet_ctx *temp;
920 char *media = NULL;
921
922 /* <origin> <media_list>? */
923
924 /* Find end of origin */
925 for (p = data; p < end; p++) {
926 if (isspace(*p))
927 break;
928 }
929
930 if (p - data == 6 && strncasecmp(data, "author", 6) == 0)
931 origin = CSS_ORIGIN_AUTHOR;
932 else if (p - data == 4 && strncasecmp(data, "user", 4) == 0)
933 origin = CSS_ORIGIN_USER;
934 else if (p - data == 2 && strncasecmp(data, "ua", 2) == 0)
935 origin = CSS_ORIGIN_UA;
936 else
937 assert(0 && "Unknown stylesheet origin");
938
939 /* Skip any whitespace */
940 while (p < end && isspace(*p))
941 p++;
942
943 assert(end >= p);
944 media = malloc(end - p + 1);
945 assert(media != NULL);
946 memcpy(media, p, end - p);
947 media[end - p] = '\0';
948
950 params.level = CSS_LEVEL_21;
951 params.charset = "UTF-8";
952 params.url = "foo";
953 params.title = "foo";
954 params.allow_quirks = false;
955 params.inline_style = false;
956 params.resolve = resolve_url;
957 params.resolve_pw = NULL;
958 params.import = NULL;
959 params.import_pw = NULL;
960 params.color = NULL;
961 params.color_pw = NULL;
962 params.font = css_font_resolution_func;
963 params.font_pw = NULL;
964
966 assert(css_stylesheet_create(&params, &sheet) == CSS_OK);
967
968 /* Extend array of sheets and append new sheet to it */
969 temp = realloc(ctx->sheets,
970 (ctx->n_sheets + 1) * sizeof(sheet_ctx));
971 assert(temp != NULL);
972
973 ctx->sheets = temp;
974
975 ctx->sheets[ctx->n_sheets].sheet = sheet;
976 ctx->sheets[ctx->n_sheets].origin = origin;
977 ctx->sheets[ctx->n_sheets].media = media;
978
979 ctx->n_sheets++;
980}
981
982static void css__parse_media_list(const char **data, size_t *len, css_media *media)
983{
984 const char *p = *data;
985 const char *end = p + *len;
986 uint64_t result = 0;
987
988 /* <medium> [ ',' <medium> ]* */
989
990 while (p < end) {
991 const char *start = p;
992
993 /* consume a medium */
994 while (isspace(*p) == false && *p != ',')
995 p++;
996
997 if (p - start == 10 &&
998 strncasecmp(start, "projection", 10) == 0)
999 result |= CSS_MEDIA_PROJECTION;
1000 else if (p - start == 8 &&
1001 strncasecmp(start, "handheld", 8) == 0)
1002 result |= CSS_MEDIA_HANDHELD;
1003 else if (p - start == 8 &&
1004 strncasecmp(start, "embossed", 8) == 0)
1005 result |= CSS_MEDIA_EMBOSSED;
1006 else if (p - start == 7 &&
1007 strncasecmp(start, "braille", 7) == 0)
1008 result |= CSS_MEDIA_BRAILLE;
1009 else if (p - start == 6 &&
1010 strncasecmp(start, "speech", 6) == 0)
1011 result |= CSS_MEDIA_SPEECH;
1012 else if (p - start == 6 &&
1013 strncasecmp(start, "screen", 6) == 0)
1014 result |= CSS_MEDIA_SCREEN;
1015 else if (p - start == 5 &&
1016 strncasecmp(start, "print", 5) == 0)
1017 result |= CSS_MEDIA_PRINT;
1018 else if (p - start == 5 &&
1019 strncasecmp(start, "aural", 5) == 0)
1020 result |= CSS_MEDIA_AURAL;
1021 else if (p - start == 3 &&
1022 strncasecmp(start, "tty", 3) == 0)
1023 result |= CSS_MEDIA_TTY;
1024 else if (p - start == 3 &&
1025 strncasecmp(start, "all", 3) == 0)
1026 result |= CSS_MEDIA_ALL;
1027 else if (p - start == 2 &&
1028 strncasecmp(start, "tv", 2) == 0)
1029 result |= CSS_MEDIA_TV;
1030 else
1031 assert(0 && "Unknown media type");
1032
1033 /* Consume whitespace */
1034 while (p < end && isspace(*p))
1035 p++;
1036
1037 /* Stop if we've reached the end */
1038 if (p == end || *p != ',')
1039 break;
1040
1041 /* Consume comma */
1042 p++;
1043
1044 /* Consume whitespace */
1045 while (p < end && isspace(*p))
1046 p++;
1047 }
1048
1049 media->type = result;
1050
1051 *data = p;
1052 *len = end - p;
1053}
1054
1055static void css__parse_pseudo_list(const char **data, size_t *len, uint32_t *element)
1056{
1057 const char *p = *data;
1058 const char *end = p + *len;
1059
1060 /* <pseudo> [ ',' <pseudo> ]* */
1061
1062 *element = CSS_PSEUDO_ELEMENT_NONE;
1063
1064 while (p < end) {
1065 const char *start = p;
1066
1067 /* consume a pseudo */
1068 while (isspace(*p) == false && *p != ',')
1069 p++;
1070
1071 /* Pseudo elements */
1072 if (p - start == 12 &&
1073 strncasecmp(start, "first-letter", 12) == 0)
1075 else if (p - start == 10 &&
1076 strncasecmp(start, "first-line", 10) == 0)
1078 else if (p - start == 6 &&
1079 strncasecmp(start, "before", 6) == 0)
1080 *element = CSS_PSEUDO_ELEMENT_BEFORE;
1081 else if (p - start == 5 &&
1082 strncasecmp(start, "after", 5) == 0)
1083 *element = CSS_PSEUDO_ELEMENT_AFTER;
1084 else
1085 assert(0 && "Unknown pseudo");
1086
1087 /* Consume whitespace */
1088 while (p < end && isspace(*p))
1089 p++;
1090
1091 /* Stop if we've reached the end */
1092 if (p == end || *p != ',')
1093 break;
1094
1095 /* Consume comma */
1096 p++;
1097
1098 /* Consume whitespace */
1099 while (p < end && isspace(*p))
1100 p++;
1101 }
1102
1103 *data = p;
1104 *len = end - p;
1105}
1106
1107static void css__parse_tree(line_ctx *ctx, const char *data, size_t len)
1108{
1109 const char *p = data;
1110 const char *end = data + len;
1111 size_t left;
1112
1113 /* [ <media_list> <pseudo>? ] ? */
1114
1115 ctx->media.type = CSS_MEDIA_ALL;
1117
1118 /* Consume any leading whitespace */
1119 while (p < end && isspace(*p))
1120 p++;
1121
1122 if (p < end) {
1123 left = end - p;
1124
1125 css__parse_media_list(&p, &left, &ctx->media);
1126
1127 end = p + left;
1128 }
1129
1130 if (p < end) {
1131 left = end - p;
1132
1133 css__parse_pseudo_list(&p, &left, &ctx->pseudo_element);
1134 }
1135}
1136
1137static void css__parse_expected(line_ctx *ctx, const char *data, size_t len)
1138{
1139 while (ctx->expused + len >= ctx->explen) {
1140 size_t required = ctx->explen == 0 ? 64 : ctx->explen * 2;
1141 char *temp = realloc(ctx->exp, required);
1142 if (temp == NULL) {
1143 assert(0 && "No memory for expected output");
1144 }
1145
1146 ctx->exp = temp;
1147 ctx->explen = required;
1148 }
1149
1150 memcpy(ctx->exp + ctx->expused, data, len);
1151
1152 ctx->expused += len;
1153}
1154
1155static void css__parse_tree_data(line_ctx *ctx, const char *data, size_t len)
1156{
1157 const char *p = data;
1158 const char *end = data + len;
1159 const char *name = NULL;
1160 const char *value = NULL;
1161 size_t namelen = 0;
1162 size_t valuelen = 0;
1163 uint32_t depth = 0;
1164 bool target = false;
1165
1166 /* ' '{depth+1} [ <element> '*'? | <attr> ]
1167 *
1168 * <element> ::= [^=*[:space:]]+
1169 * <attr> ::= [^=*[:space:]]+ '=' [^[:space:]]*
1170 */
1171
1172 while (p < end && isspace(*p)) {
1173 depth++;
1174 p++;
1175 }
1176 depth--;
1177
1178 /* Get element/attribute name */
1179 name = p;
1180 while (p < end && *p != '=' && *p != '*' && isspace(*p) == false) {
1181 namelen++;
1182 p++;
1183 }
1184
1185 /* Skip whitespace */
1186 while (p < end && isspace(*p))
1187 p++;
1188
1189 if (p < end && *p == '=') {
1190 /* Attribute value */
1191 p++;
1192
1193 value = p;
1194
1195 while (p < end && isspace(*p) == false) {
1196 valuelen++;
1197 p++;
1198 }
1199 } else if (p < end && *p == '*') {
1200 /* Element is target node */
1201 target = true;
1202 }
1203
1204 if (value == NULL) {
1205 /* We have an element, so create it */
1206 node *n = malloc(sizeof(node));
1207 assert(n != NULL);
1208
1209 memset(n, 0, sizeof(node));
1210
1211 lwc_intern_string(name, namelen, &n->name);
1212
1213 /* Insert it into tree */
1214 if (ctx->tree == NULL) {
1215 ctx->tree = n;
1216 } else {
1217 assert(depth > 0);
1218 assert(depth <= ctx->depth + 1);
1219
1220 /* Find node to insert into */
1221 while (depth <= ctx->depth) {
1222 ctx->depth--;
1223 ctx->current = ctx->current->parent;
1224 }
1225
1226 /* Insert into current node */
1227 if (ctx->current->children == NULL) {
1228 ctx->current->children = n;
1229 ctx->current->last_child = n;
1230 } else {
1231 ctx->current->last_child->next = n;
1232 n->prev = ctx->current->last_child;
1233
1234 ctx->current->last_child = n;
1235 }
1236 n->parent = ctx->current;
1237 }
1238
1239 ctx->current = n;
1240 ctx->depth = depth;
1241
1242 /* Mark the target, if it's us */
1243 if (target)
1244 ctx->target = n;
1245 } else {
1246 /* New attribute */
1247 bool amatch = false;
1248 attribute *attr;
1249 node *n = ctx->current;
1250
1251 attribute *temp = realloc(n->attrs,
1252 (n->n_attrs + 1) * sizeof(attribute));
1253 assert(temp != NULL);
1254
1255 n->attrs = temp;
1256
1257 attr = &n->attrs[n->n_attrs];
1258
1259 lwc_intern_string(name, namelen, &attr->name);
1260 lwc_intern_string(value, valuelen, &attr->value);
1261
1262 assert(lwc_string_caseless_isequal(
1263 n->attrs[n->n_attrs].name,
1264 ctx->attr_class, &amatch) == lwc_error_ok);
1265 if (amatch == true) {
1266 n->classes = realloc(NULL, sizeof(lwc_string **));
1267 assert(n->classes != NULL);
1268
1269 n->classes[0] = lwc_string_ref(
1270 n->attrs[n->n_attrs].
1271 value);
1272 n->n_classes = 1;
1273 }
1274
1275 n->n_attrs++;
1276 }
1277}
1278
1279
1280static void run_test_select_tree(css_select_ctx *select,
1281 node *node, line_ctx *ctx,
1282 char *buf, size_t *buflen)
1283{
1285 struct node *n = NULL;
1286
1287 if (node->parent == NULL) {
1288 unit_ctx.root_style = NULL;
1289 }
1290
1291
1292 assert(css_select_style(select, node, &unit_ctx, &ctx->media, NULL,
1293 &select_handler, ctx, &sr) == CSS_OK);
1294
1295 if (node->parent != NULL) {
1296 css_computed_style *composed;
1299 sr->styles[ctx->pseudo_element],
1300 &unit_ctx,
1301 &composed) == CSS_OK);
1303 sr->styles[ctx->pseudo_element] = composed;
1304 }
1305
1306 node->sr = sr;
1307
1308 if (node == ctx->target) {
1309 dump_computed_style(sr->styles[ctx->pseudo_element],
1310 buf, buflen, &unit_ctx);
1311 }
1312
1313 if (node->parent == NULL) {
1314 unit_ctx.root_style = node->sr->styles[ctx->pseudo_element];
1315 }
1316
1317 for (n = node->children; n != NULL; n = n->next) {
1318 run_test_select_tree(select, n, ctx, buf, buflen);
1319 }
1320}
1321
1322static void show_differences(size_t len, const char *exp, const char *res)
1323{
1324 const char *pos_exp, *opos_exp;
1325 const char *pos_res, *opos_res;
1326
1327 opos_exp = pos_exp = exp;
1328 opos_res = pos_res = res;
1329
1330 printf("Line differences:\n");
1331 while (pos_exp < exp + len && pos_res < res + len) {
1332 if (*pos_exp == '\n' && *pos_res == '\n') {
1333 if (pos_exp - opos_exp != pos_res - opos_res ||
1334 memcmp(opos_exp, opos_res,
1335 pos_exp - opos_exp) != 0) {
1336 printf("Expected:\t%.*s\n",
1337 (int)(pos_exp - opos_exp),
1338 opos_exp);
1339 printf(" Result:\t%.*s\n",
1340 (int)(pos_res - opos_res),
1341 opos_res);
1342 printf("\n");
1343 }
1344 opos_exp = ++pos_exp;
1345 opos_res = ++pos_res;
1346 } else if (*pos_exp == '\n') {
1347 pos_res++;
1348 } else if (*pos_res == '\n') {
1349 pos_exp++;
1350 } else {
1351 pos_exp++;
1352 pos_res++;
1353 }
1354 }
1355}
1356
1357static void destroy_tree(node *root)
1358{
1359 node *n, *p;
1360 uint32_t i;
1361
1362 for (n = root->children; n != NULL; n = p) {
1363 p = n->next;
1364
1365 destroy_tree(n);
1366 }
1367
1369
1370 for (i = 0; i < root->n_attrs; ++i) {
1371 lwc_string_unref(root->attrs[i].name);
1372 lwc_string_unref(root->attrs[i].value);
1373 }
1374 free(root->attrs);
1375
1376 if (root->classes != NULL) {
1377 for (i = 0; i < root->n_classes; ++i) {
1378 lwc_string_unref(root->classes[i]);
1379 }
1380 free(root->classes);
1381 }
1382
1383 if (root->libcss_node_data != NULL) {
1385 NULL, root, NULL, root->libcss_node_data);
1386 }
1387
1388 lwc_string_unref(root->name);
1389 free(root);
1390}
1391
1392static void run_test(line_ctx *ctx, const char *exp, size_t explen)
1393{
1394 css_select_ctx *select;
1395 css_select_results *results;
1396 uint32_t i;
1397 char *buf;
1398 size_t buflen;
1399 static int testnum;
1400
1401 UNUSED(exp);
1402
1403 buf = malloc(8192);
1404 if (buf == NULL) {
1405 assert(0 && "No memory for result data");
1406 }
1407 buflen = 8192;
1408
1409 assert(css_select_ctx_create(&select) == CSS_OK);
1410
1411 for (i = 0; i < ctx->n_sheets; i++) {
1413 ctx->sheets[i].sheet, ctx->sheets[i].origin,
1414 ctx->sheets[i].media) == CSS_OK);
1415 }
1416
1417 testnum++;
1418
1419 run_test_select_tree(select, ctx->tree, ctx, buf, &buflen);
1420
1421 results = ctx->target->sr;
1422 assert(results->styles[ctx->pseudo_element] != NULL);
1423
1424 if (8192 - buflen != explen || memcmp(buf, exp, explen) != 0) {
1425 size_t len = 8192 - buflen < explen ? 8192 - buflen : explen;
1426 printf("Expected (%u):\n%.*s\n",
1427 (int) explen, (int) explen, exp);
1428 printf("Result (%u):\n%.*s\n", (int) (8192 - buflen),
1429 (int) (8192 - buflen), buf);
1430
1431 show_differences(len, exp, buf);
1432 assert(0 && "Result doesn't match expected");
1433 }
1434
1435 /* Clean up */
1436 css_select_ctx_destroy(select);
1437 destroy_tree(ctx->tree);
1438
1439 for (i = 0; i < ctx->n_sheets; i++) {
1441 free(ctx->sheets[i].media);
1442 }
1443
1444 ctx->tree = NULL;
1445 ctx->current = NULL;
1446 ctx->depth = 0;
1447 ctx->n_sheets = 0;
1448 free(ctx->sheets);
1449 ctx->sheets = NULL;
1450 ctx->target = NULL;
1451
1452 free(buf);
1453
1454 printf("Test %d: PASS\n", testnum);
1455}
1456
1457static bool handle_line(const char *data, size_t datalen, void *pw)
1458{
1459 line_ctx *ctx = (line_ctx *) pw;
1460 css_error error;
1461
1462 if (data[0] == '#') {
1463 if (ctx->intree) {
1464 if (strncasecmp(data+1, "errors", 6) == 0) {
1465 ctx->intree = false;
1466 ctx->insheet = false;
1467 ctx->inerrors = true;
1468 ctx->inexp = false;
1469 } else {
1470 /* Assume start of stylesheet */
1471 css__parse_sheet(ctx, data + 1, datalen - 1);
1472
1473 ctx->intree = false;
1474 ctx->insheet = true;
1475 ctx->inerrors = false;
1476 ctx->inexp = false;
1477 }
1478 } else if (ctx->insheet) {
1479 if (strncasecmp(data+1, "errors", 6) == 0) {
1481 ctx->sheets[ctx->n_sheets - 1].sheet)
1482 == CSS_OK);
1483
1484 ctx->intree = false;
1485 ctx->insheet = false;
1486 ctx->inerrors = true;
1487 ctx->inexp = false;
1488 } else if (strncasecmp(data+1, "ua", 2) == 0 ||
1489 strncasecmp(data+1, "user", 4) == 0 ||
1490 strncasecmp(data+1, "author", 6) == 0) {
1492 ctx->sheets[ctx->n_sheets - 1].sheet)
1493 == CSS_OK);
1494
1495 css__parse_sheet(ctx, data + 1, datalen - 1);
1496 } else {
1498 ctx->sheets[ctx->n_sheets - 1].sheet,
1499 (const uint8_t *) data,
1500 datalen);
1501 assert(error == CSS_OK ||
1502 error == CSS_NEEDDATA);
1503 }
1504 } else if (ctx->inerrors) {
1505 ctx->intree = false;
1506 ctx->insheet = false;
1507 ctx->inerrors = false;
1508 ctx->inexp = true;
1509 } else if (ctx->inexp) {
1510 /* This marks end of testcase, so run it */
1511 run_test(ctx, ctx->exp, ctx->expused);
1512
1513 ctx->expused = 0;
1514
1515 ctx->intree = false;
1516 ctx->insheet = false;
1517 ctx->inerrors = false;
1518 ctx->inexp = false;
1519 } else {
1520 /* Start state */
1521 if (strncasecmp(data+1, "tree", 4) == 0) {
1522 css__parse_tree(ctx, data + 5, datalen - 5);
1523
1524 ctx->intree = true;
1525 ctx->insheet = false;
1526 ctx->inerrors = false;
1527 ctx->inexp = false;
1528 }
1529 }
1530 } else {
1531 if (ctx->intree) {
1532 /* Not interested in the '|' */
1533 css__parse_tree_data(ctx, data + 1, datalen - 1);
1534 } else if (ctx->insheet) {
1536 ctx->sheets[ctx->n_sheets - 1].sheet,
1537 (const uint8_t *) data, datalen);
1538 assert(error == CSS_OK || error == CSS_NEEDDATA);
1539 } else if (ctx->inexp) {
1540 css__parse_expected(ctx, data, datalen);
1541 }
1542 }
1543
1544 return true;
1545}
1546
1547int main(int argc, char **argv)
1548{
1549 line_ctx ctx;
1550
1551 if (argc != 2) {
1552 printf("Usage: %s <filename>\n", argv[0]);
1553 return 1;
1554 }
1555
1556 memset(&ctx, 0, sizeof(ctx));
1557
1558
1559 lwc_intern_string("class", SLEN("class"), &ctx.attr_class);
1560 lwc_intern_string("id", SLEN("id"), &ctx.attr_id);
1561
1562 assert(css__parse_testfile(argv[1], handle_line, &ctx) == true);
1563
1564 /* and run final test */
1565 if (ctx.tree != NULL)
1566 run_test(&ctx, ctx.exp, ctx.expused);
1567
1568 free(ctx.exp);
1569
1570 lwc_string_unref(ctx.attr_class);
1571 lwc_string_unref(ctx.attr_id);
1572
1573 lwc_iterate_strings(printing_lwc_iterator, NULL);
1574
1575 assert(fail_because_lwc_leaked == false);
1576
1577 printf("PASS\n");
1578 return 0;
1579}
css_error
Definition errors.h:18
@ CSS_INVALID
Definition errors.h:23
@ CSS_NOMEM
Definition errors.h:21
@ CSS_BADPARM
Definition errors.h:22
@ CSS_NEEDDATA
Definition errors.h:25
@ CSS_OK
Definition errors.h:19
#define UNUSED(x)
Definition example1.c:18
#define INTTOFIX(a)
Definition fpmath.h:127
#define CSS_RADIX_POINT
Definition fpmath.h:20
css_error css_computed_style_destroy(css_computed_style *style)
Definition computed.c:101
css_error css_computed_style_compose(const css_computed_style *restrict parent, const css_computed_style *restrict child, const css_unit_ctx *unit_ctx, css_computed_style **restrict result)
Definition computed.c:290
@ CSS_FONT_STYLE_NORMAL
Definition properties.h:526
@ CSS_FONT_WEIGHT_NORMAL
Definition properties.h:539
@ CSS_PROP_VOICE_FAMILY
Definition properties.h:109
@ CSS_PROP_FONT_FAMILY
Definition properties.h:53
@ CSS_PROP_COLOR
Definition properties.h:41
@ CSS_PROP_QUOTES
Definition properties.h:91
@ CSS_FONT_FAMILY_SANS_SERIF
Definition properties.h:504
@ CSS_FONT_VARIANT_NORMAL
Definition properties.h:533
@ CSS_COLOR_COLOR
Definition properties.h:316
@ CSS_QUOTES_NONE
Definition properties.h:781
css_error css_select_results_destroy(css_select_results *results)
Definition select.c:1535
css_error css_select_style(css_select_ctx *ctx, void *node, const css_unit_ctx *unit_ctx, const css_media *media, const css_stylesheet *inline_style, css_select_handler *handler, void *pw, css_select_results **result)
Definition select.c:1255
css_error css_select_ctx_destroy(css_select_ctx *ctx)
Definition select.c:276
css_error css_libcss_node_data_handler(css_select_handler *handler, css_node_data_action action, void *pw, void *node, void *clone_node, void *libcss_node_data)
Definition select.c:184
@ CSS_SELECT_HANDLER_VERSION_1
Definition select.h:48
css_error css_select_ctx_append_sheet(css_select_ctx *ctx, const css_stylesheet *sheet, css_origin origin, const char *media)
Definition select.c:309
@ CSS_NODE_DELETED
Definition select.h:165
@ CSS_PSEUDO_ELEMENT_AFTER
Definition select.h:28
@ CSS_PSEUDO_ELEMENT_BEFORE
Definition select.h:27
@ CSS_PSEUDO_ELEMENT_NONE
Definition select.h:24
@ CSS_PSEUDO_ELEMENT_FIRST_LINE
Definition select.h:25
@ CSS_PSEUDO_ELEMENT_FIRST_LETTER
Definition select.h:26
css_error css_select_ctx_create(css_select_ctx **result)
Definition select.c:240
css_error css_stylesheet_data_done(css_stylesheet *sheet)
Definition stylesheet.c:334
@ CSS_STYLESHEET_PARAMS_VERSION_1
Definition stylesheet.h:92
css_error css_stylesheet_append_data(css_stylesheet *sheet, const uint8_t *data, size_t len)
Definition stylesheet.c:314
css_error css_stylesheet_destroy(css_stylesheet *sheet)
Definition stylesheet.c:255
css_error css_stylesheet_create(const css_stylesheet_params *params, css_stylesheet **stylesheet)
Definition stylesheet.c:128
Definition select.c:18
lwc_string * value
Definition select.c:20
lwc_string * name
Definition select.c:19
Definition autogenerated_computed.h:282
Definition hint.h:34
uint8_t status
Definition hint.h:53
lwc_string ** strings
Definition hint.h:49
css_color color
Definition hint.h:38
union css_hint::@3 data
Definition types.h:204
css_media_type type
Definition types.h:206
Definition types.h:246
lwc_string * name
Definition types.h:259
Definition select.c:50
Definition select.h:51
Definition select.h:36
css_computed_style * styles[CSS_PSEUDO_ELEMENT_COUNT]
Definition select.h:44
Definition stylesheet.h:98
const char * url
Definition stylesheet.h:108
css_font_resolution_fn font
Definition stylesheet.h:133
css_color_resolution_fn color
Definition stylesheet.h:128
const char * charset
Definition stylesheet.h:106
bool allow_quirks
Definition stylesheet.h:113
void * import_pw
Definition stylesheet.h:125
void * color_pw
Definition stylesheet.h:130
const char * title
Definition stylesheet.h:110
css_import_notification_fn import
Definition stylesheet.h:123
void * resolve_pw
Definition stylesheet.h:120
uint32_t params_version
Definition stylesheet.h:100
css_url_resolution_fn resolve
Definition stylesheet.h:118
void * font_pw
Definition stylesheet.h:135
css_language_level level
Definition stylesheet.h:103
bool inline_style
Definition stylesheet.h:115
Definition stylesheet.h:170
Definition stylesheet.h:63
enum css_font_style_e style
Definition stylesheet.h:64
struct css_system_font::@6 line_height
enum css_font_weight_e weight
Definition stylesheet.h:66
css_unit unit
Definition stylesheet.h:69
css_fixed size
Definition stylesheet.h:68
lwc_string * family
Definition stylesheet.h:76
enum css_font_variant_e variant
Definition stylesheet.h:65
Definition unit.h:39
css_fixed font_size_default
Definition unit.h:53
const css_computed_style * root_style
Definition unit.h:67
Definition csdetect.c:16
css_media media
Definition select.c:65
node * current
Definition select.c:59
lwc_string * attr_class
Definition select.c:69
exp_entry * exp
Definition lex-auto.c:32
size_t explen
Definition lex-auto.c:30
bool inexp
Definition lex-auto.c:36
bool inerrors
Definition parse-auto.c:42
bool intree
Definition select.c:53
lwc_string * attr_id
Definition select.c:70
bool insheet
Definition select.c:54
uint32_t depth
Definition select.c:60
node * target
Definition select.c:67
uint32_t pseudo_element
Definition select.c:66
uint32_t n_sheets
Definition select.c:62
size_t expused
Definition lex-auto.c:31
node * tree
Definition select.c:58
sheet_ctx * sheets
Definition select.c:63
Definition select.c:23
struct node * parent
Definition select.c:35
struct node * children
Definition select.c:38
attribute * attrs
Definition select.c:30
struct node * prev
Definition select.c:37
uint32_t n_attrs
Definition select.c:29
css_select_results * sr
Definition select.c:32
lwc_string ** classes
Definition select.c:27
struct node * next
Definition select.c:36
lwc_string * name
Definition select.c:24
void * libcss_node_data
Definition select.c:33
uint32_t n_classes
Definition select.c:26
struct node * last_child
Definition select.c:39
Definition select.c:42
char * media
Definition select.c:45
css_origin origin
Definition select.c:44
css_stylesheet * sheet
Definition select.c:43
int main(int argc, char **argv)
Definition select.c:1547
#define assert(expr)
Definition testutils.h:32
bool css__parse_testfile(const char *filename, line_func callback, void *pw)
Definition testutils.h:51
@ CSS_LEVEL_21
Definition types.h:43
@ CSS_UNIT_EM
Definition types.h:85
@ CSS_UNIT_PT
Definition types.h:89
css_origin
Definition types.h:72
@ CSS_ORIGIN_USER
Definition types.h:74
@ CSS_ORIGIN_UA
Definition types.h:73
@ CSS_ORIGIN_AUTHOR
Definition types.h:75
@ CSS_MEDIA_BRAILLE
Definition types.h:53
@ CSS_MEDIA_TTY
Definition types.h:60
@ CSS_MEDIA_EMBOSSED
Definition types.h:54
@ CSS_MEDIA_SCREEN
Definition types.h:58
@ CSS_MEDIA_TV
Definition types.h:61
@ CSS_MEDIA_ALL
Definition types.h:62
@ CSS_MEDIA_PRINT
Definition types.h:56
@ CSS_MEDIA_HANDHELD
Definition types.h:55
@ CSS_MEDIA_PROJECTION
Definition types.h:57
@ CSS_MEDIA_AURAL
Definition types.h:52
@ CSS_MEDIA_SPEECH
Definition types.h:59
#define SLEN(s)
Definition utils.h:26