aboutsummaryrefslogtreecommitdiff
path: root/src/cssparser.cc
diff options
context:
space:
mode:
authorJohannes Hofmann <Johannes.Hofmann@gmx.de>2010-08-20 23:24:19 +0200
committerJohannes Hofmann <Johannes.Hofmann@gmx.de>2010-08-20 23:24:19 +0200
commitf5c598b518d1f906148534d015f50075d3e8242d (patch)
tree21dd70add5b366c3dd80641b77f6b18e0baa009e /src/cssparser.cc
parente98d02a01ffeb18ede86af025e51ae1ec011c75a (diff)
parent5f0fc0e48b8cbee7e1795935da0abff6627fd498 (diff)
merge
Diffstat (limited to 'src/cssparser.cc')
-rw-r--r--src/cssparser.cc1476
1 files changed, 1476 insertions, 0 deletions
diff --git a/src/cssparser.cc b/src/cssparser.cc
new file mode 100644
index 00000000..00ba7428
--- /dev/null
+++ b/src/cssparser.cc
@@ -0,0 +1,1476 @@
+/*
+ * File: cssparser.cc
+ *
+ * Copyright 2004 Sebastian Geerken <sgeerken@dillo.org>
+ * Copyright 2008-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * This file is heavily based on the CSS parser of dillo-0.8.0-css-3 -
+ * a dillo1 based CSS prototype written by Sebastian Geerken.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "msg.h"
+#include "colors.h"
+#include "html_common.hh"
+#include "css.hh"
+#include "cssparser.hh"
+
+using namespace dw::core::style;
+
+#define DEBUG_MSG(A, B, ...) _MSG(B, __VA_ARGS__)
+#define MSG_CSS(A, ...) MSG(A, __VA_ARGS__)
+#define DEBUG_TOKEN_LEVEL 0
+#define DEBUG_PARSE_LEVEL 0
+#define DEBUG_CREATE_LEVEL 0
+
+#define DEBUG_LEVEL 10
+
+/* The last three ones are never parsed. */
+#define CSS_NUM_INTERNAL_PROPERTIES 3
+#define CSS_NUM_PARSED_PROPERTIES \
+ (CSS_PROPERTY_LAST - CSS_NUM_INTERNAL_PROPERTIES)
+
+
+typedef struct {
+ const char *symbol;
+ const CssValueType type[3];
+ const char *const *enum_symbols;
+} CssPropertyInfo;
+
+static const char *const Css_border_style_enum_vals[] = {
+ "none", "hidden", "dotted", "dashed", "solid", "double", "groove",
+ "ridge", "inset", "outset", NULL
+};
+
+static const char *const Css_border_width_enum_vals[] = {
+ "thin", "medium", "thick", NULL
+};
+
+static const char *const Css_cursor_enum_vals[] = {
+ "crosshair", "default", "pointer", "move", "e-resize", "ne-resize",
+ "nw-resize", "n-resize", "se-resize", "sw-resize", "s-resize",
+ "w-resize", "text", "wait", "help", NULL
+};
+
+static const char *const Css_display_enum_vals[] = {
+ "block", "inline", "list-item", "none", "table", "table-row-group",
+ "table-header-group", "table-footer-group", "table-row",
+ "table-cell", NULL
+};
+
+static const char *const Css_font_size_enum_vals[] = {
+ "large", "larger", "medium", "small", "smaller", "xx-large", "xx-small",
+ "x-large", "x-small", NULL
+};
+
+static const char *const Css_font_style_enum_vals[] = {
+ "normal", "italic", "oblique", NULL
+};
+
+static const char *const Css_font_weight_enum_vals[] = {
+ "bold", "bolder", "light", "lighter", "normal", NULL
+};
+
+static const char *const Css_letter_spacing_enum_vals[] = {
+ "normal", NULL
+};
+
+static const char *const Css_list_style_position_enum_vals[] = {
+ "inside", "outside", NULL
+};
+
+static const char *const Css_line_height_enum_vals[] = {
+ "normal", NULL
+};
+
+static const char *const Css_list_style_type_enum_vals[] = {
+ "disc", "circle", "square", "decimal", "decimal-leading-zero",
+ "lower-roman", "upper-roman", "lower-greek", "lower-alpha",
+ "lower-latin", "upper-alpha", "upper-latin", "hebrew", "armenian",
+ "georgian", "cjk-ideographic", "hiragana", "katakana", "hiragana-iroha",
+ "katakana-iroha", "none", NULL
+};
+
+static const char *const Css_text_align_enum_vals[] = {
+ "left", "right", "center", "justify", "string", NULL
+};
+
+static const char *const Css_text_decoration_enum_vals[] = {
+ "underline", "overline", "line-through", "blink", NULL
+};
+
+static const char *const Css_vertical_align_vals[] = {
+ "top", "bottom", "middle", "baseline", "sub", "super", "text-top",
+ "text-bottom", NULL
+};
+
+static const char *const Css_white_space_vals[] = {
+ "normal", "pre", "nowrap", "pre-wrap", "pre-line", NULL
+};
+
+static const char *const Css_word_spacing_enum_vals[] = {
+ "normal", NULL
+};
+
+const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
+ {"background-attachment", {CSS_TYPE_UNUSED}, NULL},
+ {"background-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"background-image", {CSS_TYPE_UNUSED}, NULL},
+ {"background-position", {CSS_TYPE_UNUSED}, NULL},
+ {"background-repeat", {CSS_TYPE_UNUSED}, NULL},
+ {"border-bottom-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"border-bottom-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_border_style_enum_vals},
+ {"border-bottom-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
+ Css_border_width_enum_vals},
+ {"border-collapse", {CSS_TYPE_UNUSED}, NULL},
+ {"border-left-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"border-left-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_border_style_enum_vals},
+ {"border-left-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
+ Css_border_width_enum_vals},
+ {"border-right-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"border-right-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_border_style_enum_vals},
+ {"border-rigth-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
+ Css_border_width_enum_vals},
+ {"border-spacing", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"border-top-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"border-top-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_border_style_enum_vals},
+ {"border-top-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
+ Css_border_width_enum_vals},
+ {"bottom", {CSS_TYPE_UNUSED}, NULL},
+ {"caption-side", {CSS_TYPE_UNUSED}, NULL},
+ {"clear", {CSS_TYPE_UNUSED}, NULL},
+ {"clip", {CSS_TYPE_UNUSED}, NULL},
+ {"color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"content", {CSS_TYPE_STRING, CSS_TYPE_UNUSED}, NULL},
+ {"counter-increment", {CSS_TYPE_UNUSED}, NULL},
+ {"counter-reset", {CSS_TYPE_UNUSED}, NULL},
+ {"cursor", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_cursor_enum_vals},
+ {"direction", {CSS_TYPE_UNUSED}, NULL},
+ {"display", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_display_enum_vals},
+ {"empty-cells", {CSS_TYPE_UNUSED}, NULL},
+ {"float", {CSS_TYPE_UNUSED}, NULL},
+ {"font-family", {CSS_TYPE_SYMBOL, CSS_TYPE_UNUSED}, NULL},
+ {"font-size", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED},
+ Css_font_size_enum_vals},
+ {"font-size-adjust", {CSS_TYPE_UNUSED}, NULL},
+ {"font-stretch", {CSS_TYPE_UNUSED}, NULL},
+ {"font-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_font_style_enum_vals},
+ {"font-variant", {CSS_TYPE_UNUSED}, NULL},
+ {"font-weight", {CSS_TYPE_ENUM, CSS_TYPE_FONT_WEIGHT, CSS_TYPE_UNUSED},
+ Css_font_weight_enum_vals},
+ {"height", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
+ {"left", {CSS_TYPE_UNUSED}, NULL},
+ {"letter-spacing", {CSS_TYPE_ENUM, CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED},
+ Css_letter_spacing_enum_vals},
+ {"line-height",
+ {CSS_TYPE_ENUM, CSS_TYPE_LENGTH_PERCENTAGE_NUMBER, CSS_TYPE_UNUSED},
+ Css_line_height_enum_vals},
+ {"list-style-image", {CSS_TYPE_UNUSED}, NULL},
+ {"list-style-position", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_list_style_position_enum_vals},
+ {"list-style-type", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_list_style_type_enum_vals},
+ {"margin-bottom", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"margin-left", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"margin-right", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"margin-top", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"marker-offset", {CSS_TYPE_UNUSED}, NULL},
+ {"marks", {CSS_TYPE_UNUSED}, NULL},
+ {"max-height", {CSS_TYPE_UNUSED}, NULL},
+ {"max-width", {CSS_TYPE_UNUSED}, NULL},
+ {"min-height", {CSS_TYPE_UNUSED}, NULL},
+ {"min-width", {CSS_TYPE_UNUSED}, NULL},
+ {"outline-color", {CSS_TYPE_UNUSED}, NULL},
+ {"outline-style", {CSS_TYPE_UNUSED}, NULL},
+ {"outline-width", {CSS_TYPE_UNUSED}, NULL},
+ {"overflow", {CSS_TYPE_UNUSED}, NULL},
+ {"padding-bottom", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"padding-left", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"padding-right", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"padding-top", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"position", {CSS_TYPE_UNUSED}, NULL},
+ {"quotes", {CSS_TYPE_UNUSED}, NULL},
+ {"right", {CSS_TYPE_UNUSED}, NULL},
+ {"text-align", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_text_align_enum_vals},
+ {"text-decoration", {CSS_TYPE_MULTI_ENUM, CSS_TYPE_UNUSED},
+ Css_text_decoration_enum_vals},
+ {"text-indent", {CSS_TYPE_UNUSED}, NULL},
+ {"text-shadow", {CSS_TYPE_UNUSED}, NULL},
+ {"text-transform", {CSS_TYPE_UNUSED}, NULL},
+ {"top", {CSS_TYPE_UNUSED}, NULL},
+ {"unicode-bidi", {CSS_TYPE_UNUSED}, NULL},
+ {"vertical-align",{CSS_TYPE_ENUM, CSS_TYPE_UNUSED},Css_vertical_align_vals},
+ {"visibility", {CSS_TYPE_UNUSED}, NULL},
+ {"white-space", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_white_space_vals},
+ {"width", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
+ {"word-spacing", {CSS_TYPE_ENUM, CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED},
+ Css_word_spacing_enum_vals},
+ {"z-index", {CSS_TYPE_UNUSED}, NULL},
+
+ /* These are extensions, for internal used, and never parsed. */
+ {"x-link", {CSS_TYPE_INTEGER, CSS_TYPE_UNUSED}, NULL},
+ {"x-colspan", {CSS_TYPE_INTEGER, CSS_TYPE_UNUSED}, NULL},
+ {"x-rowspan", {CSS_TYPE_INTEGER, CSS_TYPE_UNUSED}, NULL},
+ {"last", {CSS_TYPE_UNUSED}, NULL},
+};
+
+typedef struct {
+ const char *symbol;
+ enum {
+ CSS_SHORTHAND_MULTIPLE, /* [ p1 || p2 || ...], the property pi is
+ * determined by the type */
+ CSS_SHORTHAND_DIRECTIONS, /* <t>{1,4} */
+ CSS_SHORTHAND_BORDER, /* special, used for 'border' */
+ CSS_SHORTHAND_FONT, /* special, used for 'font' */
+ } type;
+ const CssPropertyName * properties;/* CSS_SHORTHAND_MULTIPLE:
+ * must be terminated by -1
+ * CSS_SHORTHAND_DIRECTIONS:
+ * must have length 4
+ * CSS_SHORTHAND_BORDERS:
+ * must have length 12
+ * CSS_SHORTHAND_FONT:
+ * unused */
+} CssShorthandInfo;
+
+const CssPropertyName Css_background_properties[] = {
+ CSS_PROPERTY_BACKGROUND_COLOR,
+ CSS_PROPERTY_BACKGROUND_IMAGE,
+ CSS_PROPERTY_BACKGROUND_REPEAT,
+ CSS_PROPERTY_BACKGROUND_ATTACHMENT,
+ CSS_PROPERTY_BACKGROUND_POSITION,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_bottom_properties[] = {
+ CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_PROPERTY_BORDER_BOTTOM_STYLE,
+ CSS_PROPERTY_BORDER_BOTTOM_COLOR,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_color_properties[4] = {
+ CSS_PROPERTY_BORDER_TOP_COLOR,
+ CSS_PROPERTY_BORDER_BOTTOM_COLOR,
+ CSS_PROPERTY_BORDER_LEFT_COLOR,
+ CSS_PROPERTY_BORDER_RIGHT_COLOR
+};
+
+const CssPropertyName Css_border_left_properties[] = {
+ CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_PROPERTY_BORDER_LEFT_STYLE,
+ CSS_PROPERTY_BORDER_LEFT_COLOR,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_right_properties[] = {
+ CSS_PROPERTY_BORDER_RIGHT_WIDTH,
+ CSS_PROPERTY_BORDER_RIGHT_STYLE,
+ CSS_PROPERTY_BORDER_RIGHT_COLOR,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_style_properties[] = {
+ CSS_PROPERTY_BORDER_TOP_STYLE,
+ CSS_PROPERTY_BORDER_BOTTOM_STYLE,
+ CSS_PROPERTY_BORDER_LEFT_STYLE,
+ CSS_PROPERTY_BORDER_RIGHT_STYLE
+};
+
+const CssPropertyName Css_border_top_properties[] = {
+ CSS_PROPERTY_BORDER_TOP_WIDTH,
+ CSS_PROPERTY_BORDER_TOP_STYLE,
+ CSS_PROPERTY_BORDER_TOP_COLOR,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_width_properties[] = {
+ CSS_PROPERTY_BORDER_TOP_WIDTH,
+ CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_PROPERTY_BORDER_RIGHT_WIDTH
+};
+
+const CssPropertyName Css_list_style_properties[] = {
+ CSS_PROPERTY_LIST_STYLE_TYPE,
+ CSS_PROPERTY_LIST_STYLE_POSITION,
+ CSS_PROPERTY_LIST_STYLE_IMAGE,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_margin_properties[] = {
+ CSS_PROPERTY_MARGIN_TOP,
+ CSS_PROPERTY_MARGIN_BOTTOM,
+ CSS_PROPERTY_MARGIN_LEFT,
+ CSS_PROPERTY_MARGIN_RIGHT
+};
+
+const CssPropertyName Css_outline_properties[] = {
+ CSS_PROPERTY_OUTLINE_COLOR,
+ CSS_PROPERTY_OUTLINE_STYLE,
+ CSS_PROPERTY_OUTLINE_WIDTH,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_padding_properties[] = {
+ CSS_PROPERTY_PADDING_TOP,
+ CSS_PROPERTY_PADDING_BOTTOM,
+ CSS_PROPERTY_PADDING_LEFT,
+ CSS_PROPERTY_PADDING_RIGHT
+};
+
+const CssPropertyName Css_border_properties[] = {
+ CSS_PROPERTY_BORDER_TOP_WIDTH,
+ CSS_PROPERTY_BORDER_TOP_STYLE,
+ CSS_PROPERTY_BORDER_TOP_COLOR,
+ CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_PROPERTY_BORDER_BOTTOM_STYLE,
+ CSS_PROPERTY_BORDER_BOTTOM_COLOR,
+ CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_PROPERTY_BORDER_LEFT_STYLE,
+ CSS_PROPERTY_BORDER_LEFT_COLOR,
+ CSS_PROPERTY_BORDER_RIGHT_WIDTH,
+ CSS_PROPERTY_BORDER_RIGHT_STYLE,
+ CSS_PROPERTY_BORDER_RIGHT_COLOR
+};
+
+const CssPropertyName Css_font_properties[] = {
+ CSS_PROPERTY_FONT_SIZE,
+ CSS_PROPERTY_FONT_STYLE,
+ CSS_PROPERTY_FONT_VARIANT,
+ CSS_PROPERTY_FONT_WEIGHT,
+ CSS_PROPERTY_FONT_FAMILY,
+ (CssPropertyName) - 1
+};
+
+static const CssShorthandInfo Css_shorthand_info[] = {
+ {"background", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_background_properties},
+ {"border", CssShorthandInfo::CSS_SHORTHAND_BORDER,
+ Css_border_properties},
+ {"border-bottom", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_border_bottom_properties},
+ {"border-color", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_border_color_properties},
+ {"border-left", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_border_left_properties},
+ {"border-right", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_border_right_properties},
+ {"border-style", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_border_style_properties},
+ {"border-top", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_border_top_properties},
+ {"border-width", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_border_width_properties},
+ {"font", CssShorthandInfo::CSS_SHORTHAND_FONT,
+ Css_font_properties},
+ {"list-style", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_list_style_properties},
+ {"margin", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_margin_properties},
+ {"outline", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_outline_properties},
+ {"padding", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_padding_properties},
+};
+
+#define CSS_SHORTHAND_NUM \
+ (sizeof(Css_shorthand_info) / sizeof(CssShorthandInfo))
+
+/* ----------------------------------------------------------------------
+ * Parsing
+ * ---------------------------------------------------------------------- */
+
+CssParser::CssParser(CssContext *context, CssOrigin origin,
+ const char *buf, int buflen)
+{
+ this->context = context;
+ this->origin = origin;
+ this->buf = buf;
+ this->buflen = buflen;
+ this->bufptr = 0;
+ this->spaceSeparated = false;
+ this->withinBlock = false;
+
+ nextToken ();
+}
+
+/*
+ * Gets the next character from the buffer, or EOF.
+ */
+int CssParser::getChar()
+{
+ int c;
+
+ if (bufptr >= buflen)
+ c = EOF;
+ else
+ c = buf[bufptr];
+
+ /* The buffer pointer is increased in any case, so that ungetChar works
+ * correctly at the end of the buffer. */
+ bufptr++;
+ return c;
+}
+
+/*
+ * Undoes the last getChar().
+ */
+void CssParser::ungetChar()
+{
+ bufptr--;
+}
+
+/*
+ * Skip string str if it is found in the input buffer.
+ * If not wind back. The first char is passed as parameter c
+ * to avoid unnecessary getChar() / ungetChar() calls.
+ */
+inline bool CssParser::skipString(int c, const char *str)
+{
+ int n = 0;
+
+ while (str[n]) {
+ if (str[n] != c) {
+ while (n--)
+ ungetChar();
+ return false;
+ }
+ c = getChar();
+ n++;
+ }
+
+ return true;
+}
+
+void CssParser::nextToken()
+{
+ int c, c1, d, j;
+ char hexbuf[5];
+ int i = 0;
+
+ ttype = CSS_TK_CHAR; /* init */
+ spaceSeparated = false;
+
+ while (true) {
+ c = getChar();
+ if (isspace(c)) { // ignore whitespace
+ spaceSeparated = true;
+ } else if (skipString(c, "/*")) { // ignore comments
+ do {
+ c = getChar();
+ } while (c != EOF && ! skipString(c, "*/"));
+ } else if (skipString(c, "<!--")) { // ignore XML comment markers
+ } else if (skipString(c, "-->")) {
+ } else {
+ break;
+ }
+ }
+
+ // handle negative numbers
+ if (c == '-') {
+ if (i < maxStrLen - 1)
+ tval[i++] = c;
+ c = getChar();
+ }
+
+ if (isdigit(c)) {
+ ttype = CSS_TK_DECINT;
+ do {
+ if (i < maxStrLen - 1) {
+ tval[i++] = c;
+ }
+ /* else silently truncated */
+ c = getChar();
+ } while (isdigit(c));
+ if (c != '.')
+ ungetChar();
+
+ /* ...but keep going to see whether it's really a float */
+ }
+
+ if (c == '.') {
+ c = getChar();
+ if (isdigit(c)) {
+ ttype = CSS_TK_FLOAT;
+ if (i < maxStrLen - 1)
+ tval[i++] = '.';
+ do {
+ if (i < maxStrLen - 1)
+ tval[i++] = c;
+ /* else silently truncated */
+ c = getChar();
+ } while (isdigit(c));
+
+ ungetChar();
+ tval[i] = 0;
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token number %s\n", tval);
+ return;
+ } else {
+ ungetChar();
+ if (ttype == CSS_TK_DECINT) {
+ ungetChar();
+ } else {
+ c = '.';
+ }
+ }
+ }
+
+ if (ttype == CSS_TK_DECINT) {
+ tval[i] = 0;
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token number %s\n", tval);
+ return;
+ }
+
+ if (i) {
+ ungetChar(); /* ungetChar '-' */
+ i--;
+ c = getChar();
+ }
+
+ if (isalpha(c) || c == '_' || c == '-') {
+ ttype = CSS_TK_SYMBOL;
+
+ tval[0] = c;
+ i = 1;
+ c = getChar();
+ while (isalnum(c) || c == '_' || c == '-') {
+ if (i < maxStrLen - 1) {
+ tval[i] = c;
+ i++;
+ } /* else silently truncated */
+ c = getChar();
+ }
+ tval[i] = 0;
+ ungetChar();
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token symbol '%s'\n", tval);
+ return;
+ }
+
+ if (c == '"' || c == '\'') {
+ c1 = c;
+ ttype = CSS_TK_STRING;
+
+ i = 0;
+ c = getChar();
+
+ while (c != EOF && c != c1) {
+ if (c == '\\') {
+ d = getChar();
+ if (isxdigit(d)) {
+ /* Read hex Unicode char. (Actually, strings are yet only 8
+ * bit.) */
+ hexbuf[0] = d;
+ j = 1;
+ d = getChar();
+ while (j < 4 && isxdigit(d)) {
+ hexbuf[j] = d;
+ j++;
+ d = getChar();
+ }
+ hexbuf[j] = 0;
+ ungetChar();
+ c = strtol(hexbuf, NULL, 16);
+ } else {
+ /* Take character literally. */
+ c = d;
+ }
+ }
+
+ if (i < maxStrLen - 1) {
+ tval[i] = c;
+ i++;
+ } /* else silently truncated */
+ c = getChar();
+ }
+ tval[i] = 0;
+ /* No ungetChar(). */
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token string '%s'\n", tval);
+ return;
+ }
+
+ /*
+ * Within blocks, '#' starts a color, outside, it is used in selectors.
+ */
+ if (c == '#' && withinBlock) {
+ ttype = CSS_TK_COLOR;
+
+ tval[0] = c;
+ i = 1;
+ c = getChar();
+ while (isxdigit(c)) {
+ if (i < maxStrLen - 1) {
+ tval[i] = c;
+ i++;
+ } /* else silently truncated */
+ c = getChar();
+ }
+ tval[i] = 0;
+ ungetChar();
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token color '%s'\n", tval);
+ return;
+ }
+
+ if (c == EOF) {
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token %s\n", "EOF");
+ ttype = CSS_TK_END;
+ return;
+ }
+
+ ttype = CSS_TK_CHAR;
+ tval[0] = c;
+ tval[1] = 0;
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token char '%c'\n", c);
+}
+
+
+bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type)
+{
+ int i, err = 1;
+ CssValueType savedType = *type;
+
+ for (int j = 0; Css_property_info[prop].type[j] != CSS_TYPE_UNUSED; j++) {
+ *type = Css_property_info[prop].type[j];
+
+ switch (Css_property_info[prop].type[j]) {
+
+ case CSS_TYPE_ENUM:
+ if (ttype == CSS_TK_SYMBOL) {
+ for (i = 0; Css_property_info[prop].enum_symbols[i]; i++)
+ if (dStrcasecmp(tval,
+ Css_property_info[prop].enum_symbols[i]) == 0)
+ return true;
+ }
+ break;
+
+ case CSS_TYPE_MULTI_ENUM:
+ if (ttype == CSS_TK_SYMBOL) {
+ if (dStrcasecmp(tval, "none") == 0)
+ return true;
+ else {
+ for (i = 0; Css_property_info[prop].enum_symbols[i]; i++) {
+ if (dStrcasecmp(tval,
+ Css_property_info[prop].enum_symbols[i]) == 0)
+ return true;
+ }
+ }
+ }
+ break;
+
+ case CSS_TYPE_LENGTH_PERCENTAGE:
+ case CSS_TYPE_LENGTH_PERCENTAGE_NUMBER:
+ case CSS_TYPE_LENGTH:
+ if (tval[0] == '-')
+ return false;
+ // Fall Through
+ case CSS_TYPE_SIGNED_LENGTH:
+ if (ttype == CSS_TK_DECINT ||
+ ttype == CSS_TK_FLOAT ||
+ (ttype == CSS_TK_SYMBOL && dStrcasecmp(tval, "auto") == 0))
+ return true;
+ break;
+
+ case CSS_TYPE_COLOR:
+ if ((ttype == CSS_TK_COLOR ||
+ ttype == CSS_TK_SYMBOL) &&
+ (dStrcasecmp(tval, "rgb") == 0 ||
+ a_Color_parse(tval, -1, &err) != -1))
+ return true;
+ break;
+
+ case CSS_TYPE_STRING:
+ if (ttype == CSS_TK_STRING)
+ return true;
+ break;
+
+ case CSS_TYPE_SYMBOL:
+ if (ttype == CSS_TK_SYMBOL ||
+ ttype == CSS_TK_STRING)
+ return true;
+ break;
+
+ case CSS_TYPE_FONT_WEIGHT:
+ if (ttype == CSS_TK_DECINT) {
+ i = strtol(tval, NULL, 10);
+ if (i >= 100 && i <= 900)
+ return true;
+ }
+ break;
+
+ case CSS_TYPE_UNUSED:
+ case CSS_TYPE_INTEGER:
+ /* Not used for parser values. */
+ default:
+ assert(false);
+ break;
+ }
+ }
+
+ *type = savedType;
+ return false;
+}
+
+bool CssParser::parseRgbColorComponent(int32_t *cc, int *percentage) {
+ if (ttype != CSS_TK_DECINT) {
+ MSG_CSS("expected integer not found in %s color\n", "rgb");
+ return false;
+ }
+
+ *cc = strtol(tval, NULL, 10);
+
+ nextToken();
+ if (ttype == CSS_TK_CHAR && tval[0] == '%') {
+ if (*percentage == 0) {
+ MSG_CSS("'%s' unexpected in rgb color\n", "%");
+ return false;
+ }
+ *percentage = 1;
+ *cc = *cc * 255 / 100;
+ nextToken();
+ } else {
+ if (*percentage == 1) {
+ MSG_CSS("expected '%s' not found in rgb color\n", "%");
+ return false;
+ }
+ *percentage = 0;
+ }
+
+ if (*cc > 255)
+ *cc = 255;
+ if (*cc < 0)
+ *cc = 0;
+
+ return true;
+}
+
+bool CssParser::parseRgbColor(int32_t *c) {
+ int32_t cc;
+ int percentage = -1;
+
+ *c = 0;
+
+ if (ttype != CSS_TK_CHAR || tval[0] != '(') {
+ MSG_CSS("expected '%s' not found in rgb color\n", "(");
+ return false;
+ }
+ nextToken();
+
+ if (!parseRgbColorComponent(&cc, &percentage))
+ return false;
+ *c |= cc << 16;
+
+ if (ttype != CSS_TK_CHAR || tval[0] != ',') {
+ MSG_CSS("expected '%s' not found in rgb color\n", ",");
+ return false;
+ }
+ nextToken();
+
+ if (!parseRgbColorComponent(&cc, &percentage))
+ return false;
+ *c |= cc << 8;
+
+ if (ttype != CSS_TK_CHAR || tval[0] != ',') {
+ MSG_CSS("expected '%s' not found in rgb color\n", ",");
+ return false;
+ }
+ nextToken();
+
+ if (!parseRgbColorComponent(&cc, &percentage))
+ return false;
+ *c |= cc;
+
+ if (ttype != CSS_TK_CHAR || tval[0] != ')') {
+ MSG_CSS("expected '%s' not found in rgb color\n", ")");
+ return false;
+ }
+
+ return true;
+}
+
+bool CssParser::parseValue(CssPropertyName prop,
+ CssValueType type,
+ CssPropertyValue * val)
+{
+ CssLengthType lentype;
+ bool found, ret = false;
+ float fval;
+ int i, ival, err = 1;
+ Dstr *dstr;
+
+ switch (type) {
+ case CSS_TYPE_ENUM:
+ if (ttype == CSS_TK_SYMBOL) {
+ for (i = 0; Css_property_info[prop].enum_symbols[i]; i++)
+ if (dStrcasecmp(tval,
+ Css_property_info[prop].enum_symbols[i]) == 0) {
+ val->intVal = i;
+ ret = true;
+ break;
+ }
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_MULTI_ENUM:
+ val->intVal = 0;
+ ret = true;
+
+ while (ttype == CSS_TK_SYMBOL) {
+ if (dStrcasecmp(tval, "none") != 0) {
+ for (i = 0, found = false;
+ !found && Css_property_info[prop].enum_symbols[i]; i++) {
+ if (dStrcasecmp(tval,
+ Css_property_info[prop].enum_symbols[i]) == 0)
+ val->intVal |= (1 << i);
+ }
+ }
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_LENGTH_PERCENTAGE:
+ case CSS_TYPE_LENGTH_PERCENTAGE_NUMBER:
+ case CSS_TYPE_LENGTH:
+ case CSS_TYPE_SIGNED_LENGTH:
+ if (ttype == CSS_TK_DECINT || ttype == CSS_TK_FLOAT) {
+ fval = atof(tval);
+ lentype = CSS_LENGTH_TYPE_NONE;
+
+ nextToken();
+ if (!spaceSeparated && ttype == CSS_TK_SYMBOL) {
+ ret = true;
+
+ if (dStrcasecmp(tval, "px") == 0) {
+ lentype = CSS_LENGTH_TYPE_PX;
+ nextToken();
+ } else if (dStrcasecmp(tval, "mm") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ nextToken();
+ } else if (dStrcasecmp(tval, "cm") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ fval *= 10;
+ nextToken();
+ } else if (dStrcasecmp(tval, "in") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ fval *= 25.4;
+ nextToken();
+ } else if (dStrcasecmp(tval, "pt") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ fval *= (25.4 / 72);
+ nextToken();
+ } else if (dStrcasecmp(tval, "pc") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ fval *= (25.4 / 6);
+ nextToken();
+ } else if (dStrcasecmp(tval, "em") == 0) {
+ lentype = CSS_LENGTH_TYPE_EM;
+ nextToken();
+ } else if (dStrcasecmp(tval, "ex") == 0) {
+ lentype = CSS_LENGTH_TYPE_EX;
+ nextToken();
+ } else {
+ ret = false;
+ }
+ } else if (!spaceSeparated &&
+ (type == CSS_TYPE_LENGTH_PERCENTAGE ||
+ type == CSS_TYPE_LENGTH_PERCENTAGE_NUMBER) &&
+ ttype == CSS_TK_CHAR &&
+ tval[0] == '%') {
+ fval /= 100;
+ lentype = CSS_LENGTH_TYPE_PERCENTAGE;
+ ret = true;
+ nextToken();
+ }
+
+ /* Allow numbers without unit only for 0 or
+ * CSS_TYPE_LENGTH_PERCENTAGE_NUMBER
+ */
+ if (lentype == CSS_LENGTH_TYPE_NONE &&
+ (type == CSS_TYPE_LENGTH_PERCENTAGE_NUMBER || fval == 0.0))
+ ret = true;
+
+ val->intVal = CSS_CREATE_LENGTH(fval, lentype);
+ } else if (ttype == CSS_TK_SYMBOL && dStrcasecmp(tval, "auto") == 0) {
+ ret = true;
+ val->intVal = CSS_LENGTH_TYPE_AUTO;
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_COLOR:
+ if (ttype == CSS_TK_COLOR) {
+ val->intVal = a_Color_parse(tval, -1, &err);
+ if (err)
+ MSG_CSS("color is not in \"%s\" format\n", "#RRGGBB");
+ else
+ ret = true;
+ nextToken();
+ } else if (ttype == CSS_TK_SYMBOL) {
+ if (dStrcasecmp(tval, "rgb") == 0) {
+ nextToken();
+ if (parseRgbColor(&val->intVal))
+ ret = true;
+ else
+ MSG_CSS("Failed to parse %s color\n", "rgb(r,g,b)");
+ } else {
+ val->intVal = a_Color_parse(tval, -1, &err);
+ if (err)
+ MSG_CSS("color is not in \"%s\" format\n", "#RRGGBB");
+ else
+ ret = true;
+ }
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_STRING:
+ if (ttype == CSS_TK_STRING) {
+ val->strVal = dStrdup(tval);
+ ret = true;
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_SYMBOL:
+ /* Read comma separated list of font family names */
+ dstr = dStr_new("");
+ while (ttype == CSS_TK_SYMBOL || ttype == CSS_TK_STRING ||
+ (ttype == CSS_TK_CHAR && tval[0] == ',')) {
+ if (spaceSeparated)
+ dStr_append_c(dstr, ' ');
+ dStr_append(dstr, tval);
+ ret = true;
+ nextToken();
+ }
+
+ if (ret) {
+ val->strVal = dStrstrip(dstr->str);
+ dStr_free(dstr, 0);
+ } else {
+ dStr_free(dstr, 1);
+ }
+ break;
+
+ case CSS_TYPE_FONT_WEIGHT:
+ ival = 0;
+ if (ttype == CSS_TK_DECINT) {
+ ival = strtol(tval, NULL, 10);
+ if (ival < 100 || ival > 900)
+ /* invalid */
+ ival = 0;
+ }
+
+ if (ival != 0) {
+ val->intVal = ival;
+ ret = true;
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_UNUSED:
+ /* nothing */
+ break;
+
+ case CSS_TYPE_INTEGER:
+ /* Not used for parser values. */
+ default:
+ assert(false); /* not reached */
+ }
+
+ return ret;
+}
+
+bool CssParser::parseWeight()
+{
+ if (ttype == CSS_TK_CHAR && tval[0] == '!') {
+ nextToken();
+ if (ttype == CSS_TK_SYMBOL &&
+ dStrcasecmp(tval, "important") == 0) {
+ nextToken();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * bsearch(3) compare function for searching properties
+ */
+static int Css_property_info_cmp(const void *a, const void *b)
+{
+ return dStrcasecmp(((CssPropertyInfo *) a)->symbol,
+ ((CssPropertyInfo *) b)->symbol);
+}
+
+
+/*
+ * bsearch(3) compare function for searching shorthands
+ */
+static int Css_shorthand_info_cmp(const void *a, const void *b)
+{
+ return dStrcasecmp(((CssShorthandInfo *) a)->symbol,
+ ((CssShorthandInfo *) b)->symbol);
+}
+
+void CssParser::parseDeclaration(CssPropertyList * props,
+ CssPropertyList * importantProps)
+{
+ CssPropertyInfo pi = {NULL, {CSS_TYPE_UNUSED}, NULL}, *pip;
+ CssShorthandInfo si, *sip;
+ CssValueType type = CSS_TYPE_UNUSED;
+
+ CssPropertyName prop;
+ CssPropertyValue val, dir_vals[4];
+ CssValueType dir_types[4];
+ bool found, weight;
+ int sh_index, i, j, n;
+ int dir_set[4][4] = {
+ /* 1 value */ {0, 0, 0, 0},
+ /* 2 values */ {0, 0, 1, 1},
+ /* 3 values */ {0, 2, 1, 1},
+ /* 4 values */ {0, 2, 3, 1}
+ };
+
+ if (ttype == CSS_TK_SYMBOL) {
+ pi.symbol = tval;
+ pip =
+ (CssPropertyInfo *) bsearch(&pi, Css_property_info,
+ CSS_NUM_PARSED_PROPERTIES,
+ sizeof(CssPropertyInfo),
+ Css_property_info_cmp);
+ if (pip) {
+ prop = (CssPropertyName) (pip - Css_property_info);
+ nextToken();
+ if (ttype == CSS_TK_CHAR && tval[0] == ':') {
+ nextToken();
+ if (tokenMatchesProperty (prop, &type) &&
+ parseValue(prop, type, &val)) {
+ weight = parseWeight();
+ if (weight && importantProps)
+ importantProps->set(prop, type, val);
+ else
+ props->set(prop, type, val);
+ }
+ }
+ } else {
+ /* Try shorthands. */
+ si.symbol = tval;
+ sip =
+ (CssShorthandInfo *) bsearch(&pi, Css_shorthand_info,
+ CSS_SHORTHAND_NUM,
+ sizeof(CssShorthandInfo),
+ Css_shorthand_info_cmp);
+ if (sip) {
+ sh_index = sip - Css_shorthand_info;
+ nextToken();
+ if (ttype == CSS_TK_CHAR && tval[0] == ':') {
+ nextToken();
+
+ switch (Css_shorthand_info[sh_index].type) {
+
+ case CssShorthandInfo::CSS_SHORTHAND_FONT:
+ /* \todo Implement details. */
+ case CssShorthandInfo::CSS_SHORTHAND_MULTIPLE:
+ do {
+ for (found = false, i = 0;
+ !found &&
+ Css_shorthand_info[sh_index].properties[i] != -1;
+ i++)
+ if (tokenMatchesProperty(Css_shorthand_info[sh_index].
+ properties[i], &type)) {
+ found = true;
+ DEBUG_MSG(DEBUG_PARSE_LEVEL,
+ "will assign to '%s'\n",
+ Css_property_info
+ [Css_shorthand_info[sh_index]
+ .properties[i]].symbol);
+ if (parseValue(Css_shorthand_info[sh_index]
+ .properties[i], type, &val)) {
+ weight = parseWeight();
+ if (weight && importantProps)
+ importantProps->
+ set(Css_shorthand_info[sh_index].
+ properties[i], type, val);
+ else
+ props->set(Css_shorthand_info[sh_index].
+ properties[i], type, val);
+ }
+ }
+ } while (found);
+ break;
+
+ case CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS:
+ n = 0;
+ while (n < 4) {
+ if (tokenMatchesProperty(Css_shorthand_info[sh_index].
+ properties[0], &type) &&
+ parseValue(Css_shorthand_info[sh_index]
+ .properties[0], type, &val)) {
+ dir_vals[n] = val;
+ dir_types[n] = type;
+ n++;
+ } else
+ break;
+ }
+
+ weight = parseWeight();
+ if (n > 0) {
+ for (i = 0; i < 4; i++)
+ if (weight && importantProps)
+ importantProps->set(Css_shorthand_info[sh_index]
+ .properties[i],
+ dir_types[dir_set[n - 1][i]],
+ dir_vals[dir_set[n - 1][i]]);
+ else
+ props->set(Css_shorthand_info[sh_index]
+ .properties[i],
+ dir_types[dir_set[n - 1][i]],
+ dir_vals[dir_set[n - 1][i]]);
+ } else
+ MSG_CSS("no values for shorthand property '%s'\n",
+ Css_shorthand_info[sh_index].symbol);
+
+ break;
+
+ case CssShorthandInfo::CSS_SHORTHAND_BORDER:
+ do {
+ for (found = false, i = 0;
+ !found && i < 3;
+ i++)
+ if (tokenMatchesProperty(Css_shorthand_info[sh_index].
+ properties[i], &type)) {
+ found = true;
+ if (parseValue(Css_shorthand_info[sh_index]
+ .properties[i], type, &val)) {
+ weight = parseWeight();
+ for (j = 0; j < 4; j++)
+ if (weight && importantProps)
+ importantProps->
+ set(Css_shorthand_info[sh_index].
+ properties[j * 3 + i], type, val);
+ else
+ props->set(Css_shorthand_info[sh_index].
+ properties[j * 3 + i], type, val);
+ }
+ }
+ } while (found);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Skip all tokens until the expected end. */
+ while (!(ttype == CSS_TK_END ||
+ (ttype == CSS_TK_CHAR &&
+ (tval[0] == ';' || tval[0] == '}'))))
+ nextToken();
+
+ if (ttype == CSS_TK_CHAR && tval[0] == ';')
+ nextToken();
+}
+
+bool CssParser::parseSimpleSelector(CssSimpleSelector *selector)
+{
+ CssSimpleSelector::SelectType selectType;
+
+ if (ttype == CSS_TK_SYMBOL) {
+ selector->setElement (a_Html_tag_index(tval));
+ nextToken();
+ if (spaceSeparated)
+ return true;
+ } else if (ttype == CSS_TK_CHAR && tval[0] == '*') {
+ selector->setElement (CssSimpleSelector::ELEMENT_ANY);
+ nextToken();
+ if (spaceSeparated)
+ return true;
+ } else if (ttype == CSS_TK_CHAR &&
+ (tval[0] == '#' ||
+ tval[0] == '.' ||
+ tval[0] == ':')) {
+ // nothing to be done in this case
+ } else {
+ return false;
+ }
+
+ do {
+ selectType = CssSimpleSelector::SELECT_NONE;
+ if (ttype == CSS_TK_CHAR) {
+ switch (tval[0]) {
+ case '#':
+ selectType = CssSimpleSelector::SELECT_ID;
+ break;
+ case '.':
+ selectType = CssSimpleSelector::SELECT_CLASS;
+ break;
+ case ':':
+ selectType = CssSimpleSelector::SELECT_PSEUDO_CLASS;
+ if (selector->getPseudoClass ())
+ // pseudo class has been set already.
+ // As dillo currently only supports :link and :visisted, a
+ // selector with more than one pseudo class will never match.
+ // By returning false, the whole CssRule will be dropped.
+ // \todo adapt this when supporting :hover, :active...
+ return false;
+ break;
+ }
+ }
+
+ if (selectType != CssSimpleSelector::SELECT_NONE) {
+ nextToken();
+ if (spaceSeparated)
+ return true;
+
+ if (ttype == CSS_TK_SYMBOL) {
+ selector->setSelect (selectType, tval);
+ nextToken();
+ } else {
+ return false; // don't accept classes or id's starting with integer
+ }
+ if (spaceSeparated)
+ return true;
+ }
+ } while (selectType != CssSimpleSelector::SELECT_NONE);
+
+ DEBUG_MSG(DEBUG_PARSE_LEVEL, "end of simple selector (%s, %s, %s, %d)\n",
+ selector->id, selector->klass,
+ selector->pseudo, selector->element);
+
+ return true;
+}
+
+CssSelector *CssParser::parseSelector()
+{
+ CssSelector *selector = new CssSelector ();
+
+ while (true) {
+ if (! parseSimpleSelector (selector->top ())) {
+ delete selector;
+ selector = NULL;
+ break;
+ }
+
+ if (ttype == CSS_TK_CHAR &&
+ (tval[0] == ',' || tval[0] == '{')) {
+ break;
+ } else if (ttype == CSS_TK_CHAR && tval[0] == '>') {
+ selector->addSimpleSelector (CssSelector::CHILD);
+ nextToken();
+ } else if (ttype != CSS_TK_END && spaceSeparated) {
+ selector->addSimpleSelector (CssSelector::DESCENDANT);
+ } else {
+ delete selector;
+ selector = NULL;
+ break;
+ }
+ }
+
+ while (ttype != CSS_TK_END &&
+ (ttype != CSS_TK_CHAR ||
+ (tval[0] != ',' && tval[0] != '{')))
+ nextToken();
+
+ return selector;
+}
+
+void CssParser::parseRuleset()
+{
+ lout::misc::SimpleVector < CssSelector * >*list;
+ CssPropertyList *props, *importantProps;
+ CssSelector *selector;
+
+ list = new lout::misc::SimpleVector < CssSelector * >(1);
+
+ while (true) {
+ selector = parseSelector();
+
+ if (selector) {
+ selector->ref();
+ list->increase();
+ list->set(list->size() - 1, selector);
+ }
+
+ // \todo dump whole ruleset in case of parse error as required by CSS 2.1
+ // however make sure we don't dump it if only dillo fails to parse
+ // valid CSS.
+
+ if (ttype == CSS_TK_CHAR && tval[0] == ',')
+ /* To read the next token. */
+ nextToken();
+ else
+ /* No more selectors. */
+ break;
+ }
+
+ DEBUG_MSG(DEBUG_PARSE_LEVEL, "end of %s\n", "selectors");
+
+ props = new CssPropertyList(true);
+ props->ref();
+ importantProps = new CssPropertyList(true);
+ importantProps->ref();
+
+ /* Read block. ('{' has already been read.) */
+ if (ttype != CSS_TK_END) {
+ withinBlock = true;
+ nextToken();
+ do
+ parseDeclaration(props, importantProps);
+ while (!(ttype == CSS_TK_END ||
+ (ttype == CSS_TK_CHAR && tval[0] == '}')));
+ withinBlock = false;
+ }
+
+ for (int i = 0; i < list->size(); i++) {
+ CssSelector *s = list->get(i);
+
+ if (origin == CSS_ORIGIN_USER_AGENT) {
+ context->addRule(s, props, CSS_PRIMARY_USER_AGENT);
+ } else if (origin == CSS_ORIGIN_USER) {
+ context->addRule(s, props, CSS_PRIMARY_USER);
+ context->addRule(s, importantProps, CSS_PRIMARY_USER_IMPORTANT);
+ } else if (origin == CSS_ORIGIN_AUTHOR) {
+ context->addRule(s, props, CSS_PRIMARY_AUTHOR);
+ context->addRule(s, importantProps, CSS_PRIMARY_AUTHOR_IMPORTANT);
+ }
+
+ s->unref();
+ }
+
+ props->unref();
+ importantProps->unref();
+
+ delete list;
+
+ if (ttype == CSS_TK_CHAR && tval[0] == '}')
+ nextToken();
+}
+
+char * CssParser::parseUrl()
+{
+ Dstr *urlStr = NULL;
+
+ if (ttype != CSS_TK_SYMBOL ||
+ dStrcasecmp(tval, "url") != 0)
+ return NULL;
+
+ nextToken();
+
+ if (ttype != CSS_TK_CHAR || tval[0] != '(')
+ return NULL;
+
+ nextToken();
+
+ if (ttype == CSS_TK_STRING) {
+ urlStr = dStr_new(tval);
+ nextToken();
+ } else {
+ urlStr = dStr_new("");
+ while (ttype != CSS_TK_END &&
+ (ttype != CSS_TK_CHAR || tval[0] != ')')) {
+ dStr_append(urlStr, tval);
+ nextToken();
+ }
+ }
+
+ if (ttype != CSS_TK_CHAR || tval[0] != ')') {
+ dStr_free(urlStr, 1);
+ urlStr = NULL;
+ }
+
+ if (urlStr) {
+ char *url = urlStr->str;
+ dStr_free(urlStr, 0);
+ return url;
+ } else {
+ return NULL;
+ }
+}
+
+void CssParser::parseImport(DilloHtml *html, DilloUrl *baseUrl)
+{
+ char *urlStr = NULL;
+
+ if (html != NULL &&
+ ttype == CSS_TK_SYMBOL &&
+ dStrcasecmp(tval, "import") == 0) {
+ nextToken();
+
+ if (ttype == CSS_TK_SYMBOL &&
+ dStrcasecmp(tval, "url") == 0)
+ urlStr = parseUrl();
+ else if (ttype == CSS_TK_STRING)
+ urlStr = dStrdup (tval);
+
+ /* Skip all tokens until the expected end. */
+ while (!(ttype == CSS_TK_END ||
+ (ttype == CSS_TK_CHAR && (tval[0] == ';'))))
+ nextToken();
+
+ nextToken();
+
+ if (urlStr) {
+ MSG("CssParser::parseImport(): @import %s\n", urlStr);
+ DilloUrl *url = a_Html_url_new (html, urlStr, a_Url_str(baseUrl),
+ baseUrl ? 1 : 0);
+ a_Html_load_stylesheet(html, url);
+ a_Url_free(url);
+ dFree (urlStr);
+ }
+ }
+}
+
+const char * CssParser::propertyNameString(CssPropertyName name)
+{
+ return Css_property_info[name].symbol;
+}
+
+void CssParser::parse(DilloHtml *html, DilloUrl *url, CssContext * context,
+ const char *buf,
+ int buflen, CssOrigin origin)
+{
+ CssParser parser (context, origin, buf, buflen);
+
+ while (parser.ttype == CSS_TK_CHAR && parser.tval[0] == '@') {
+ parser.nextToken();
+ parser.parseImport(html, url);
+ }
+
+ while (parser.ttype != CSS_TK_END)
+ parser.parseRuleset();
+}
+
+CssPropertyList *CssParser::parseDeclarationBlock(const char *buf, int buflen)
+{
+ CssPropertyList *props = new CssPropertyList (true);
+ CssParser parser (NULL, CSS_ORIGIN_AUTHOR, buf, buflen);
+
+ parser.withinBlock = true;
+
+ do
+ parser.parseDeclaration(props, NULL);
+ while (!(parser.ttype == CSS_TK_END ||
+ (parser.ttype == CSS_TK_CHAR && parser.tval[0] == '}')));
+
+ if (props->size () == 0) {
+ delete props;
+ props = NULL;
+ }
+
+ return props;
+}