static fz_css_selector *parse_adjacent_selector(struct lexbuf *buf) { fz_css_selector *s, *a, *b; a = parse_simple_selector(buf); if (accept(buf, '+')) { b = parse_adjacent_selector(buf); s = fz_new_css_selector(buf->ctx, NULL); s->combine = '+'; s->left = a; s->right = b; return s; } return a; }
/* flags: only XML_IGNORE_CASE is handled */ void css_parse_style_sheet(CSSStyleSheet *s, CSSParseState *b) { char value[1024]; char tag[64]; char tag_id[64]; char *q; int ch, media, val, last_tree_op, i; CSSStyleSheetEntry *e, **first_eprops; CSSSimpleSelector ss2, *ss = &ss2, *last_ss, *ss1; CSSProperty *props; ch = bgetc(b); media = CSS_MEDIA_ALL; for (;;) { redo: first_eprops = s->plast_entry; bskip_spaces(b, &ch); if (ch == EOF) break; /* eat inserted HTML comments for compatible STYLE tag parsing */ if (ch == '<') { beat(b, &ch, "<!--"); goto redo; } else if (ch == '-') { beat(b, &ch, "-->"); goto redo; } /* handle '@media { ... }' */ if (ch == '@') { ch = bgetc(b); read_ident(b, &ch, tag, sizeof(tag)); switch (css_get_enum(tag, "media,page")) { case 0: /* @media */ media = 0; for (;;) { bskip_spaces(b, &ch); read_ident(b, &ch, tag, sizeof(tag)); val = css_get_enum(tag, "tty,screen,print,tv,speech,all"); if (val < 0 || val == 5) media = CSS_MEDIA_ALL; else media |= (1 << val); bskip_spaces(b, &ch); if (ch == ',') { ch = bgetc(b); } else if (ch == '{' || ch == EOF) { ch = bgetc(b); break; } } goto redo; case 1: /* @page */ bskip_spaces(b, &ch); if (ch != '{') { read_ident(b, &ch, tag_id, sizeof(tag_id)); bskip_spaces(b, &ch); } memset(ss, 0, sizeof(CSSSimpleSelector)); ss->tag = css_new_ident("@page"); if (tag_id[0] != '\0') ss->tag_id = css_new_ident(tag_id); add_style_entry(s, ss, media); goto parse_props; default: css_error1(b, "unrecognized css directive '@%s'", tag); break; } } else if (ch == '}') { /* XXX: end of media, should unstack */ ch = bgetc(b); goto redo; } /* parse a selector list */ for (;;) { /* parse simple selectors with operations */ last_ss = NULL; last_tree_op = CSS_TREE_OP_NONE; for (;;) { int tree_op; bskip_spaces(b, &ch); parse_simple_selector(ss, b, &ch); bskip_spaces(b, &ch); ss->tree_op = last_tree_op; ss->next = last_ss; if (ch == '+') { tree_op = CSS_TREE_OP_PRECEEDED; ch = bgetc(b); goto add_tree; } else if (ch == '>') { tree_op = CSS_TREE_OP_CHILD; ch = bgetc(b); goto add_tree; } else if (isalpha(ch)) { tree_op = CSS_TREE_OP_DESCENDANT; add_tree: ss1 = malloc(sizeof(CSSSimpleSelector)); if (ss1) { memcpy(ss1, ss, sizeof(CSSSimpleSelector)); last_ss = ss1; } last_tree_op = tree_op; } else { /* other char: exit */ break; } } add_style_entry(s, ss, media); /* get next selector, if present */ if (ch != ',') break; ch = bgetc(b); } parse_props: /* expect start of properties */ if (ch != '{') break; ch = bgetc(b); q = value; while (ch != '}' && ch != EOF) { if ((q - value) < (int)sizeof(value) - 1) *q++ = ch; ch = bgetc(b); } *q = '\0'; if (ch == '}') ch = bgetc(b); /* the properties are extracted, now add them to each tag */ /* XXX: should locate entries first, then add, to avoid adding duplicate entries */ /* XXX: should put font properties first to avoid em/ex units problems, but it would still not be sufficient. */ props = css_parse_properties(b, value); i = 0; for (e = *first_eprops; e != NULL; e = e->next) { if (i == 0) e->props = props; else e->props = dup_properties(props); i++; } } #ifdef DEBUG css_dump_style_sheet(s); #endif }