diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 7 | ||||
-rw-r--r-- | src/css.cc | 228 | ||||
-rw-r--r-- | src/css.hh | 269 | ||||
-rw-r--r-- | src/cssparser.cc | 1116 | ||||
-rw-r--r-- | src/cssparser.hh | 73 | ||||
-rw-r--r-- | src/doctree.hh | 20 | ||||
-rw-r--r-- | src/form.cc | 18 | ||||
-rw-r--r-- | src/html.cc | 441 | ||||
-rw-r--r-- | src/html_common.hh | 16 | ||||
-rw-r--r-- | src/styleengine.cc | 348 | ||||
-rw-r--r-- | src/styleengine.hh | 57 | ||||
-rw-r--r-- | src/table.cc | 206 |
12 files changed, 2457 insertions, 342 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index d4d03eea..7629aaef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -65,6 +65,13 @@ dillo_SOURCES = \ dicache.h \ capi.c \ capi.h \ + css.cc \ + css.hh \ + cssparser.cc \ + cssparser.hh \ + doctree.hh \ + styleengine.cc \ + styleengine.hh \ plain.cc \ html.cc \ html.hh \ diff --git a/src/css.cc b/src/css.cc new file mode 100644 index 00000000..8ecad167 --- /dev/null +++ b/src/css.cc @@ -0,0 +1,228 @@ +/* + * File: css.cc + * + * Copyright 2008 Jorge Arellano Cid <jcid@dillo.org> + * + * 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. + */ + +#include <stdio.h> +#include <math.h> +#include "../dlib/dlib.h" +#include "prefs.h" +#include "html_common.hh" +#include "css.hh" +#include "cssparser.hh" + +using namespace dw::core::style; + +void CssProperty::print () { + fprintf (stderr, "%s - %d\n", Css_property_info[name].symbol, value.intVal); +} + +void CssPropertyList::set (CssProperty::Name name, CssProperty::Value value) { + for (int i = 0; i < size (); i++) + if (getRef (i)->name == name) { + getRef (i)->value = value; + return; + } + + increase (); + getRef (size () - 1)->name = name; + getRef (size () - 1)->value = value; +} + +void CssPropertyList::apply (CssPropertyList *props) { + for (int i = 0; i < size (); i++) + props->set (getRef (i)->name, getRef (i)->value); +} + +void CssPropertyList::print () { + for (int i = 0; i < size (); i++) + getRef (i)->print (); +} + +CssSelector::CssSelector (int element, const char *klass, + const char *pseudo, const char *id) { + refCount = 0; + this->element = element; + this->klass = klass; + this->pseudo = pseudo; + this->id = id; +}; + +/** \todo implement all selection option CSS offers */ +bool CssSelector::match (Doctree *docTree) { + const DoctreeNode *n = docTree-> top (); + + if (element >= 0 && element != n->element) + return false; + if (klass != NULL && + (n->klass == NULL || strcasecmp (klass, n->klass) != 0)) + return false; + if (pseudo != NULL && + (n->pseudo == NULL || strcasecmp (pseudo, n->pseudo) != 0)) + return false; + if (id != NULL && (n->id == NULL || strcasecmp (id, n->id) != 0)) + return false; + + return true; +} + +void CssSelector::print () { + fprintf (stderr, "Element %d, class %s, pseudo %s, id %s\n", + element, klass, pseudo, id); +} + +CssRule::CssRule (CssSelector *selector, CssPropertyList *props) { + this->selector = selector; + this->selector->ref (); + this->props = props; + this->props->ref (); +}; + +CssRule::~CssRule () { + this->selector->unref (); + this->props->unref (); +}; + +void CssRule::apply (CssPropertyList *props, Doctree *docTree) { + if (selector->match (docTree)) + this->props->apply (props); +} + +void CssRule::print () { + selector->print (); + props->print (); +} + +void CssStyleSheet::addRule (CssRule *rule) { + increase (); + set (size () - 1, rule); +} + +void CssStyleSheet::addRule (CssSelector *selector, CssPropertyList *props) { + CssRule *rule = new CssRule (selector, props); + addRule (rule); +} + +void CssStyleSheet::apply (CssPropertyList *props, Doctree *docTree) { + for (int i = 0; i < size (); i++) + get (i)->apply (props, docTree); +} + +CssStyleSheet::~CssStyleSheet () { + for (int i = 0; i < size (); i++) + delete get (i); +} + +CssStyleSheet *CssContext::userAgentStyle; +CssStyleSheet *CssContext::userStyle; +CssStyleSheet *CssContext::userImportantStyle; + +CssContext::CssContext () { + for (int o = CSS_PRIMARY_USER_AGENT; o < CSS_PRIMARY_LAST; o++) + sheet[o] = NULL; + + if (userAgentStyle == NULL) { + userAgentStyle = new CssStyleSheet (); + userStyle = new CssStyleSheet (); + userImportantStyle = new CssStyleSheet (); + + sheet[CSS_PRIMARY_USER_AGENT] = userAgentStyle; + sheet[CSS_PRIMARY_USER] = userStyle; + sheet[CSS_PRIMARY_USER_IMPORTANT] = userImportantStyle; + + buildUserAgentStyle (); + buildUserStyle (); + } + + sheet[CSS_PRIMARY_USER_AGENT] = userAgentStyle; + sheet[CSS_PRIMARY_USER] = userStyle; + sheet[CSS_PRIMARY_USER_IMPORTANT] = userImportantStyle; +} + +CssContext::~CssContext () { + for (int o = CSS_PRIMARY_USER_AGENT; o < CSS_PRIMARY_LAST; o++) + if (sheet[o] != userAgentStyle && sheet[o] != userStyle && + sheet[o] != userImportantStyle) + delete sheet[o]; +} + +void CssContext::apply (CssPropertyList *props, Doctree *docTree, + CssPropertyList *tagStyle, CssPropertyList *nonCssHints) { + + for (int o = CSS_PRIMARY_USER_AGENT; o <= CSS_PRIMARY_USER; o++) + if (sheet[o]) + sheet[o]->apply (props, docTree); + + if (nonCssHints) + nonCssHints->apply (props); + + for (int o = CSS_PRIMARY_AUTHOR; o <= CSS_PRIMARY_USER_IMPORTANT; o++) + if (sheet[o]) + sheet[o]->apply (props, docTree); + + if (tagStyle) + tagStyle->apply (props); +} + +void CssContext::addRule (CssRule *rule, CssPrimaryOrder order) { + if (sheet[order] == NULL) + sheet[order] = new CssStyleSheet (); + + sheet[order]->addRule (rule); + +// fprintf(stderr, "Adding Rule (%d)\n", order); +// rule->print (); +} + +void CssContext::buildUserAgentStyle () { + const char *cssBuf = + "body {background-color: #dcd1ba; font-family: helvetica; color: black;" + " margin: 5px; }" + "big { font-size: 1.17em }" + ":link {color: blue; text-decoration: underline; cursor: pointer; } " + ":visited {color: green; text-decoration: underline; cursor: pointer; } " + "h1, h2, h3, h4, h5, h6, b, strong {font-weight: bolder; } " + "i, em, cite {font-style: italic; } " + "h1 {font-size: 2em; margin-top: .67em; margin-bottom: 0em;} " + "h2 {font-size: 1.5em; margin-top: .75em; margin-bottom: 0em;} " + "h3 {font-size: 1.17em; margin-top: .83em; margin-bottom: 0em;} " + "h4 {margin-top: 1.12em; margin-bottom: 0em;} " + "h5 {font-size: 0.83em; margin-top: 1.5em; margin-bottom: 0em;} " + "h6 {font-size: 0.75em; margin-top: 1.67em; margin-bottom: 0em;} " + "small, sub, sup { font-size: 0.83em } " + "sub { vertical-align: sub } " + "sup { vertical-align: super } " + "s, strike, del { text-decoration: line-through }" + "table {border-top-style: outset; border-spacing: 1px} " + "td {border-top-style: inset; padding: 2px;} " + "thead, tbody, tfoot { vertical-align: middle }" + "tt, pre {font-family: courier;} "; + + a_Css_parse (this, cssBuf, strlen (cssBuf), 0, CSS_ORIGIN_USER_AGENT); +} + +void CssContext::buildUserStyle () { + char buf[1024]; + char *filename; + + filename = dStrconcat(dGethomedir(), "/.dillo/style.css", NULL); + FILE *fp = fopen (filename, "r"); + if (fp) { + Dstr *style = dStr_sized_new (1024); + size_t len; + + while ((len = fread (buf, 1, sizeof (buf), fp))) + dStr_append_l (style, buf, len); + + a_Css_parse (this, style->str, style->len, 0, CSS_ORIGIN_USER); + dStr_free (style, 1); + } + + dFree (filename); +} diff --git a/src/css.hh b/src/css.hh new file mode 100644 index 00000000..552fd457 --- /dev/null +++ b/src/css.hh @@ -0,0 +1,269 @@ +#ifndef __CSS_HH__ +#define __CSS_HH__ + +#include "dw/core.hh" +#include "doctree.hh" + +/* Origin and weight. Used only internally.*/ +typedef enum { + CSS_PRIMARY_USER_AGENT, + CSS_PRIMARY_USER, + CSS_PRIMARY_AUTHOR, + CSS_PRIMARY_AUTHOR_IMPORTANT, + CSS_PRIMARY_USER_IMPORTANT, + CSS_PRIMARY_LAST, +} CssPrimaryOrder; + +/* + * Lengths are represented as int in the following way: + * + * +---+ - - - +---+---+- - - - - -+---+---+---+---+ + * | integer part | decimal fraction | type | + * +---+ - - - +---+---+- - - - - -+---+---+---+---+ + * n-1 19 18 3 2 1 0 + * + * | <------ fixed point value ------> | + * + * where type is one of the CSS_LENGTH_TYPE_* values. + */ + +typedef int CssLength; + +enum { + CSS_LENGTH_TYPE_PX, + CSS_LENGTH_TYPE_MM, /* "cm", "in", "pt" and "pc" are converted into + millimeters. */ + CSS_LENGTH_TYPE_EM, + CSS_LENGTH_TYPE_EX, + CSS_LENGTH_TYPE_PERCENTAGE, + CSS_LENGTH_TYPE_AUTO /* This can be used as a simple value. */ +}; + +#define CSS_CREATE_LENGTH(v, t) ( ( (int)((v) * (1 << 19)) & ~7 ) | (t) ) +#define CSS_LENGTH_VALUE(l) ( ( (float)((l) & ~7) ) / (1 << 19) ) +#define CSS_LENGTH_TYPE(l) ((l) & 7) + +/** + * \brief This class holds a CSS property and value pair. + */ +class CssProperty { + public: + typedef union { + int intVal; + const char *strVal; + } Value; + + typedef enum { + CSS_PROPERTY_BACKGROUND_ATTACHMENT, + CSS_PROPERTY_BACKGROUND_COLOR, + CSS_PROPERTY_BACKGROUND_IMAGE, + CSS_PROPERTY_BACKGROUND_POSITION, + CSS_PROPERTY_BACKGROUND_REPEAT, + CSS_PROPERTY_BORDER_BOTTOM_COLOR, + CSS_PROPERTY_BORDER_BOTTOM_STYLE, + CSS_PROPERTY_BORDER_BOTTOM_WIDTH, + CSS_PROPERTY_BORDER_COLLAPSE, + CSS_PROPERTY_BORDER_LEFT_COLOR, + CSS_PROPERTY_BORDER_LEFT_STYLE, + CSS_PROPERTY_BORDER_LEFT_WIDTH, + CSS_PROPERTY_BORDER_RIGHT_COLOR, + CSS_PROPERTY_BORDER_RIGHT_STYLE, + CSS_PROPERTY_BORDER_RIGHT_WIDTH, + CSS_PROPERTY_BORDER_SPACING, + CSS_PROPERTY_BORDER_TOP_COLOR, + CSS_PROPERTY_BORDER_TOP_STYLE, + CSS_PROPERTY_BORDER_TOP_WIDTH, + CSS_PROPERTY_BOTTOM, + CSS_PROPERTY_CAPTION_SIDE, + CSS_PROPERTY_CLEAR, + CSS_PROPERTY_CLIP, + CSS_PROPERTY_COLOR, + CSS_PROPERTY_CONTENT, + CSS_PROPERTY_COUNTER_INCREMENT, + CSS_PROPERTY_COUNTER_RESET, + CSS_PROPERTY_CURSOR, + CSS_PROPERTY_DIRECTION, + CSS_PROPERTY_DISPLAY, + CSS_PROPERTY_EMPTY_CELLS, + CSS_PROPERTY_FLOAT, + CSS_PROPERTY_FONT_FAMILY, + CSS_PROPERTY_FONT_SIZE, + CSS_PROPERTY_FONT_SIZE_ADJUST, + CSS_PROPERTY_FONT_STRETCH, + CSS_PROPERTY_FONT_STYLE, + CSS_PROPERTY_FONT_VARIANT, + CSS_PROPERTY_FONT_WEIGHT, + CSS_PROPERTY_HEIGHT, + CSS_PROPERTY_LEFT, + CSS_PROPERTY_LETTER_SPACING, + CSS_PROPERTY_LINE_HEIGHT, + CSS_PROPERTY_LIST_STYLE_IMAGE, + CSS_PROPERTY_LIST_STYLE_POSITION, + CSS_PROPERTY_LIST_STYLE_TYPE, + CSS_PROPERTY_MARGIN_BOTTOM, + CSS_PROPERTY_MARGIN_LEFT, + CSS_PROPERTY_MARGIN_RIGHT, + CSS_PROPERTY_MARGIN_TOP, + CSS_PROPERTY_MARKER_OFFSET, + CSS_PROPERTY_MARKS, + CSS_PROPERTY_MAX_HEIGHT, + CSS_PROPERTY_MAX_WIDTH, + CSS_PROPERTY_MIN_HEIGHT, + CSS_PROPERTY_MIN_WIDTH, + CSS_PROPERTY_OUTLINE_COLOR, + CSS_PROPERTY_OUTLINE_STYLE, + CSS_PROPERTY_OUTLINE_WIDTH, + CSS_PROPERTY_OVERFLOW, + CSS_PROPERTY_PADDING_BOTTOM, + CSS_PROPERTY_PADDING_LEFT, + CSS_PROPERTY_PADDING_RIGHT, + CSS_PROPERTY_PADDING_TOP, + CSS_PROPERTY_POSITION, + CSS_PROPERTY_QUOTES, + CSS_PROPERTY_RIGHT, + CSS_PROPERTY_TEXT_ALIGN, + CSS_PROPERTY_TEXT_DECORATION, + CSS_PROPERTY_TEXT_SHADOW, + CSS_PROPERTY_TEXT_TRANSFORM, + CSS_PROPERTY_TOP, + CSS_PROPERTY_UNICODE_BIDI, + CSS_PROPERTY_VERTICAL_ALIGN, + CSS_PROPERTY_VISIBILITY, + CSS_PROPERTY_WHITE_SPACE, + CSS_PROPERTY_TEXT_INDENT, + CSS_PROPERTY_WIDTH, + CSS_PROPERTY_WORD_SPACING, + CSS_PROPERTY_Z_INDEX, + CSS_PROPERTY_X_LINK, + CSS_PROPERTY_X_COLSPAN, + CSS_PROPERTY_X_ROWSPAN, + PROPERTY_X_LINK, + PROPERTY_X_IMG, + PROPERTY_X_TOOLTIP, + CSS_PROPERTY_LAST + } Name; + + typedef enum { + CSS_FONT_WEIGHT_LIGHTER = -1, + CSS_FONT_WEIGHT_BOLDER = -2, + CSS_FONT_WEIGHT_STEP = 300, + /* Some special font weights. */ + CSS_FONT_WEIGHT_LIGHT = 100, + CSS_FONT_WEIGHT_NORMAL = 400, + CSS_FONT_WEIGHT_BOLD = 700, + CSS_FONT_WEIGHT_MIN = 100, + CSS_FONT_WEIGHT_MAX = 900, + } FontWeightExtensions; + + Name name; + Value value; + + void print (); +}; + +/** + * \brief A list of CssProperty objects. + */ +class CssPropertyList : public lout::misc::SimpleVector <CssProperty> { + int refCount; + + public: + CssPropertyList() : lout::misc::SimpleVector <CssProperty> (1) { + refCount = 0; + }; + CssPropertyList(const CssPropertyList &p) : + lout::misc::SimpleVector <CssProperty> (p) { + refCount = 0; + }; + + void set (CssProperty::Name name, CssProperty::Value value); + void set (CssProperty::Name name, const char *value) { + CssProperty::Value v; + v.strVal = value; + set (name, v); + }; + void set (CssProperty::Name name, int value) { + CssProperty::Value v; + v.intVal = value; + set (name, v); + }; + void apply (CssPropertyList *props); + void print (); + inline void ref () { refCount++; } + inline void unref () { if(--refCount == 0) delete this; } +}; + +/** + * \brief CSS selector class. + * \todo Implement missing selector options. + */ +class CssSelector { + private: + int refCount; + + public: + int element; + const char *klass, *pseudo, *id; + + CssSelector (int element = -1, const char *klass = NULL, + const char *pseudo = NULL, const char *id = NULL); + bool match (Doctree *dt); + void print (); + inline void ref () { refCount++; } + inline void unref () { if(--refCount == 0) delete this; } +}; + +/** + * \brief A CssSelector CssPropertyList pair. + * The CssPropertyList is applied if the CssSelector matches. + */ +class CssRule { + private: + CssSelector *selector; + CssPropertyList *props; + + public: + CssRule (CssSelector *selector, CssPropertyList *props); + ~CssRule (); + + void apply (CssPropertyList *props, Doctree *docTree); + void print (); +}; + +/** + * \brief A list of CssRules. + * In apply () all matching rules are applied. + */ +class CssStyleSheet : lout::misc::SimpleVector <CssRule*> { + public: + CssStyleSheet() : lout::misc::SimpleVector <CssRule*> (1) {}; + ~CssStyleSheet(); + void addRule (CssRule *rule); + void addRule (CssSelector *selector, CssPropertyList *props); + void apply (CssPropertyList *props, Doctree *docTree); +}; + +/** + * \brief A set of CssStyleSheets. + */ +class CssContext { + private: + static CssStyleSheet *userAgentStyle; + static CssStyleSheet *userStyle; + static CssStyleSheet *userImportantStyle; + CssStyleSheet *sheet[CSS_PRIMARY_USER_IMPORTANT + 1]; + + void buildUserAgentStyle (); + void buildUserStyle (); + + public: + CssContext (); + ~CssContext (); + + void addRule (CssRule *rule, CssPrimaryOrder order); + void apply (CssPropertyList *props, + Doctree *docTree, + CssPropertyList *tagStyle, CssPropertyList *nonCssHints); +}; + +#endif diff --git a/src/cssparser.cc b/src/cssparser.cc new file mode 100644 index 00000000..cf6fc341 --- /dev/null +++ b/src/cssparser.cc @@ -0,0 +1,1116 @@ +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.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 + +/* Applies to symbol lengths and string literals. */ +#define MAX_STR_LEN 256 + +static const char *Css_border_style_enum_vals[] = { + "none", "hidden", "dotted", "dashed", "solid", "double", "groove", + "ridge", "inset", "outset", NULL +}; + +static const char *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 *Css_display_enum_vals[DISPLAY_LAST + 1] = { + "block", "inline", "list-item", "table", "table-row-group", + "table-header-group", "table-footer-group", "table-row", + "table-cell", NULL +}; + +static const char *Css_font_style_enum_vals[] = { + "normal", "italic", "oblique", NULL +}; + +static const char *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 *Css_text_align_enum_vals[] = { + "left", "right", "center", "justify", "string", NULL +}; + +static const char *Css_text_decoration_enum_vals[] = { + "underline", "overline", "line-through", "blink", NULL +}; + +static const char *Css_vertical_align_vals[] = { + "top", "bottom", "middle", "baseline", "sub", "super", NULL +}; + +static const char *Css_white_space_vals[] = { + "normal", "pre", "nowrap", NULL +}; + +CssPropertyInfo Css_property_info[CssProperty::CSS_PROPERTY_LAST] = { + {"background-attachment", CSS_TYPE_UNUSED, NULL}, + {"background-color", CSS_TYPE_COLOR, 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, NULL}, + {"border-bottom-style", CSS_TYPE_ENUM, Css_border_style_enum_vals}, + {"border-bottom-width", CSS_TYPE_LENGTH, NULL}, + {"border-collapse", CSS_TYPE_UNUSED, NULL}, + {"border-left-color", CSS_TYPE_COLOR, NULL}, + {"border-left-style", CSS_TYPE_ENUM, Css_border_style_enum_vals}, + {"border-left-width", CSS_TYPE_LENGTH, NULL}, + {"border-right-color", CSS_TYPE_COLOR, NULL}, + {"border-right-style", CSS_TYPE_ENUM, Css_border_style_enum_vals}, + {"border-right-width", CSS_TYPE_LENGTH, NULL}, + {"border-spacing", CSS_TYPE_LENGTH, NULL}, + {"border-top-color", CSS_TYPE_COLOR, NULL}, + {"border-top-style", CSS_TYPE_ENUM, Css_border_style_enum_vals}, + {"border-top-width", CSS_TYPE_LENGTH, NULL}, + {"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, NULL}, + {"content", CSS_TYPE_STRING, NULL}, + {"counter-increment", CSS_TYPE_UNUSED, NULL}, + {"counter-reset", CSS_TYPE_UNUSED, NULL}, + {"cursor", CSS_TYPE_ENUM, Css_cursor_enum_vals}, + {"direction", CSS_TYPE_UNUSED, NULL}, + {"display", CSS_TYPE_ENUM, Css_display_enum_vals}, + {"empty-cells", CSS_TYPE_UNUSED, NULL}, + {"float", CSS_TYPE_UNUSED, NULL}, + {"font-family", CSS_TYPE_SYMBOL, NULL}, + {"font-size", CSS_TYPE_LENGTH_PERCENTAGE, NULL}, + {"font-size-adjust", CSS_TYPE_UNUSED, NULL}, + {"font-stretch", CSS_TYPE_UNUSED, NULL}, + {"font-style", CSS_TYPE_ENUM, Css_font_style_enum_vals}, + {"font-variant", CSS_TYPE_UNUSED, NULL}, + {"font-weight", CSS_TYPE_FONT_WEIGHT, NULL}, + {"height", CSS_TYPE_LENGTH_PERCENTAGE, NULL}, + {"left", CSS_TYPE_UNUSED, NULL}, + {"letter-spacing", CSS_TYPE_UNUSED, NULL}, + {"line-height", CSS_TYPE_UNUSED, NULL}, + {"list-style-image", CSS_TYPE_UNUSED, NULL}, + {"list-style-position", CSS_TYPE_UNUSED, NULL}, + {"list-style-type", CSS_TYPE_ENUM, Css_list_style_type_enum_vals}, + {"margin-bottom", CSS_TYPE_LENGTH, NULL}, + {"margin-left", CSS_TYPE_LENGTH, NULL}, + {"margin-right", CSS_TYPE_LENGTH, NULL}, + {"margin-top", CSS_TYPE_LENGTH, 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, NULL}, + {"padding-left", CSS_TYPE_LENGTH, NULL}, + {"padding-right", CSS_TYPE_LENGTH, NULL}, + {"padding-top", CSS_TYPE_LENGTH, NULL}, + {"position", CSS_TYPE_UNUSED, NULL}, + {"quotes", CSS_TYPE_UNUSED, NULL}, + {"right", CSS_TYPE_UNUSED, NULL}, + {"text-align", CSS_TYPE_ENUM, Css_text_align_enum_vals}, + {"text-decoration", CSS_TYPE_MULTI_ENUM, Css_text_decoration_enum_vals}, + {"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_vertical_align_vals}, + {"visibility", CSS_TYPE_UNUSED, NULL}, + {"white-space", CSS_TYPE_ENUM, Css_white_space_vals}, + {"text-indent", CSS_TYPE_UNUSED, NULL}, + {"width", CSS_TYPE_LENGTH_PERCENTAGE, NULL}, + {"word-spacing", CSS_TYPE_UNUSED, NULL}, + {"z-index", CSS_TYPE_UNUSED, NULL}, + + /* These are extensions, for internal used, and never parsed. */ + {"x-link", CSS_TYPE_INTEGER, NULL}, + {"x-colspan", CSS_TYPE_INTEGER, NULL}, + {"x-rowspan", CSS_TYPE_INTEGER, NULL}, + + {"last", CSS_TYPE_UNUSED, NULL}, +}; + +#define CSS_SHORTHAND_NUM 14 + +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; + CssProperty::Name * properties; /* CSS_SHORTHAND_MULTIPLE: + * must be terminated by -1 + * CSS_SHORTHAND_DIRECTIONS: + * must have length 4 + * CSS_SHORTHAND_BORDERS: + * unused + * CSS_SHORTHAND_FONT: + * unused */ +} CssShorthandInfo; + +CssProperty::Name Css_background_properties[] = { + CssProperty::CSS_PROPERTY_BACKGROUND_COLOR, + CssProperty::CSS_PROPERTY_BACKGROUND_IMAGE, + CssProperty::CSS_PROPERTY_BACKGROUND_REPEAT, + CssProperty::CSS_PROPERTY_BACKGROUND_ATTACHMENT, + CssProperty::CSS_PROPERTY_BACKGROUND_POSITION, + (CssProperty::Name) - 1 +}; + +CssProperty::Name Css_border_bottom_properties[] = { + CssProperty::CSS_PROPERTY_BORDER_BOTTOM_WIDTH, + CssProperty::CSS_PROPERTY_BORDER_BOTTOM_STYLE, + CssProperty::CSS_PROPERTY_BORDER_BOTTOM_COLOR, + (CssProperty::Name) - 1 +}; + +CssProperty::Name Css_border_color_properties[4] = { + CssProperty::CSS_PROPERTY_BORDER_TOP_COLOR, + CssProperty::CSS_PROPERTY_BORDER_BOTTOM_COLOR, + CssProperty::CSS_PROPERTY_BORDER_LEFT_COLOR, + CssProperty::CSS_PROPERTY_BORDER_RIGHT_COLOR +}; + +CssProperty::Name Css_border_left_properties[] = { + CssProperty::CSS_PROPERTY_BORDER_LEFT_WIDTH, + CssProperty::CSS_PROPERTY_BORDER_LEFT_STYLE, + CssProperty::CSS_PROPERTY_BORDER_LEFT_COLOR, + (CssProperty::Name) - 1 +}; + +CssProperty::Name Css_border_right_properties[] = { + CssProperty::CSS_PROPERTY_BORDER_RIGHT_WIDTH, + CssProperty::CSS_PROPERTY_BORDER_RIGHT_STYLE, + CssProperty::CSS_PROPERTY_BORDER_RIGHT_COLOR, + (CssProperty::Name) - 1 +}; + +CssProperty::Name Css_border_style_properties[4] = { + CssProperty::CSS_PROPERTY_BORDER_TOP_STYLE, + CssProperty::CSS_PROPERTY_BORDER_BOTTOM_STYLE, + CssProperty::CSS_PROPERTY_BORDER_LEFT_STYLE, + CssProperty::CSS_PROPERTY_BORDER_RIGHT_STYLE +}; + +CssProperty::Name Css_border_top_properties[] = { + CssProperty::CSS_PROPERTY_BORDER_TOP_WIDTH, + CssProperty::CSS_PROPERTY_BORDER_TOP_STYLE, + CssProperty::CSS_PROPERTY_BORDER_TOP_COLOR, + (CssProperty::Name) - 1 +}; + +CssProperty::Name Css_border_width_properties[4] = { + CssProperty::CSS_PROPERTY_BORDER_TOP_WIDTH, + CssProperty::CSS_PROPERTY_BORDER_BOTTOM_WIDTH, + CssProperty::CSS_PROPERTY_BORDER_LEFT_WIDTH, + CssProperty::CSS_PROPERTY_BORDER_RIGHT_WIDTH +}; + +CssProperty::Name Css_list_style_properties[] = { + CssProperty::CSS_PROPERTY_LIST_STYLE_TYPE, + CssProperty::CSS_PROPERTY_LIST_STYLE_POSITION, + CssProperty::CSS_PROPERTY_LIST_STYLE_IMAGE, + (CssProperty::Name) - 1 +}; + +CssProperty::Name Css_margin_properties[4] = { + CssProperty::CSS_PROPERTY_MARGIN_TOP, + CssProperty::CSS_PROPERTY_MARGIN_BOTTOM, + CssProperty::CSS_PROPERTY_MARGIN_LEFT, + CssProperty::CSS_PROPERTY_MARGIN_RIGHT +}; + +CssProperty::Name Css_outline_properties[] = { + CssProperty::CSS_PROPERTY_OUTLINE_COLOR, + CssProperty::CSS_PROPERTY_OUTLINE_STYLE, + CssProperty::CSS_PROPERTY_OUTLINE_WIDTH, + (CssProperty::Name) - 1 +}; + +CssProperty::Name Css_padding_properties[4] = { + CssProperty::CSS_PROPERTY_PADDING_TOP, + CssProperty::CSS_PROPERTY_PADDING_BOTTOM, + CssProperty::CSS_PROPERTY_PADDING_LEFT, + CssProperty::CSS_PROPERTY_PADDING_RIGHT +}; + +static CssShorthandInfo Css_shorthand_info[CSS_SHORTHAND_NUM] = { + {"background", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE, + Css_background_properties}, + {"border", CssShorthandInfo::CSS_SHORTHAND_BORDER, NULL}, + {"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, NULL}, + {"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}, +}; + + +static const char *Css_primary_text[CSS_PRIMARY_LAST] = { + "user stylesheets, important", "author stylesheets, important", + "author stylesheets", "user stylesheets", "user-agent stylesheets" +}; + +static const char *Css_level_text[4] = + { "id", "class", "pseudo-class", "element" }; + + +/* ---------------------------------------------------------------------- + * Initialization, Cleanup + * ---------------------------------------------------------------------- */ + +static int values_num; + +void a_Css_init(void) +{ + values_num = 0; +} + +void a_Css_freeall(void) +{ + if (values_num) + fprintf(stderr, "%d CSS values left", values_num); +} + +/* ---------------------------------------------------------------------- + * Parsing + * ---------------------------------------------------------------------- */ + +typedef enum { + CSS_TK_DECINT, CSS_TK_FLOAT, CSS_TK_COLOR, CSS_TK_SYMBOL, CSS_TK_STRING, + CSS_TK_CHAR, CSS_TK_END +} CssTokenType; + +typedef struct { + CssContext *context; + int order_count; + CssOrigin origin; + + const char *buf; + int buflen, bufptr; + + CssTokenType ttype; + char tval[MAX_STR_LEN]; + bool within_block; +} CssParser; + +/* + * Gets the next character from the buffer, or EOF. + */ +static int Css_getc(CssParser * parser) +{ + int c; + + if (parser->bufptr >= parser->buflen) + c = EOF; + else + c = parser->buf[parser->bufptr]; + + /* The buffer pointer is increased in any case, so that Css_ungetc works + * correctly at the end of the buffer. */ + parser->bufptr++; + return c; +} + +/* + * Undoes the last Css_getc(). + */ +static void Css_ungetc(CssParser * parser) +{ + parser->bufptr--; +} + +static void Css_next_token(CssParser * parser) +{ + int c, c1, d, i, j; + bool point_allowed; + char hexbuf[5]; + bool escaped; + + do + c = Css_getc(parser); + while (isspace(c)); + + if (isdigit(c)) { + parser->ttype = CSS_TK_DECINT; + point_allowed = true; + + parser->tval[0] = c; + i = 1; + c = Css_getc(parser); + while (isdigit(c) || (point_allowed && c == '.')) { + if (c == '.') { + parser->ttype = CSS_TK_FLOAT; + point_allowed = false; /* Only one point read. */ + } + + if (i < MAX_STR_LEN - 1) { + parser->tval[i] = c; + i++; + } + /* else silently truncated */ + c = Css_getc(parser); + } + parser->tval[i] = 0; + Css_ungetc(parser); + + DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token number %s\n", parser->tval); + return; + } + + if (isalpha(c) || c == '_' || c == '-') { + parser->ttype = CSS_TK_SYMBOL; + + parser->tval[0] = c; + i = 1; + c = Css_getc(parser); + while (isalnum(c) || c == '_' || c == '-') { + if (i < MAX_STR_LEN - 1) { + parser->tval[i] = c; + i++; + } /* else silently truncated */ + c = Css_getc(parser); + } + parser->tval[i] = 0; + Css_ungetc(parser); + DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token symbol '%s'\n", parser->tval); + return; + } + + if (c == '"' || c == '\'') { + c1 = c; + parser->ttype = CSS_TK_STRING; + + i = 0; + c = Css_getc(parser); + escaped = false; + + while (escaped || c != c1) { + if (c == '\\') { + escaped = true; + d = Css_getc(parser); + if (isxdigit(d)) { + /* Read hex Unicode char. (Actually, strings are yet only 8 + * bit.) */ + hexbuf[0] = d; + j = 1; + d = Css_getc(parser); + while (j < 4 && isxdigit(d)) { + hexbuf[j] = d; + j++; + d = Css_getc(parser); + } + hexbuf[j] = 0; + Css_ungetc(parser); + c = strtol(hexbuf, NULL, 16); + } else + /* Take next character literally. */ + c = Css_getc(parser); + } else + escaped = false; + + if (i < MAX_STR_LEN - 1) { + parser->tval[i] = c; + i++; + } /* else silently truncated */ + c = Css_getc(parser); + } + parser->tval[i] = 0; + /* No Css_ungetc(). */ + DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token string '%s'\n", parser->tval); + return; + } + + /* + * Within blocks, '#' starts a color, outside, it is used in selectors. + */ + if (c == '#' && parser->within_block) { + parser->ttype = CSS_TK_COLOR; + + parser->tval[0] = c; + i = 1; + c = Css_getc(parser); + while (isxdigit(c)) { + if (i < MAX_STR_LEN - 1) { + parser->tval[i] = c; + i++; + } /* else silently truncated */ + c = Css_getc(parser); + } + parser->tval[i] = 0; + Css_ungetc(parser); + DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token color '%s'\n", parser->tval); + return; + } + + if (c == EOF) { + DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token %s\n", "EOF"); + parser->ttype = CSS_TK_END; + return; + } + + parser->ttype = CSS_TK_CHAR; + parser->tval[0] = c; + parser->tval[1] = 0; + DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token char '%c'\n", c); +} + + +static bool Css_token_matches_property(CssParser * parser, + CssProperty::Name prop) +{ + int i, err = 1; + + switch (Css_property_info[prop].type) { + case CSS_TYPE_ENUM: + if (parser->ttype == CSS_TK_SYMBOL) { + for (i = 0; Css_property_info[prop].enum_symbols[i]; i++) + if (strcmp(parser->tval, + Css_property_info[prop].enum_symbols[i]) == 0) + return true; + } + return false; + + case CSS_TYPE_MULTI_ENUM: + if (parser->ttype == CSS_TK_SYMBOL) { + if (strcmp(parser->tval, "none") != 0) + return true; + else { + for (i = 0; Css_property_info[prop].enum_symbols[i]; i++) { + if (strcmp(parser->tval, + Css_property_info[prop].enum_symbols[i]) == 0) + return true; + } + } + } + return true; + + case CSS_TYPE_LENGTH_PERCENTAGE: + case CSS_TYPE_LENGTH: + return parser->ttype == CSS_TK_DECINT || + parser->ttype == CSS_TK_FLOAT || (parser->ttype == CSS_TK_SYMBOL + && strcmp(parser->tval, + "auto") == 0); + + case CSS_TYPE_COLOR: + return (parser->ttype == CSS_TK_COLOR || + parser->ttype == CSS_TK_SYMBOL) && + a_Color_parse(parser->tval, -1, &err) != -1; + + case CSS_TYPE_STRING: + return parser->ttype == CSS_TK_STRING; + + case CSS_TYPE_SYMBOL: + return parser->ttype == CSS_TK_SYMBOL; + + case CSS_TYPE_FONT_WEIGHT: + if (parser->ttype == CSS_TK_DECINT) { + i = atoi(parser->tval); + return i >= 100 && i <= 900; + } else + return (parser->ttype == CSS_TK_SYMBOL && + (strcmp(parser->tval, "normal") == 0 || + strcmp(parser->tval, "bold") == 0 || + strcmp(parser->tval, "bolder") == 0 || + strcmp(parser->tval, "lighter") == 0)); + break; + + case CSS_TYPE_UNUSED: + return false; + + case CSS_TYPE_INTEGER: + /* Not used for parser values. */ + default: + assert(false); + return false; + } +} + +static bool Css_parse_value(CssParser * parser, + CssProperty::Name prop, + CssProperty::Value * val) +{ + int i, lentype; + bool found, ret = false; + float fval; + int ival, err = 1; + + switch (Css_property_info[prop].type) { + case CSS_TYPE_ENUM: + if (parser->ttype == CSS_TK_SYMBOL) { + for (i = 0; Css_property_info[prop].enum_symbols[i]; i++) + if (strcmp(parser->tval, + Css_property_info[prop].enum_symbols[i]) == 0) { + val->intVal = i; + ret = true; + break; + } + Css_next_token(parser); + } + break; + + case CSS_TYPE_MULTI_ENUM: + val->intVal = 0; + ret = true; + + while (parser->ttype == CSS_TK_SYMBOL) { + if (strcmp(parser->tval, "none") != 0) { + for (i = 0, found = false; + !found && Css_property_info[prop].enum_symbols[i]; i++) { + if (strcmp(parser->tval, + Css_property_info[prop].enum_symbols[i]) == 0) + val->intVal |= (1 << i); + } + } + Css_next_token(parser); + } + break; + + case CSS_TYPE_LENGTH_PERCENTAGE: + case CSS_TYPE_LENGTH: + if (parser->ttype == CSS_TK_DECINT || parser->ttype == CSS_TK_FLOAT) { + fval = atof(parser->tval); + lentype = CSS_LENGTH_TYPE_PX; /* Actually, there must be a unit, + * except for num == 0. */ + + ret = true; + + Css_next_token(parser); + if (parser->ttype == CSS_TK_SYMBOL) { + if (strcmp(parser->tval, "px") == 0) { + lentype = CSS_LENGTH_TYPE_PX; + Css_next_token(parser); + } else if (strcmp(parser->tval, "mm") == 0) { + lentype = CSS_LENGTH_TYPE_MM; + Css_next_token(parser); + } else if (strcmp(parser->tval, "cm") == 0) { + lentype = CSS_LENGTH_TYPE_MM; + fval *= 10; + Css_next_token(parser); + } else if (strcmp(parser->tval, "in") == 0) { + lentype = CSS_LENGTH_TYPE_MM; + fval *= 25.4; + Css_next_token(parser); + } else if (strcmp(parser->tval, "pt") == 0) { + lentype = CSS_LENGTH_TYPE_MM; + fval *= (25.4 / 72); + Css_next_token(parser); + } else if (strcmp(parser->tval, "pc") == 0) { + lentype = CSS_LENGTH_TYPE_MM; + fval *= (25.4 / 6); + Css_next_token(parser); + } else if (strcmp(parser->tval, "em") == 0) { + lentype = CSS_LENGTH_TYPE_EM; + Css_next_token(parser); + } else if (strcmp(parser->tval, "ex") == 0) { + lentype = CSS_LENGTH_TYPE_EX; + Css_next_token(parser); + } + } else if (Css_property_info[prop].type == + CSS_TYPE_LENGTH_PERCENTAGE && + parser->ttype == CSS_TK_CHAR && + parser->tval[0] == '%') { + fval /= 100; + lentype = CSS_LENGTH_TYPE_PERCENTAGE; + Css_next_token(parser); + } + + val->intVal = CSS_CREATE_LENGTH(fval, lentype); + } else if (parser->ttype == CSS_TK_SYMBOL && + strcmp(parser->tval, "auto") == 0) { + val->intVal = CSS_LENGTH_TYPE_AUTO; + } + break; + + case CSS_TYPE_COLOR: + if (parser->ttype == CSS_TK_COLOR) { + val->intVal = a_Color_parse(parser->tval, -1, &err); + if (err) + MSG_CSS("color is not in \"%s\" format\n", "#RRGGBB"); + else + ret = true; + Css_next_token(parser); + } else if (parser->ttype == CSS_TK_SYMBOL) { + val->intVal = a_Color_parse(parser->tval, -1, &err); + if (err) + MSG_CSS("color is not in \"%s\" format\n", "#RRGGBB"); + else + ret = true; + Css_next_token(parser); + } + break; + + case CSS_TYPE_STRING: + if (parser->ttype == CSS_TK_STRING) { + val->strVal = dStrdup(parser->tval); + Css_next_token(parser); + } + break; + + case CSS_TYPE_SYMBOL: + if (parser->ttype == CSS_TK_SYMBOL) { + val->strVal = dStrdup(parser->tval); + ret = true; + Css_next_token(parser); + } + break; + + case CSS_TYPE_FONT_WEIGHT: + ival = 0; + if (parser->ttype == CSS_TK_DECINT) { + ival = atoi(parser->tval); + if (ival < 100 || ival > 900) + /* invalid */ + ival = 0; + } else if (parser->ttype == CSS_TK_SYMBOL) { + if (strcmp(parser->tval, "normal") == 0) + ival = CssProperty::CSS_FONT_WEIGHT_NORMAL; + if (strcmp(parser->tval, "bold") == 0) + ival = CssProperty::CSS_FONT_WEIGHT_BOLD; + if (strcmp(parser->tval, "bolder") == 0) + ival = CssProperty::CSS_FONT_WEIGHT_BOLDER; + if (strcmp(parser->tval, "lighter") == 0) + ival = CssProperty::CSS_FONT_WEIGHT_LIGHTER; + } + + if (ival != 0) { + val->intVal = ival; + ret = true; + Css_next_token(parser); + } + break; + + case CSS_TYPE_UNUSED: + /* nothing */ + break; + + case CSS_TYPE_INTEGER: + /* Not used for parser values. */ + default: + assert(false); /* not reached */ + } + + return ret; +} + +static bool Css_parse_weight(CssParser * parser) +{ + if (parser->ttype == CSS_TK_CHAR && parser->tval[0] == '!') { + Css_next_token(parser); + if (parser->ttype == CSS_TK_SYMBOL && + strcmp(parser->tval, "important") == 0) { + Css_next_token(parser); + return true; + } + } + + return false; +} + +/* + * bsearch(3) compare function for searching properties + */ +static int Css_property_info_cmp(const void *a, const void *b) +{ + return strcmp(((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 strcmp(((CssShorthandInfo *) a)->symbol, + ((CssShorthandInfo *) b)->symbol); +} + +static void Css_parse_declaration(CssParser * parser, + CssPropertyList * props, + CssPropertyList * importantProps) +{ + CssPropertyInfo pi, *pip; + CssShorthandInfo si, *sip; + + CssProperty::Name prop; + CssProperty::Value val, dir_vals[4]; + bool found, weight; + int sh_index, i, 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 (parser->ttype == CSS_TK_SYMBOL) { + pi.symbol = parser->tval; + pip = + (CssPropertyInfo *) bsearch(&pi, Css_property_info, + CSS_NUM_PARSED_PROPERTIES, + sizeof(CssPropertyInfo), + Css_property_info_cmp); + if (pip) { + prop = (CssProperty::Name) (pip - Css_property_info); + Css_next_token(parser); + if (parser->ttype == CSS_TK_CHAR && parser->tval[0] == ':') { + Css_next_token(parser); + if (Css_parse_value(parser, prop, &val)) { + weight = Css_parse_weight(parser); + if (weight) + importantProps->set(prop, val); + else + props->set(prop, val); + } + } + } else { + /* Try shorthands. */ + si.symbol = parser->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; + Css_next_token(parser); + if (parser->ttype == CSS_TK_CHAR && parser->tval[0] == ':') { + Css_next_token(parser); + + switch (Css_shorthand_info[sh_index].type) { + case CssShorthandInfo::CSS_SHORTHAND_MULTIPLE: + do { + for (found = false, i = 0; + !found && + Css_shorthand_info[sh_index].properties[i] != -1; + i++) + if (Css_token_matches_property(parser, + Css_shorthand_info + [sh_index]. + properties[i])) { + found = true; + DEBUG_MSG(DEBUG_PARSE_LEVEL, + "will assign to '%s'\n", + Css_property_info + [Css_shorthand_info[sh_index] + .properties[i]].symbol); + if (Css_parse_value(parser, + Css_shorthand_info[sh_index] + .properties[i], &val)) { + weight = Css_parse_weight(parser); + if (weight) + importantProps-> + set(Css_shorthand_info[sh_index]. + properties[i], val); + else + props->set(Css_shorthand_info[sh_index]. + properties[i], val); + } + } + } while (found); + break; + + case CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS: + n = 0; + while (n < 4) { + if (Css_token_matches_property(parser, + Css_shorthand_info + [sh_index]. + properties[0]) && + Css_parse_value(parser, + Css_shorthand_info[sh_index] + .properties[0], &val)) { + dir_vals[n] = val; + n++; + } else + break; + } + + weight = Css_parse_weight(parser); + if (n > 0) { + for (i = 0; i < 4; i++) + if (weight) + importantProps->set(Css_shorthand_info[sh_index] + .properties[i], + dir_vals[dir_set[n - 1] + [i]]); + else + props->set(Css_shorthand_info[sh_index] + .properties[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: + /* todo: Not yet implemented. */ + break; + + case CssShorthandInfo::CSS_SHORTHAND_FONT: + /* todo: Not yet implemented. */ + break; + } + } + } + } + } + + /* Skip all tokens until the expected end. */ + while (!(parser->ttype == CSS_TK_END || + (parser->ttype == CSS_TK_CHAR && + (parser->tval[0] == ';' || parser->tval[0] == '}')))) + Css_next_token(parser); + + if (parser->ttype == CSS_TK_CHAR && parser->tval[0] == ';') + Css_next_token(parser); +} + +static void Css_parse_ruleset(CssParser * parser) +{ + lout::misc::SimpleVector < CssSelector * >*list; + CssPropertyList *props, *importantProps; + CssSelector *selector; + const char *p, **pp; + + list = new lout::misc::SimpleVector < CssSelector * >(1); + + while (true) { + selector = NULL; + + if (parser->ttype == CSS_TK_SYMBOL) { + int element = a_Html_tag_index(parser->tval); + if (element != -1) + selector = new CssSelector(element); + Css_next_token(parser); + } else if (parser->ttype == CSS_TK_CHAR && parser->tval[0] == '*') { + selector = new CssSelector(); + Css_next_token(parser); + } else if (parser->ttype == CSS_TK_CHAR && + (parser->tval[0] == '.' || parser->tval[0] == ':' || + parser->tval[0] == '#')) { + selector = new CssSelector(); + /* But no next token. */ + } + + if (selector) { + selector->ref(); + selector->id = selector->klass = selector->pseudo = NULL; + + do { + pp = NULL; + if (parser->ttype == CSS_TK_CHAR) { + switch (parser->tval[0]) { + case '#': + pp = &selector->id; + break; + case '.': + pp = &selector->klass; + break; + case ':': + pp = &selector->pseudo; + break; + } + } + + if (pp) { + Css_next_token(parser); + if (parser->ttype == CSS_TK_SYMBOL || + parser->ttype == CSS_TK_DECINT) { + if (*pp == NULL) + *pp = dStrdup(parser->tval); + Css_next_token(parser); + } else if (parser->ttype == CSS_TK_FLOAT) { + /* In this case, we are actually interested in three tokens: + * number, '.', number. Instead, we have a decimal fraction, + * which we split up again. */ + p = strchr(parser->tval, '.'); + if (*pp == NULL) + *pp = dStrndup(parser->tval, p - parser->tval); + if (selector->klass == NULL) + selector->klass = dStrdup(p + 1); + Css_next_token(parser); + } + } + } while (pp); + } + + /* Skip all tokens which may "belong" to this selector. */ + while (!(parser->ttype == CSS_TK_END || + (parser->ttype == CSS_TK_CHAR && + (parser->tval[0] == ',' || parser->tval[0] == '{')))) + Css_next_token(parser); + + if (selector) + DEBUG_MSG(DEBUG_PARSE_LEVEL, "end of selector (%s, %s, %s, %d)\n", + selector->id, selector->klass, selector->pseudo, + selector->element); + else + DEBUG_MSG(DEBUG_PARSE_LEVEL, "not a %s\n", "selector"); + + if (selector) { + list->increase(); + list->set(list->size() - 1, selector); + } + + if (parser->ttype == CSS_TK_CHAR && parser->tval[0] == ',') + /* To read the next token. */ + Css_next_token(parser); + else + /* No more selectors. */ + break; + } + + DEBUG_MSG(DEBUG_PARSE_LEVEL, "end of %s\n", "selectors"); + + props = new CssPropertyList(); + props->ref(); + importantProps = new CssPropertyList(); + importantProps->ref(); + + /* Read block. ('{' has already been read.) */ + if (parser->ttype != CSS_TK_END) { + parser->within_block = true; + Css_next_token(parser); + do + Css_parse_declaration(parser, props, importantProps); + while (!(parser->ttype == CSS_TK_END || + (parser->ttype == CSS_TK_CHAR && parser->tval[0] == '}'))); + parser->within_block = false; + } + + for (int i = 0; i < list->size(); i++) { + CssSelector *s = list->get(i); + + if (parser->origin == CSS_ORIGIN_USER_AGENT) { + parser->context->addRule(new CssRule(s, props), + CSS_PRIMARY_USER_AGENT); + } else if (parser->origin == CSS_ORIGIN_USER) { + parser->context->addRule(new CssRule(s, props), CSS_PRIMARY_USER); + parser->context->addRule(new CssRule(s, importantProps), + CSS_PRIMARY_USER_IMPORTANT); + } else if (parser->origin == CSS_ORIGIN_AUTHOR) { + parser->context->addRule(new CssRule(s, props), + CSS_PRIMARY_AUTHOR); + parser->context->addRule(new CssRule(s, importantProps), + CSS_PRIMARY_AUTHOR_IMPORTANT); + } + + s->unref(); + } + + props->unref(); + importantProps->unref(); + + delete list; + + if (parser->ttype == CSS_TK_CHAR && parser->tval[0] == '}') + Css_next_token(parser); +} + +void a_Css_parse(CssContext * context, + const char *buf, + int buflen, int order_count, CssOrigin origin) +{ + CssParser parser; + + parser.context = context; + parser.buf = buf; + parser.buflen = buflen; + parser.bufptr = 0; + parser.order_count = 0; + parser.origin = origin; + parser.within_block = false; + + Css_next_token(&parser); + while (parser.ttype != CSS_TK_END) + Css_parse_ruleset(&parser); +} + +#if 0 +void p_Css_parse_element_style(CssContext * context, + char *id, + char *klass, + char *pseudo, + char *element, + const char *buf, + int buflen, + int order_count, CssOrigin origin) +{ + CssParser parser; + CssSelector *selector; + GSList *list; + + parser.context = context; + parser.buf = buf; + parser.buflen = buflen; + parser.bufptr = 0; + parser.order_count = 0; + parser.origin = origin; + parser.within_block = true; + + selector = g_new(CssSelector, 1); + selector->id = id; + selector->klass = klass; + selector->pseudo = pseudo; + selector->element = element; + list = g_slist_append(NULL, selector); + + Css_next_token(&parser); + while (parser.ttype != CSS_TK_END) + Css_parse_declaration(&parser, list); + + dFree(selector); + g_slist_free(list); +} +#endif diff --git a/src/cssparser.hh b/src/cssparser.hh new file mode 100644 index 00000000..2446e4a9 --- /dev/null +++ b/src/cssparser.hh @@ -0,0 +1,73 @@ +#ifndef __CSSPARSER_HH__ +#define __CSSPARSER_HH__ + +#include "css.hh" + +/* The last three ones are never parsed. */ +#define CSS_NUM_INTERNAL_PROPERTIES 3 +#define CSS_NUM_PARSED_PROPERTIES \ + (CssProperty::CSS_PROPERTY_LAST - CSS_NUM_INTERNAL_PROPERTIES) + + +typedef enum { + CSS_TYPE_INTEGER, /* This type is only used internally, for x-* + properties. */ + CSS_TYPE_ENUM, /* Value is i, if represented by + enum_symbols[i]. */ + CSS_TYPE_MULTI_ENUM, /* For all enum_symbols[i], 1 << i are + combined. */ + CSS_TYPE_LENGTH_PERCENTAGE, /* <length> or <percentage>. Represented by + CssLength. */ + CSS_TYPE_LENGTH, /* <length>, represented as CssLength. + Note: In some cases, CSS_TYPE_LENGTH is used + instead of CSS_TYPE_LENGTH_PERCENTAGE, + only because Dw cannot handle percentages + in this particular case (e.g. + 'margin-*-width'). */ + CSS_TYPE_COLOR, /* Represented as integer. */ + CSS_TYPE_FONT_WEIGHT, /* this very special and only used by + 'font-weight' */ + CSS_TYPE_STRING, /* <string> */ + CSS_TYPE_SYMBOL, /* Symbols, which are directly copied (as + opposed to CSS_TYPE_ENUM and + CSS_TYPE_MULTI_ENUM). Used for + 'font-family'. */ + CSS_TYPE_UNUSED /* Not yet used. Will itself get unused some + day. */ +} CssValueType; + +typedef enum { + CSS_ORIGIN_USER_AGENT, + CSS_ORIGIN_USER, + CSS_ORIGIN_AUTHOR, +} CssOrigin; + +typedef struct { + const char *symbol; + CssValueType type; + const char **enum_symbols; +} CssPropertyInfo; + + +void a_Css_init (void); +void a_Css_freeall (void); + +void a_Css_parse (CssContext *context, + const char *buf, + int buflen, + int order_count, + CssOrigin origin); + +void p_Css_parse_element_style (CssContext *context, + char *id, + char *klass, + char *pseudo, + char *element, + const char *buf, + int buflen, + int order_count, + CssOrigin origin); + +extern CssPropertyInfo Css_property_info[CssProperty::CSS_PROPERTY_LAST]; + +#endif // __CSS_H__ diff --git a/src/doctree.hh b/src/doctree.hh new file mode 100644 index 00000000..97686e40 --- /dev/null +++ b/src/doctree.hh @@ -0,0 +1,20 @@ +#ifndef __DOCTREE_HH__ +#define __DOCTREE_HH__ + +class DoctreeNode { + public: + int depth; + int element; + const char *klass; + const char *pseudo; + const char *id; +}; + +class Doctree { + public: + virtual ~Doctree () {}; + virtual const DoctreeNode *top () = 0; + virtual const DoctreeNode *parent (const DoctreeNode *node) = 0; +}; + +#endif diff --git a/src/form.cc b/src/form.cc index 6f7979be..bf706ebf 100644 --- a/src/form.cc +++ b/src/form.cc @@ -277,7 +277,7 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize) char *charset, *first; const char *attrbuf; - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); if (html->InFlags & IN_FORM) { BUG_MSG("nested forms\n"); @@ -540,7 +540,7 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize) bg = Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color); HTML_SET_TOP_ATTR(html, backgroundColor, bg); - DW2TB(html->dw)->addWidget (embed, S_TOP(html)->style); + DW2TB(html->dw)->addWidget (embed, html->styleEngine->style ()); } dFree(type); dFree(name); @@ -573,10 +573,10 @@ void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize) html->charset); html->InFlags |= IN_FORM; - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "prompt"))) - DW2TB(html->dw)->addText(attrbuf, S_TOP(html)->style); + DW2TB(html->dw)->addText(attrbuf, html->styleEngine->style ()); ResourceFactory *factory = HT2LT(html)->getResourceFactory(); EntryResource *entryResource = factory->createEntryResource (20, false); @@ -589,7 +589,7 @@ void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize) else bg = Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color); HTML_SET_TOP_ATTR(html, backgroundColor, bg); - DW2TB(html->dw)->addWidget (embed, S_TOP(html)->style); + DW2TB(html->dw)->addWidget (embed, html->styleEngine->style ()); a_Url_free(action); html->InFlags &= ~IN_FORM; @@ -658,7 +658,7 @@ void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize) bg = Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color); HTML_SET_TOP_ATTR(html, backgroundColor, bg); - DW2TB(html->dw)->addWidget (embed, S_TOP(html)->style); + DW2TB(html->dw)->addWidget (embed, html->styleEngine->style ()); dFree(name); } @@ -740,7 +740,7 @@ void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize) } HTML_SET_TOP_ATTR(html, backgroundColor, Color::createShaded (HT2LT(html), bg)); - DW2TB(html->dw)->addWidget (embed, S_TOP(html)->style); + DW2TB(html->dw)->addWidget (embed, html->styleEngine->style ()); // size = 0; // if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "size"))) @@ -861,7 +861,7 @@ void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize) Embed *embed; char *name, *value; - style_attrs = *S_TOP(html)->style; + style_attrs = *html->styleEngine->style (); style_attrs.margin.setVal(0); style_attrs.borderWidth.setVal(0); style_attrs.padding.setVal(0); @@ -1904,7 +1904,7 @@ static Embed *Html_input_image(DilloHtml *html, const char *tag, int tagsize) if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "src")) && (url = a_Html_url_new(html, attrbuf, NULL, 0))) { - style_attrs = *S_TOP(html)->style; + style_attrs = *html->styleEngine->style (); style_attrs.cursor = CURSOR_POINTER; style_attrs.backgroundColor = style::Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color); diff --git a/src/html.cc b/src/html.cc index 88607395..3edd59a4 100644 --- a/src/html.cc +++ b/src/html.cc @@ -113,7 +113,6 @@ static int Html_write_raw(DilloHtml *html, char *buf, int bufsize, int Eof); static void Html_load_image(BrowserWindow *bw, DilloUrl *url, DilloImage *image); static void Html_callback(int Op, CacheClient_t *Client); -static int Html_tag_index(const char *tag); static void Html_tag_cleanup_at_close(DilloHtml *html, int TagIdx); /*----------------------------------------------------------------------------- @@ -266,9 +265,10 @@ static int Html_add_new_linkimage(DilloHtml *html, void a_Html_set_top_font(DilloHtml *html, const char *name, int size, int BI, int BImask) { +#if 0 FontAttrs font_attrs; - font_attrs = *S_TOP(html)->style->font; + font_attrs = *html->styleEngine->style ()->font; if (name) font_attrs.name = name; if (size) @@ -280,6 +280,7 @@ void a_Html_set_top_font(DilloHtml *html, const char *name, int size, HTML_SET_TOP_ATTR (html, font, Font::create (HT2LT(html), &font_attrs)); +#endif } /* @@ -287,25 +288,26 @@ void a_Html_set_top_font(DilloHtml *html, const char *name, int size, * sets the style at the top of the stack. */ void a_Html_tag_set_align_attr(DilloHtml *html, + CssPropertyList *props, const char *tag, int tagsize) { - const char *align, *charattr; + const char *align; if ((align = a_Html_get_attr(html, tag, tagsize, "align"))) { - Style *old_style = S_TOP(html)->style; - StyleAttrs style_attrs = *old_style; + TextAlignType textAlignType = TEXT_ALIGN_LEFT; if (dStrcasecmp (align, "left") == 0) - style_attrs.textAlign = TEXT_ALIGN_LEFT; + textAlignType = TEXT_ALIGN_LEFT; else if (dStrcasecmp (align, "right") == 0) - style_attrs.textAlign = TEXT_ALIGN_RIGHT; + textAlignType = TEXT_ALIGN_RIGHT; else if (dStrcasecmp (align, "center") == 0) - style_attrs.textAlign = TEXT_ALIGN_CENTER; + textAlignType = TEXT_ALIGN_CENTER; else if (dStrcasecmp (align, "justify") == 0) - style_attrs.textAlign = TEXT_ALIGN_JUSTIFY; + textAlignType = TEXT_ALIGN_JUSTIFY; +#if 0 else if (dStrcasecmp (align, "char") == 0) { /* TODO: Actually not supported for <p> etc. */ - style_attrs.textAlign = TEXT_ALIGN_STRING; + v.textAlign = TEXT_ALIGN_STRING; if ((charattr = a_Html_get_attr(html, tag, tagsize, "char"))) { if (charattr[0] == 0) /* TODO: ALIGN=" ", and even ALIGN="&32;" will reult in @@ -319,8 +321,8 @@ void a_Html_tag_set_align_attr(DilloHtml *html, /* TODO: Examine LANG attr of <html>. */ style_attrs.textAlignChar = '.'; } - S_TOP(html)->style = Style::create (HT2LT(html), &style_attrs); - old_style->unref (); +#endif + props->set (CssProperty::CSS_PROPERTY_TEXT_ALIGN, textAlignType); } } @@ -329,19 +331,22 @@ void a_Html_tag_set_align_attr(DilloHtml *html, * sets the style in style_attrs. Returns true when set. */ bool a_Html_tag_set_valign_attr(DilloHtml *html, const char *tag, - int tagsize, StyleAttrs *style_attrs) + int tagsize, CssPropertyList *props) { const char *attr; + VAlignType valign; if ((attr = a_Html_get_attr(html, tag, tagsize, "valign"))) { if (dStrcasecmp (attr, "top") == 0) - style_attrs->valign = VALIGN_TOP; + valign = VALIGN_TOP; else if (dStrcasecmp (attr, "bottom") == 0) - style_attrs->valign = VALIGN_BOTTOM; + valign = VALIGN_BOTTOM; else if (dStrcasecmp (attr, "baseline") == 0) - style_attrs->valign = VALIGN_BASELINE; + valign = VALIGN_BASELINE; else - style_attrs->valign = VALIGN_MIDDLE; + valign = VALIGN_MIDDLE; + + props->set (CssProperty::CSS_PROPERTY_VERTICAL_ALIGN, valign); return true; } else return false; @@ -359,7 +364,7 @@ static void Html_add_indented_widget(DilloHtml *html, Widget *textblock, StyleAttrs style_attrs; Style *style; - style_attrs = *S_TOP(html)->style; + style_attrs = *html->styleEngine->style (); style_attrs.margin.setVal (0); style_attrs.borderWidth.setVal (0); @@ -468,8 +473,7 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url, stack = new misc::SimpleVector <DilloHtmlState> (16); stack->increase(); - stack->getRef(0)->style = NULL; - stack->getRef(0)->table_cell_style = NULL; + stack->getRef(0)->table_cell_props = NULL; stack->getRef(0)->parse_mode = DILLO_HTML_PARSE_MODE_INIT; stack->getRef(0)->table_mode = DILLO_HTML_TABLE_MODE_NONE; stack->getRef(0)->cell_text_align_set = false; @@ -482,6 +486,8 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url, stack->getRef(0)->current_bg_color = prefs.bg_color; stack->getRef(0)->hand_over_break = false; + styleEngine = new StyleEngine (HT2LT (this)); + InFlags = IN_NONE; Stash = dStr_new(""); @@ -511,8 +517,8 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url, images = new misc::SimpleVector <DilloLinkImage*> (16); //a_Dw_image_map_list_init(&maps); - link_color = prefs.link_color; - visited_color = prefs.visited_color; + link_color = -1; + visited_color = -1; /* Initialize the main widget */ initDw(); @@ -525,26 +531,12 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url, */ void DilloHtml::initDw() { - StyleAttrs style_attrs; - FontAttrs font_attrs; - dReturn_if_fail (dw == NULL); /* Create the main widget */ dw = stack->getRef(0)->textblock = new Textblock (prefs.limit_text_width); - /* Create a dummy font, attribute, and tag for the bottom of the stack. */ - font_attrs.name = prefs.vw_fontname; - font_attrs.size = Html_level_to_fontsize(FontSizesBase); - font_attrs.weight = 400; - font_attrs.style = FONT_STYLE_NORMAL; - - style_attrs.initValues (); - style_attrs.font = Font::create (HT2LT(this), &font_attrs); - style_attrs.color = Color::createSimple (HT2LT(this), prefs.text_color); - stack->getRef(0)->style = Style::create (HT2LT(this), &style_attrs); - - stack->getRef(0)->table_cell_style = NULL; + stack->getRef(0)->table_cell_props = NULL; /* Handle it when the user clicks on a link */ connectSignals(dw); @@ -644,7 +636,7 @@ int DilloHtml::getCurTagLineNumber() */ void DilloHtml::freeParseData() { - (stack->getRef(0)->style)->unref (); /* template style */ +// (stack->getRef(0)->style)->unref (); /* template style */ delete(stack); dStr_free(Stash, TRUE); @@ -1100,11 +1092,11 @@ static void Html_process_space(DilloHtml *html, const char *space, if (spaceCnt) { spc = dStrnfill(spaceCnt, ' '); - DW2TB(html->dw)->addText (spc, S_TOP(html)->style); + DW2TB(html->dw)->addText (spc, html->styleEngine->style ()); dFree(spc); spaceCnt = 0; } - DW2TB(html->dw)->addLinebreak (S_TOP(html)->style); + DW2TB(html->dw)->addLinebreak (html->styleEngine->style ()); html->pre_column = 0; } html->PreFirstChar = false; @@ -1132,7 +1124,7 @@ static void Html_process_space(DilloHtml *html, const char *space, if (spaceCnt) { spc = dStrnfill(spaceCnt, ' '); - DW2TB(html->dw)->addText (spc, S_TOP(html)->style); + DW2TB(html->dw)->addText (spc, html->styleEngine->style ()); dFree(spc); } @@ -1140,7 +1132,7 @@ static void Html_process_space(DilloHtml *html, const char *space, if (SGML_SPCDEL) { /* SGML_SPCDEL ignores white space inmediately after an open tag */ } else if (!html->PrevWasSPC) { - DW2TB(html->dw)->addSpace(S_TOP(html)->style); + DW2TB(html->dw)->addSpace(html->styleEngine->style ()); html->PrevWasSPC = true; } @@ -1193,7 +1185,7 @@ static void Html_process_word(DilloHtml *html, const char *word, int size) while (Pword[++i] && !isspace(Pword[i])) ; ch = Pword[i]; Pword[i] = 0; - DW2TB(html->dw)->addText(Pword, S_TOP(html)->style); + DW2TB(html->dw)->addText(Pword, html->styleEngine->style ()); Pword[i] = ch; html->pre_column += i - start; html->PreFirstChar = false; @@ -1203,7 +1195,7 @@ static void Html_process_word(DilloHtml *html, const char *word, int size) } else { if (!memchr(word,'&', size)) { /* No entities */ - DW2TB(html->dw)->addText(word, S_TOP(html)->style); + DW2TB(html->dw)->addText(word, html->styleEngine->style ()); } else { /* Collapse white-space entities inside the word (except ) */ Pword = a_Html_parse_entities(html, word, size); @@ -1211,7 +1203,7 @@ static void Html_process_word(DilloHtml *html, const char *word, int size) if (strchr("\t\f\n\r", Pword[i])) for (j = i; (Pword[j] = Pword[j+1]); ++j) ; - DW2TB(html->dw)->addText(Pword, S_TOP(html)->style); + DW2TB(html->dw)->addText(Pword, html->styleEngine->style ()); dFree(Pword); } } @@ -1248,7 +1240,7 @@ static void Html_eventually_pop_dw(DilloHtml *html, bool hand_over_break) { if (html->dw != S_TOP(html)->textblock) { if (hand_over_break) - DW2TB(html->dw)->handOverBreak (S_TOP(html)->style); + DW2TB(html->dw)->handOverBreak (html->styleEngine->style ()); DW2TB(html->dw)->flush (); html->dw = S_TOP(html)->textblock; } @@ -1267,10 +1259,8 @@ static void Html_push_tag(DilloHtml *html, int tag_idx) * instead of copying all fields except for tag. --Jcid */ *html->stack->getRef(n_items) = *html->stack->getRef(n_items - 1); html->stack->getRef(n_items)->tag_idx = tag_idx; - /* proper memory management, may be unref'd later */ - (S_TOP(html)->style)->ref (); - if (S_TOP(html)->table_cell_style) - (S_TOP(html)->table_cell_style)->ref (); + if (S_TOP(html)->table_cell_props) + S_TOP(html)->table_cell_props->ref (); html->dw = S_TOP(html)->textblock; } @@ -1280,6 +1270,7 @@ static void Html_push_tag(DilloHtml *html, int tag_idx) */ static void Html_force_push_tag(DilloHtml *html, int tag_idx) { + html->styleEngine->startElement (tag_idx); Html_push_tag(html, tag_idx); } @@ -1290,9 +1281,9 @@ static void Html_real_pop_tag(DilloHtml *html) { bool hand_over_break; - (S_TOP(html)->style)->unref (); - if (S_TOP(html)->table_cell_style) - (S_TOP(html)->table_cell_style)->unref (); + html->styleEngine->endElement (S_TOP(html)->tag_idx); + if (S_TOP(html)->table_cell_props) + S_TOP(html)->table_cell_props->unref (); hand_over_break = S_TOP(html)->hand_over_break; html->stack->setSize (html->stack->size() - 1); Html_eventually_pop_dw(html, hand_over_break); @@ -1699,8 +1690,7 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize) { const char *attrbuf; Textblock *textblock; - StyleAttrs style_attrs; - Style *style; + CssPropertyList props; int32_t color; if (!(html->InFlags & IN_BODY)) @@ -1718,43 +1708,37 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize) textblock = DW2TB(html->dw); - if (!prefs.force_my_colors) { - if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) { - color = a_Html_color_parse(html, attrbuf, prefs.bg_color); - if (color == 0xffffff && !prefs.allow_white_bg) - color = prefs.bg_color; - - style_attrs = *html->dw->getStyle (); - style_attrs.backgroundColor = Color::createShaded(HT2LT(html), color); - style = Style::create (HT2LT(html), &style_attrs); - html->dw->setStyle (style); - style->unref (); - S_TOP(html)->current_bg_color = color; - } + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) { + color = a_Html_color_parse(html, attrbuf, prefs.bg_color); + if (color == 0xffffff && !prefs.allow_white_bg) + color = prefs.bg_color; + S_TOP(html)->current_bg_color = color; + props.set (CssProperty::CSS_PROPERTY_BACKGROUND_COLOR, color); + } - if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "text"))) { - color = a_Html_color_parse(html, attrbuf, prefs.text_color); - HTML_SET_TOP_ATTR (html, color, - Color::createSimple (HT2LT(html),color)); - } + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "text"))) { + color = a_Html_color_parse(html, attrbuf, prefs.text_color); + props.set (CssProperty::CSS_PROPERTY_COLOR, color); + } - if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "link"))) - html->link_color = a_Html_color_parse(html,attrbuf,prefs.link_color); + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "link"))) + html->link_color = a_Html_color_parse(html, attrbuf, -1); - if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "vlink"))) - html->visited_color = a_Html_color_parse(html, attrbuf, - prefs.visited_color); + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "vlink"))) + html->visited_color = a_Html_color_parse(html, attrbuf, -1); - if (prefs.contrast_visited_color) { - /* get a color that has a "safe distance" from text, link and bg */ - html->visited_color = + if (prefs.contrast_visited_color) { + /* get a color that has a "safe distance" from text, link and bg */ + html->visited_color = a_Color_vc(html->visited_color, - S_TOP(html)->style->color->getColor(), + html->styleEngine->style ()->color->getColor(), html->link_color, S_TOP(html)->current_bg_color); - } } + html->styleEngine->setNonCssHints (&props); + html->dw->setStyle (html->styleEngine->style ()); + S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_BODY; } @@ -1777,13 +1761,16 @@ static void Html_tag_close_body(DilloHtml *html, int TagIdx) */ static void Html_tag_open_p(DilloHtml *html, const char *tag, int tagsize) { + CssPropertyList props; + if ((html->InFlags & IN_LI) && !html->WordAfterLI) { /* ignore first parbreak after an empty <LI> */ html->WordAfterLI = true; } else { - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); } - a_Html_tag_set_align_attr (html, tag, tagsize); + a_Html_tag_set_align_attr (html, &props, tag, tagsize); + html->styleEngine->setNonCssHints (&props); } /* @@ -1811,7 +1798,7 @@ static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize) src = dStrdup(attrbuf); - style_attrs = *(S_TOP(html)->style); + style_attrs = *(html->styleEngine->style ()); if (a_Capi_get_flags(url) & CAPI_IsCached) { /* visited frame */ style_attrs.color = @@ -1824,14 +1811,14 @@ static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize) style_attrs.cursor = CURSOR_POINTER; link_style = Style::create (HT2LT(html), &style_attrs); - textblock->addParbreak (5, S_TOP(html)->style); + textblock->addParbreak (5, html->styleEngine->style ()); /* The bullet will be assigned the current list style, which should * be "disc" by default, but may in very weird pages be different. * Anyway, there should be no harm. */ bullet = new Bullet(); - textblock->addWidget(bullet, S_TOP(html)->style); - textblock->addSpace(S_TOP(html)->style); + textblock->addWidget(bullet, html->styleEngine->style ()); + textblock->addSpace(html->styleEngine->style ()); if (tolower(tag[1]) == 'i') { /* IFRAME usually comes with very long advertising/spying URLS, @@ -1848,7 +1835,7 @@ static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize) } } - textblock->addParbreak (5, S_TOP(html)->style); + textblock->addParbreak (5, html->styleEngine->style ()); link_style->unref (); dFree(src); @@ -1862,8 +1849,8 @@ static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize) static void Html_tag_open_frameset (DilloHtml *html, const char *tag, int tagsize) { - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); - DW2TB(html->dw)->addText("--FRAME--", S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); + DW2TB(html->dw)->addText("--FRAME--", html->styleEngine->style ()); Html_add_indented(html, 40, 0, 5); } @@ -1872,18 +1859,17 @@ static void Html_tag_open_frameset (DilloHtml *html, */ static void Html_tag_open_h(DilloHtml *html, const char *tag, int tagsize) { - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + CssPropertyList props; - /* TODO: combining these two would be slightly faster */ - a_Html_set_top_font(html, prefs.vw_fontname, - Html_level_to_fontsize(FontSizesNum - (tag[2] - '0')), - 1, 3); - a_Html_tag_set_align_attr (html, tag, tagsize); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); + + a_Html_tag_set_align_attr (html, &props, tag, tagsize); + html->styleEngine->setNonCssHints (&props); /* First finalize unclosed H tags (we test if already named anyway) */ a_Menu_pagemarks_set_text(html->bw, html->Stash->str); a_Menu_pagemarks_add(html->bw, DW2TB(html->dw), - S_TOP(html)->style, (tag[2] - '0')); + html->styleEngine->style (), (tag[2] - '0')); a_Html_stash_init(html); S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_STASH_AND_BODY; @@ -1895,7 +1881,7 @@ static void Html_tag_open_h(DilloHtml *html, const char *tag, int tagsize) static void Html_tag_close_h(DilloHtml *html, int TagIdx) { a_Menu_pagemarks_set_text(html->bw, html->Stash->str); - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); a_Html_pop_tag(html, TagIdx); } @@ -1908,7 +1894,7 @@ static void Html_tag_open_big_small(DilloHtml *html, int level; level = - Html_fontsize_to_level(S_TOP(html)->style->font->size) + + Html_fontsize_to_level(html->styleEngine->style ()->font->size) + ((dStrncasecmp(tag+1, "big", 3)) ? -1 : 1); a_Html_set_top_font(html, NULL, Html_level_to_fontsize(level), 0, 0); } @@ -1918,7 +1904,7 @@ static void Html_tag_open_big_small(DilloHtml *html, */ static void Html_tag_open_br(DilloHtml *html, const char *tag, int tagsize) { - DW2TB(html->dw)->addLinebreak (S_TOP(html)->style); + DW2TB(html->dw)->addLinebreak (html->styleEngine->style ()); } /* @@ -1926,39 +1912,26 @@ static void Html_tag_open_br(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_font(DilloHtml *html, const char *tag, int tagsize) { - StyleAttrs style_attrs; - Style *old_style; /*Font font;*/ const char *attrbuf; int32_t color; - - if (!prefs.force_my_colors) { - old_style = S_TOP(html)->style; - style_attrs = *old_style; - - if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "color"))) { - if (prefs.contrast_visited_color && html->InVisitedLink) { - color = html->visited_color; - } else { - /* use the tag-specified color */ - color = a_Html_color_parse(html, attrbuf, - style_attrs.color->getColor()); - style_attrs.color = Color::createSimple (HT2LT(html), color); - } + CssPropertyList props; + + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "color"))) { + if (prefs.contrast_visited_color && html->InVisitedLink) { + color = html->visited_color; + } else { + /* use the tag-specified color */ + color = a_Html_color_parse(html, attrbuf, -1); } + if (color != -1) + props.set (CssProperty::CSS_PROPERTY_COLOR, color); + } -#if 0 - //if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "face"))) { - // font = *( style_attrs.font ); - // font.name = attrbuf; - // style_attrs.font = a_Dw_style_font_new_from_list (&font); - //} -#endif + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "face"))) + props.set (CssProperty::CSS_PROPERTY_FONT_FAMILY, attrbuf); - S_TOP(html)->style = - Style::create (HT2LT(html), &style_attrs); - old_style->unref (); - } + html->styleEngine->setNonCssHints (&props); } /* @@ -1980,7 +1953,6 @@ static void Html_tag_open_abbr(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_b(DilloHtml *html, const char *tag, int tagsize) { - a_Html_set_top_font(html, NULL, 0, 1, 1); } /* @@ -1988,7 +1960,6 @@ static void Html_tag_open_b(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_strong(DilloHtml *html, const char *tag, int tagsize) { - a_Html_set_top_font(html, NULL, 0, 1, 1); } /* @@ -1996,7 +1967,6 @@ static void Html_tag_open_strong(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_i(DilloHtml *html, const char *tag, int tagsize) { - a_Html_set_top_font(html, NULL, 0, 2, 2); } /* @@ -2004,7 +1974,6 @@ static void Html_tag_open_i(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_em(DilloHtml *html, const char *tag, int tagsize) { - a_Html_set_top_font(html, NULL, 0, 2, 2); } /* @@ -2012,7 +1981,6 @@ static void Html_tag_open_em(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_cite(DilloHtml *html, const char *tag, int tagsize) { - a_Html_set_top_font(html, NULL, 0, 2, 2); } /* @@ -2020,7 +1988,7 @@ static void Html_tag_open_cite(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_center(DilloHtml *html, const char *tag, int tagsize) { - DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (0, html->styleEngine->style ()); HTML_SET_TOP_ATTR(html, textAlign, TEXT_ALIGN_CENTER); } @@ -2030,7 +1998,7 @@ static void Html_tag_open_center(DilloHtml *html, const char *tag, int tagsize) static void Html_tag_open_address(DilloHtml *html, const char *tag, int tagsize) { - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); a_Html_set_top_font(html, NULL, 0, 2, 2); } @@ -2182,15 +2150,15 @@ static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize) usemap_url = a_Html_url_new(html, attrbuf, NULL, 0); /* Set the style attributes for this image */ - style_attrs = *S_TOP(html)->style; - if (S_TOP(html)->style->x_link != -1 || + style_attrs = *html->styleEngine->style (); + if (html->styleEngine->style ()->x_link != -1 || usemap_url != NULL) { /* Images within links */ border = 1; if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "border"))) border = strtol (attrbuf, NULL, 10); - if (S_TOP(html)->style->x_link != -1) { + if (html->styleEngine->style ()->x_link != -1) { /* In this case we can use the text color */ style_attrs.setBorderColor ( Color::createShaded (HT2LT(html), style_attrs.color->getColor())); @@ -2208,7 +2176,7 @@ static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize) if (a_Html_get_attr(html, tag, tagsize, "ismap")) { ((::dw::Image*)Image->dw)->setIsMap(); _MSG(" Html_tag_open_img: server-side map (ISMAP)\n"); - } else if (S_TOP(html)->style->x_link != -1 && + } else if (html->styleEngine->style ()->x_link != -1 && usemap_url == NULL) { /* For simple links, we have to suppress the "image_pressed" signal. * This is overridden for USEMAP images. */ @@ -2390,7 +2358,7 @@ static void Html_tag_open_object(DilloHtml *html, const char *tag, int tagsize) URL_STR(base_url), (base_url != NULL)); dReturn_if_fail ( url != NULL ); - style_attrs = *S_TOP(html)->style; + style_attrs = *html->styleEngine->style (); if (a_Capi_get_flags(url) & CAPI_IsCached) { style_attrs.color = Color::createSimple ( @@ -2398,9 +2366,10 @@ static void Html_tag_open_object(DilloHtml *html, const char *tag, int tagsize) html->visited_color /* a_Color_vc(html->visited_color, - S_TOP(html)->style->color->getColor(), + html->styleEngine->style()->color->getColor(), html->link_color, - S_TOP(html)->style->backgroundColor->getColor()), + html->styleEngine->style()->backgroundColor->getColor() + ); */ ); } else { @@ -2448,7 +2417,7 @@ static const char* Html_get_javascript_link(DilloHtml *html) static void Html_add_anchor(DilloHtml *html, const char *name) { _MSG("Registering ANCHOR: %s\n", name); - if (!DW2TB(html->dw)->addAnchor (name, S_TOP(html)->style)) + if (!DW2TB(html->dw)->addAnchor (name, html->styleEngine->style ())) BUG_MSG("Anchor names must be unique within the document\n"); /* * According to Sec. 12.2.1 of the HTML 4.01 spec, "anchor names that @@ -2465,9 +2434,8 @@ static void Html_add_anchor(DilloHtml *html, const char *name) */ static void Html_tag_open_a(DilloHtml *html, const char *tag, int tagsize) { - StyleAttrs style_attrs; - Style *old_style; DilloUrl *url; + CssPropertyList props; const char *attrbuf; /* TODO: add support for MAP with A HREF */ @@ -2482,36 +2450,20 @@ static void Html_tag_open_a(DilloHtml *html, const char *tag, int tagsize) url = a_Html_url_new(html, attrbuf, NULL, 0); dReturn_if_fail ( url != NULL ); - old_style = S_TOP(html)->style; - style_attrs = *old_style; - if (a_Capi_get_flags(url) & CAPI_IsCached) { html->InVisitedLink = true; - style_attrs.color = Color::createSimple ( - HT2LT(html), - html->visited_color -/* - a_Color_vc(html->visited_color, - S_TOP(html)->style->color->getColor(), - html->link_color, - S_TOP(html)->current_bg_color), -*/ - ); + html->styleEngine->setPseudo ("visited"); + if (html->visited_color != -1) + props.set (CssProperty::CSS_PROPERTY_COLOR, html->visited_color); } else { - style_attrs.color = Color::createSimple(HT2LT(html), - html->link_color); + html->styleEngine->setPseudo ("link"); + if (html->link_color != -1) + props.set (CssProperty::CSS_PROPERTY_COLOR, html->link_color); } -// if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) -// style_attrs.x_tooltip = a_Dw_tooltip_new_no_ref(attrbuf); - - style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE; - style_attrs.x_link = Html_set_new_link(html, &url); - style_attrs.cursor = CURSOR_POINTER; + props.set (CssProperty::PROPERTY_X_LINK, Html_set_new_link(html, &url)); - S_TOP(html)->style = - Style::create (HT2LT(html), &style_attrs); - old_style->unref (); + html->styleEngine->setNonCssHints (&props); } if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) { @@ -2540,12 +2492,12 @@ static void Html_tag_open_u(DilloHtml *html, const char *tag, int tagsize) Style *style; StyleAttrs style_attrs; - style = S_TOP(html)->style; + style = html->styleEngine->style (); style_attrs = *style; style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE; - S_TOP(html)->style = - Style::create (HT2LT(html), &style_attrs); - style->unref (); +// html->styleEngine->style () = +// Style::create (HT2LT(html), &style_attrs); +// style->unref (); } /* @@ -2556,12 +2508,12 @@ static void Html_tag_open_strike(DilloHtml *html, const char *tag, int tagsize) Style *style; StyleAttrs style_attrs; - style = S_TOP(html)->style; + style = html->styleEngine->style (); style_attrs = *style; style_attrs.textDecoration |= TEXT_DECORATION_LINE_THROUGH; - S_TOP(html)->style = - Style::create (HT2LT(html), &style_attrs); - style->unref (); +// html->styleEngine->style () = +// Style::create (HT2LT(html), &style_attrs); +// style->unref (); } /* @@ -2570,7 +2522,7 @@ static void Html_tag_open_strike(DilloHtml *html, const char *tag, int tagsize) static void Html_tag_open_blockquote(DilloHtml *html, const char *tag, int tagsize) { - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); Html_add_indented(html, 40, 40, 9); } @@ -2582,7 +2534,7 @@ static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize) const char *attrbuf; ListStyleType list_style_type; - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); Html_add_indented(html, 40, 0, 9); if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) { @@ -2602,7 +2554,7 @@ static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize) /* --EG :: I changed the behavior here : types are cycling instead of * being forced to square. It's easier for mixed lists level counting. */ - switch (S_TOP(html)->style->listStyleType) { + switch (html->styleEngine->style ()->listStyleType) { case LIST_STYLE_TYPE_DISC: list_style_type = LIST_STYLE_TYPE_CIRCLE; break; @@ -2633,11 +2585,8 @@ static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_dir(DilloHtml *html, const char *tag, int tagsize) { - ListStyleType list_style_type = LIST_STYLE_TYPE_DISC; + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); - Html_add_indented(html, 40, 0, 9); - HTML_SET_TOP_ATTR(html, listStyleType, list_style_type); S_TOP(html)->list_type = HTML_LIST_UNORDERED; S_TOP(html)->list_number = 0; S_TOP(html)->ref_list_item = NULL; @@ -2660,28 +2609,30 @@ static void Html_tag_open_menu(DilloHtml *html, const char *tag, int tagsize) static void Html_tag_open_ol(DilloHtml *html, const char *tag, int tagsize) { const char *attrbuf; - ListStyleType list_style_type; int n = 1; - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); - Html_add_indented(html, 40, 0, 9); - - list_style_type = LIST_STYLE_TYPE_DECIMAL; - if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) { + CssPropertyList props; + ListStyleType listStyleType = LIST_STYLE_TYPE_DECIMAL; + if (*attrbuf == '1') - list_style_type = LIST_STYLE_TYPE_DECIMAL; + listStyleType = LIST_STYLE_TYPE_DECIMAL; else if (*attrbuf == 'a') - list_style_type = LIST_STYLE_TYPE_LOWER_ALPHA; + listStyleType = LIST_STYLE_TYPE_LOWER_ALPHA; else if (*attrbuf == 'A') - list_style_type = LIST_STYLE_TYPE_UPPER_ALPHA; + listStyleType = LIST_STYLE_TYPE_UPPER_ALPHA; else if (*attrbuf == 'i') - list_style_type = LIST_STYLE_TYPE_LOWER_ROMAN; + listStyleType = LIST_STYLE_TYPE_LOWER_ROMAN; else if (*attrbuf == 'I') - list_style_type = LIST_STYLE_TYPE_UPPER_ROMAN; + listStyleType = LIST_STYLE_TYPE_UPPER_ROMAN; + + props.set (CssProperty::CSS_PROPERTY_LIST_STYLE_TYPE, listStyleType); + html->styleEngine->setNonCssHints (&props); } - HTML_SET_TOP_ATTR(html, listStyleType, list_style_type); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); + Html_add_indented(html, 40, 0, 9); + S_TOP(html)->list_type = HTML_LIST_ORDERED; if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "start")) && @@ -2714,7 +2665,7 @@ static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize) ref_list_item = &html->stack->getRef(html->stack->size()-2)->ref_list_item; /* set the item style */ - word_style = S_TOP(html)->style; + word_style = html->styleEngine->style (); style_attrs = *word_style; //style_attrs.backgroundColor = Color::createShaded (HT2LT(html), 0xffff40); //style_attrs.setBorderColor (Color::createSimple (HT2LT(html), 0x000000)); @@ -2740,7 +2691,8 @@ static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize) BUG_MSG("illegal negative LIST VALUE attribute; Starting from 0\n"); *list_number = 0; } - numtostr((*list_number)++, buf, 16, S_TOP(html)->style->listStyleType); + numtostr((*list_number)++, buf, 16, + html->styleEngine->style ()->listStyleType); list_item->initWithText (dStrdup(buf), word_style); list_item->addSpace (word_style); html->PrevWasSPC = true; @@ -2777,7 +2729,7 @@ static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize) const char *attrbuf; int32_t size = 0; - style_attrs = *S_TOP(html)->style; + style_attrs = *html->styleEngine->style (); width_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "width", "100%"); style_attrs.width = a_Html_parse_length (html, width_ptr); @@ -2817,12 +2769,12 @@ static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize) style_attrs.borderWidth.right = size / 2; style = Style::create (HT2LT(html), &style_attrs); - DW2TB(html->dw)->addParbreak (5, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (5, html->styleEngine->style ()); hruler = new Ruler(); hruler->setStyle (style); DW2TB(html->dw)->addWidget (hruler, style); style->unref (); - DW2TB(html->dw)->addParbreak (5, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (5, html->styleEngine->style ()); } /* @@ -2831,7 +2783,7 @@ static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize) static void Html_tag_open_dl(DilloHtml *html, const char *tag, int tagsize) { /* may want to actually do some stuff here. */ - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); } /* @@ -2839,7 +2791,7 @@ static void Html_tag_open_dl(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_dt(DilloHtml *html, const char *tag, int tagsize) { - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); a_Html_set_top_font(html, NULL, 0, 1, 1); } @@ -2848,7 +2800,7 @@ static void Html_tag_open_dt(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_dd(DilloHtml *html, const char *tag, int tagsize) { - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); Html_add_indented(html, 40, 40, 9); } @@ -2857,7 +2809,7 @@ static void Html_tag_open_dd(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_pre(DilloHtml *html, const char *tag, int tagsize) { - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0); /* Is the placement of this statement right? */ @@ -2874,7 +2826,7 @@ static void Html_tag_open_pre(DilloHtml *html, const char *tag, int tagsize) static void Html_tag_close_pre(DilloHtml *html, int TagIdx) { html->InFlags &= ~IN_PRE; - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); a_Html_pop_tag(html, TagIdx); } @@ -2891,7 +2843,7 @@ static int Html_tag_pre_excludes(int tag_idx) /* initialize array */ if (!ei_set[0]) for (i = 0; es_set[i]; ++i) - ei_set[i] = Html_tag_index(es_set[i]); + ei_set[i] = a_Html_tag_index(es_set[i]); for (i = 0; ei_set[i]; ++i) if (tag_idx == ei_set[i]) @@ -3084,8 +3036,11 @@ static void Html_tag_open_sup(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_div(DilloHtml *html, const char *tag, int tagsize) { - DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style); - a_Html_tag_set_align_attr (html, tag, tagsize); + CssPropertyList props; + + DW2TB(html->dw)->addParbreak (0, html->styleEngine->style ()); + a_Html_tag_set_align_attr (html, &props, tag, tagsize); + html->styleEngine->setNonCssHints (&props); } /* @@ -3093,7 +3048,7 @@ static void Html_tag_open_div(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_close_div(DilloHtml *html, int TagIdx) { - DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (0, html->styleEngine->style ()); a_Html_pop_tag(html, TagIdx); } @@ -3110,7 +3065,7 @@ static void Html_tag_close_default(DilloHtml *html, int TagIdx) */ static void Html_tag_close_par(DilloHtml *html, int TagIdx) { - DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->style ()); a_Html_pop_tag(html, TagIdx); } @@ -3262,7 +3217,7 @@ static int Html_tag_compare(const char *p1, const char *p2) * Get 'tag' index * return -1 if tag is not handled yet */ -static int Html_tag_index(const char *tag) +int a_Html_tag_index(const char *tag) { int low, high, mid, cond; @@ -3293,17 +3248,17 @@ static int Html_needs_optional_close(int old_idx, int cur_idx) if (i_P == -1) { /* initialize the indexes of elements with optional close */ - i_P = Html_tag_index("p"), - i_LI = Html_tag_index("li"), - i_TD = Html_tag_index("td"), - i_TR = Html_tag_index("tr"), - i_TH = Html_tag_index("th"), - i_DD = Html_tag_index("dd"), - i_DT = Html_tag_index("dt"), - i_OPTION = Html_tag_index("option"); - // i_THEAD = Html_tag_index("thead"); - // i_TFOOT = Html_tag_index("tfoot"); - // i_COLGROUP = Html_tag_index("colgroup"); + i_P = a_Html_tag_index("p"), + i_LI = a_Html_tag_index("li"), + i_TD = a_Html_tag_index("td"), + i_TR = a_Html_tag_index("tr"), + i_TH = a_Html_tag_index("th"), + i_DD = a_Html_tag_index("dd"), + i_DT = a_Html_tag_index("dt"), + i_OPTION = a_Html_tag_index("option"); + // i_THEAD = a_Html_tag_index("thead"); + // i_TFOOT = a_Html_tag_index("tfoot"); + // i_COLGROUP = a_Html_tag_index("colgroup"); } if (old_idx == i_P || old_idx == i_DT) { @@ -3400,7 +3355,7 @@ static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag) if (!(html->InFlags & IN_HTML)) { tag = "<html>"; - tag_idx = Html_tag_index(tag + 1); + tag_idx = a_Html_tag_index(tag + 1); if (tag_idx != new_idx || IsCloseTag) { /* implicit open */ Html_force_push_tag(html, tag_idx); @@ -3412,7 +3367,7 @@ static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag) /* head element */ if (!(html->InFlags & IN_HEAD)) { tag = "<head>"; - tag_idx = Html_tag_index(tag + 1); + tag_idx = a_Html_tag_index(tag + 1); if (tag_idx != new_idx || IsCloseTag) { /* implicit open of the head element */ Html_force_push_tag(html, tag_idx); @@ -3424,11 +3379,11 @@ static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag) /* body element */ if (html->InFlags & IN_HEAD) { tag = "</head>"; - tag_idx = Html_tag_index(tag + 2); + tag_idx = a_Html_tag_index(tag + 2); Tags[tag_idx].close (html, tag_idx); } tag = "<body>"; - tag_idx = Html_tag_index(tag + 1); + tag_idx = a_Html_tag_index(tag + 1); if (tag_idx != new_idx || IsCloseTag) { /* implicit open */ Html_force_push_tag(html, tag_idx); @@ -3449,7 +3404,7 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize) char *start = tag + 1; /* discard the '<' */ int IsCloseTag = (*start == '/'); - ni = Html_tag_index(start + IsCloseTag); + ni = a_Html_tag_index(start + IsCloseTag); if (ni == -1) { /* TODO: doctype parsing is a bit fuzzy, but enough for the time being */ if (!(html->InFlags & IN_HTML)) { @@ -3483,10 +3438,7 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize) /* Push the tag into the stack */ Html_push_tag(html, ni); - /* Call the open function for this tag */ - Tags[ni].open (html, tag, tagsize); - if (html->stop_parser) - break; + html->styleEngine->startElement (ni); /* Now parse attributes that can appear on any tag */ if (tagsize >= 8 && /* length of "<t id=i>" */ @@ -3498,6 +3450,9 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize) * spec states in Sec. 7.5.2 that anchor ids are case-sensitive. * So we don't do it and hope for better specs in the future ... */ + if (attrbuf) + html->styleEngine->setId (attrbuf); + Html_check_name_val(html, attrbuf, "id"); /* We compare the "id" value with the url-decoded "name" value */ if (!html->NameVal || strcmp(html->NameVal, attrbuf)) { @@ -3513,6 +3468,25 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize) html->NameVal = NULL; } + if (tagsize >= 10) { /* length of "<t class=i>" */ + attrbuf = Html_get_attr2(html, tag, tagsize, "class", + HTML_LeftTrim | HTML_RightTrim); + if (attrbuf) + html->styleEngine->setClass (attrbuf); + } + + if (tagsize >= 11) { /* length of "<t style=i>" */ + attrbuf = Html_get_attr2(html, tag, tagsize, "style", + HTML_LeftTrim | HTML_RightTrim); + if (attrbuf) + html->styleEngine->setStyle (attrbuf); + } + + /* Call the open function for this tag */ + Tags[ni].open (html, tag, tagsize); + if (html->stop_parser) + break; + /* let the parser know this was an open tag */ html->PrevWasOpenTag = true; @@ -3535,6 +3509,7 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize) (size_t)tagsize == strlen(Tags[ni].name) + 3))) { /* <x/> */ Tags[ni].close (html, ni); +// html->styleEngine->endElement (ni); /* This was a close tag */ html->PrevWasOpenTag = false; html->ReqTagClose = false; diff --git a/src/html_common.hh b/src/html_common.hh index c694123e..3468ba89 100644 --- a/src/html_common.hh +++ b/src/html_common.hh @@ -13,6 +13,8 @@ #include "form.hh" +#include "styleengine.hh" + /* * Macros */ @@ -36,6 +38,7 @@ * Change one toplevel attribute. var should be an identifier. val is * only evaluated once, so you can safely use a function call for it. */ +#if 0 #define HTML_SET_TOP_ATTR(html, var, val) \ do { \ StyleAttrs style_attrs; \ @@ -48,7 +51,9 @@ Style::create (HT2LT(html), &style_attrs); \ old_style->unref (); \ } while (FALSE) - +#else +#define HTML_SET_TOP_ATTR(html, var, val) +#endif /* * Typedefs */ @@ -109,7 +114,7 @@ struct _DilloLinkImage { }; struct _DilloHtmlState { - dw::core::style::Style *style, *table_cell_style; + CssPropertyList *table_cell_props; DilloHtmlParseMode parse_mode; DilloHtmlTableMode table_mode; bool cell_text_align_set; @@ -174,6 +179,7 @@ public: //BUG: for now everything is public float DocTypeVersion; /* HTML or XHTML version number */ lout::misc::SimpleVector<DilloHtmlState> *stack; + StyleEngine *styleEngine; int InFlags; /* tracks which elements we are in */ @@ -233,6 +239,8 @@ public: * Parser functions */ +int a_Html_tag_index(const char *tag); + const char *a_Html_get_attr(DilloHtml *html, const char *tag, int tagsize, @@ -260,11 +268,11 @@ int32_t a_Html_color_parse(DilloHtml *html, const char *subtag, int32_t default_color); dw::core::style::Length a_Html_parse_length (DilloHtml *html, const char *attr); -void a_Html_tag_set_align_attr(DilloHtml *html, +void a_Html_tag_set_align_attr(DilloHtml *html, CssPropertyList *props, const char *tag, int tagsize); bool a_Html_tag_set_valign_attr(DilloHtml *html, const char *tag, int tagsize, - dw::core::style::StyleAttrs *style_attrs); + CssPropertyList *props); void a_Html_set_top_font(DilloHtml *html, const char *name, int size, int BI, int BImask); diff --git a/src/styleengine.cc b/src/styleengine.cc new file mode 100644 index 00000000..7b151469 --- /dev/null +++ b/src/styleengine.cc @@ -0,0 +1,348 @@ +/* + * File: styleengine.cc + * + * Copyright 2008 Jorge Arellano Cid <jcid@dillo.org> + * + * 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. + */ + +#include <stdio.h> +#include <math.h> +#include "../dlib/dlib.h" +#include "styleengine.hh" + +using namespace dw::core::style; + +StyleEngine::StyleEngine (dw::core::Layout *layout) { + StyleAttrs style_attrs; + FontAttrs font_attrs; + + stack = new lout::misc::SimpleVector <Node> (1); + cssContext = new CssContext (); + this->layout = layout; + + stack->increase (); + Node *n = stack->getRef (stack->size () - 1); + + /* Create a dummy font, attribute, and tag for the bottom of the stack. */ + font_attrs.name = "helvetica"; + font_attrs.size = 12; + font_attrs.weight = 400; + font_attrs.style = FONT_STYLE_NORMAL; + + style_attrs.initValues (); + style_attrs.font = Font::create (layout, &font_attrs); + style_attrs.color = Color::createSimple (layout, 0); + style_attrs.backgroundColor = Color::createSimple (layout, 0xffffff); + + n->style = Style::create (layout, &style_attrs); +} + +StyleEngine::~StyleEngine () { + delete stack; + delete cssContext; +} + +/** + * \brief tell the styleEngine that a new html element has started. + */ +void StyleEngine::startElement (int element) { +// fprintf(stderr, "===> START %d\n", element); + + if (stack->getRef (stack->size () - 1)->style == NULL) + style0 (); + + stack->increase (); + Node *n = stack->getRef (stack->size () - 1); + n->style = NULL; + n->depth = stack->size (); + n->element = element; + n->id = NULL; + n->klass = NULL; + n->pseudo = NULL; + n->styleAttribute = NULL; +} + +void StyleEngine::setId (const char *id) { + Node *n = stack->getRef (stack->size () - 1); + assert (n->id == NULL); + n->id = dStrdup (id); +}; + +void StyleEngine::setClass (const char *klass) { + Node *n = stack->getRef (stack->size () - 1); + assert (n->klass == NULL); + n->klass = dStrdup (klass); +}; + +void StyleEngine::setStyle (const char *style) { + Node *n = stack->getRef (stack->size () - 1); + assert (n->styleAttribute == NULL); + n->styleAttribute = dStrdup (style); +}; + +/** + * \brief set properties that were definded using (mostly deprecated) HTML + * attributes (e.g. bgColor). + */ +void StyleEngine::setNonCssHints (CssPropertyList *nonCssHints) { + if (stack->getRef (stack->size () - 1)->style) + stack->getRef (stack->size () - 1)->style->unref (); + style0 (nonCssHints); // evaluate now, so caller can free nonCssHints +} + +/** + * \brief set the CSS pseudo class (e.g. "link", "visited"). + */ +void StyleEngine::setPseudo (const char *pseudo) { + Node *n = stack->getRef (stack->size () - 1); + assert (n->pseudo == NULL); + n->pseudo = dStrdup (pseudo); +} + +/** + * \brief tell the styleEngine that a html element has ended. + */ +void StyleEngine::endElement (int element) { +// fprintf(stderr, "===> END %d\n", element); + assert (stack->size () > 1); + assert (element == stack->getRef (stack->size () - 1)->element); + + Node *n = stack->getRef (stack->size () - 1); + + if (n->style) + n->style->unref (); + if (n->id) + dFree ((void*) n->id); + if (n->klass) + dFree ((void*) n->klass); + if (n->styleAttribute) + dFree ((void*) n->styleAttribute); + + stack->setSize (stack->size () - 1); +} + +/** + * \brief Make changes to StyleAttrs attrs according to CssPropertyList props. + */ +void StyleEngine::apply (StyleAttrs *attrs, CssPropertyList *props) { + FontAttrs fontAttrs = *attrs->font; + Font *parentFont; + + /* Determine font first so it can be used to resolve relative lenths. + * \todo Things should be rearranged so that just one pass is necessary. + */ + for (int i = 0; i < props->size (); i++) { + CssProperty *p = props->getRef (i); + + switch (p->name) { + case CssProperty::CSS_PROPERTY_FONT_FAMILY: + fontAttrs.name = p->value.strVal; + break; + case CssProperty::CSS_PROPERTY_FONT_SIZE: + parentFont = stack->get (stack->size () - 2).style->font; + if (CSS_LENGTH_TYPE (p->value.intVal) == CSS_LENGTH_TYPE_PERCENTAGE) + fontAttrs.size = (int) (CSS_LENGTH_VALUE (p->value.intVal) * + parentFont->size); + else + fontAttrs.size = computeValue (p->value.intVal, parentFont); + break; + case CssProperty::CSS_PROPERTY_FONT_STYLE: + fontAttrs.style = (FontStyle) p->value.intVal; + break; + case CssProperty::CSS_PROPERTY_FONT_WEIGHT: + switch (p->value.intVal) { + case CssProperty::CSS_FONT_WEIGHT_LIGHTER: + fontAttrs.weight -= CssProperty::CSS_FONT_WEIGHT_STEP; + break; + case CssProperty::CSS_FONT_WEIGHT_BOLDER: + fontAttrs.weight += CssProperty::CSS_FONT_WEIGHT_STEP; + break; + default: + fontAttrs.weight = p->value.intVal; + break; + } + if (fontAttrs.weight < CssProperty::CSS_FONT_WEIGHT_MIN) + fontAttrs.weight = CssProperty::CSS_FONT_WEIGHT_MIN; + if (fontAttrs.weight > CssProperty::CSS_FONT_WEIGHT_MAX) + fontAttrs.weight = CssProperty::CSS_FONT_WEIGHT_MAX; + break; + default: + break; + } + } + + attrs->font = Font::create (layout, &fontAttrs); + + for (int i = 0; i < props->size (); i++) { + CssProperty *p = props->getRef (i); + + switch (p->name) { + /* \todo missing cases */ + case CssProperty::CSS_PROPERTY_BACKGROUND_COLOR: + attrs->backgroundColor = + Color::createSimple (layout, p->value.intVal); + break; + case CssProperty::CSS_PROPERTY_BORDER_TOP_COLOR: + attrs->borderColor.top = + Color::createSimple (layout, p->value.intVal); + break; + case CssProperty::CSS_PROPERTY_BORDER_BOTTOM_COLOR: + attrs->borderColor.bottom = + Color::createSimple (layout, p->value.intVal); + break; + case CssProperty::CSS_PROPERTY_BORDER_LEFT_COLOR: + attrs->borderColor.left = + Color::createSimple (layout, p->value.intVal); + break; + case CssProperty::CSS_PROPERTY_BORDER_RIGHT_COLOR: + attrs->borderColor.right = + Color::createSimple (layout, p->value.intVal); + break; + case CssProperty::CSS_PROPERTY_BORDER_BOTTOM_STYLE: + attrs->borderStyle.bottom = (BorderStyle) p->value.intVal; + break; + case CssProperty::CSS_PROPERTY_BORDER_LEFT_STYLE: + attrs->borderStyle.left = (BorderStyle) p->value.intVal; + break; + case CssProperty::CSS_PROPERTY_BORDER_RIGHT_STYLE: + attrs->borderStyle.right = (BorderStyle) p->value.intVal; + break; + case CssProperty::CSS_PROPERTY_BORDER_TOP_STYLE: + attrs->borderStyle.top = (BorderStyle) p->value.intVal; + break; + case CssProperty::CSS_PROPERTY_BORDER_BOTTOM_WIDTH: + attrs->borderWidth.bottom = computeValue (p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_BORDER_LEFT_WIDTH: + attrs->borderWidth.left = computeValue (p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_BORDER_RIGHT_WIDTH: + attrs->borderWidth.right = computeValue (p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_BORDER_TOP_WIDTH: + attrs->borderWidth.top = computeValue (p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_BORDER_SPACING: + attrs->hBorderSpacing = computeValue (p->value.intVal, attrs->font); + attrs->vBorderSpacing = attrs->hBorderSpacing; + break; + case CssProperty::CSS_PROPERTY_COLOR: + attrs->color = Color::createSimple (layout, p->value.intVal); + break; + case CssProperty::CSS_PROPERTY_CURSOR: + attrs->cursor = (Cursor) p->value.intVal; + break; + case CssProperty::CSS_PROPERTY_LIST_STYLE_TYPE: + attrs->listStyleType = (ListStyleType) p->value.intVal; + break; + case CssProperty::CSS_PROPERTY_MARGIN_BOTTOM: + attrs->margin.bottom = computeValue (p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_MARGIN_LEFT: + attrs->margin.left = computeValue (p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_MARGIN_RIGHT: + attrs->margin.right = computeValue (p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_MARGIN_TOP: + attrs->margin.top = computeValue (p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_PADDING_TOP: + attrs->padding.top = computeValue (p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_PADDING_BOTTOM: + attrs->padding.bottom = computeValue (p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_PADDING_LEFT: + attrs->padding.left = computeValue (p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_PADDING_RIGHT: + attrs->padding.right = computeValue (p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_TEXT_ALIGN: + attrs->textAlign = (TextAlignType) p->value.intVal; + break; + case CssProperty::CSS_PROPERTY_TEXT_DECORATION: + attrs->textDecoration |= p->value.intVal; + break; + case CssProperty::CSS_PROPERTY_VERTICAL_ALIGN: + attrs->valign = (VAlignType) p->value.intVal; + break; + /* \todo proper conversion from CssLength to dw::core::style::Length */ + case CssProperty::CSS_PROPERTY_WIDTH: + attrs->width = p->value.intVal; + break; + case CssProperty::CSS_PROPERTY_HEIGHT: + attrs->height = p->value.intVal; + break; + case CssProperty::PROPERTY_X_LINK: + attrs->x_link = p->value.intVal; + break; + case CssProperty::PROPERTY_X_IMG: + attrs->x_img = p->value.intVal; + break; + + default: + break; + } + } +} + +/** + * \brief Resolve relative lengths to absolute values. + */ + +int StyleEngine::computeValue (CssLength value, Font *font) { + int ret; + static float dpmm; + + if (dpmm == 0.0) + dpmm = layout->dpiX () / 25.4; /* assume dpiX == dpiY */ + + switch (CSS_LENGTH_TYPE (value)) { + case CSS_LENGTH_TYPE_PX: + ret = (int) CSS_LENGTH_VALUE (value); + break; + case CSS_LENGTH_TYPE_MM: + ret = (int) (CSS_LENGTH_VALUE (value) * dpmm); + break; + case CSS_LENGTH_TYPE_EM: + ret = (int) (CSS_LENGTH_VALUE (value) * font->size); + break; + case CSS_LENGTH_TYPE_EX: + ret = (int) (CSS_LENGTH_VALUE(value) * font->xHeight); + break; + default: + ret = value; + break; + } + return ret; +} + + +/** + * \brief Create a new style object based on the previously opened / closed + * HTML elements and the nonCssProperties that have been set. + * This method is private. Call style() to get a current style object. + */ +Style * StyleEngine::style0 (CssPropertyList *nonCssProperties) { + CssPropertyList props; + CssPropertyList *tagStyleProps = NULL; /** \todo implementation */ + + // get previous style from the stack + StyleAttrs attrs = *stack->getRef (stack->size () - 2)->style; + // reset values that are not inherited according to CSS + attrs.resetValues (); + + cssContext->apply (&props, this, tagStyleProps, nonCssProperties); + + apply (&attrs, &props); + + stack->getRef (stack->size () - 1)->style = Style::create (layout, &attrs); + + return stack->getRef (stack->size () - 1)->style; +} diff --git a/src/styleengine.hh b/src/styleengine.hh new file mode 100644 index 00000000..60b044b8 --- /dev/null +++ b/src/styleengine.hh @@ -0,0 +1,57 @@ +#ifndef __STYLEENGINE_HH__ +#define __STYLEENGINE_HH__ + +#include "dw/core.hh" +#include "doctree.hh" +#include "css.hh" + +class StyleEngine : public Doctree { + private: + class Node : public DoctreeNode { + public: + dw::core::style::Style *style; + const char *styleAttribute; + }; + + dw::core::Layout *layout; + lout::misc::SimpleVector <Node> *stack; + CssContext *cssContext; + + dw::core::style::Style *style0 (CssPropertyList *nonCssHints = NULL); + void apply (dw::core::style::StyleAttrs *attrs, CssPropertyList *props); + int computeValue (CssLength value, dw::core::style::Font *font); + + public: + StyleEngine (dw::core::Layout *layout); + ~StyleEngine (); + + /* Doctree interface */ + inline const DoctreeNode *top () { + return stack->getRef (stack->size () - 1); + }; + + inline const DoctreeNode *parent (const DoctreeNode *n) { + if (n->depth > 0) + return stack->getRef (n->depth - 1); + else + return NULL; + }; + + void startElement (int tag); + void setId (const char *id); + void setClass (const char *klass); + void setStyle (const char *style); + void endElement (int tag); + void setPseudo (const char *pseudo); + void setNonCssHints (CssPropertyList *nonCssHints); + + inline dw::core::style::Style *style () { + dw::core::style::Style *s = stack->getRef (stack->size () - 1)->style; + if (s) + return s; + else + return style0 (); + }; +}; + +#endif diff --git a/src/table.cc b/src/table.cc index a72411d6..994dcbbd 100644 --- a/src/table.cc +++ b/src/table.cc @@ -18,6 +18,7 @@ #include "prefs.h" #include "msg.h" +#include "css.hh" /* Undefine if you want to unroll tables. For instance for PDAs */ #define USE_TABLES @@ -43,13 +44,13 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize) { #ifdef USE_TABLES dw::core::Widget *table; - dw::core::style::StyleAttrs style_attrs; - dw::core::style::Style *cell_style, *old_style; + CssPropertyList props, *table_cell_props; const char *attrbuf; - int32_t border = 0, cellspacing = 1, cellpadding = 2, bgcolor; + int32_t border = -1, cellspacing = -1, cellpadding = -1, bgcolor = -1; + int cssLength; #endif - DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (0, html->styleEngine->style ()); #ifdef USE_TABLES if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "border"))) @@ -59,68 +60,100 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize) if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cellpadding"))) cellpadding = strtol (attrbuf, NULL, 10); - /* The style for the table */ - style_attrs = *S_TOP(html)->style; + if (border != -1) { + cssLength = CSS_CREATE_LENGTH (border, CSS_LENGTH_TYPE_PX); + props.set (CssProperty::CSS_PROPERTY_BORDER_TOP_WIDTH, cssLength); + props.set (CssProperty::CSS_PROPERTY_BORDER_BOTTOM_WIDTH, cssLength); + props.set (CssProperty::CSS_PROPERTY_BORDER_LEFT_WIDTH, cssLength); + props.set (CssProperty::CSS_PROPERTY_BORDER_RIGHT_WIDTH, cssLength); + } + + if (cellspacing != -1) { + cssLength = CSS_CREATE_LENGTH (cellspacing, CSS_LENGTH_TYPE_PX); + props.set (CssProperty::CSS_PROPERTY_BORDER_SPACING, cssLength); + } /* When dillo was started with the --debug-rendering option, there * is always a border around the table. */ - if (dillo_dbg_rendering) - style_attrs.borderWidth.setVal (MIN (border, 1)); - else - style_attrs.borderWidth.setVal (border); - - style_attrs.setBorderColor ( - Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color)); - style_attrs.setBorderStyle (BORDER_OUTSET); - style_attrs.hBorderSpacing = cellspacing; - style_attrs.vBorderSpacing = cellspacing; + if (dillo_dbg_rendering && border < 1) { + cssLength = CSS_CREATE_LENGTH (1, CSS_LENGTH_TYPE_PX); + props.set (CssProperty::CSS_PROPERTY_BORDER_TOP_WIDTH, cssLength); + props.set (CssProperty::CSS_PROPERTY_BORDER_BOTTOM_WIDTH, cssLength); + props.set (CssProperty::CSS_PROPERTY_BORDER_LEFT_WIDTH, cssLength); + props.set (CssProperty::CSS_PROPERTY_BORDER_RIGHT_WIDTH, cssLength); + } if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "width"))) - style_attrs.width = a_Html_parse_length (html, attrbuf); + props.set (CssProperty::CSS_PROPERTY_WIDTH, + a_Html_parse_length (html, attrbuf)); if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "align"))) { if (dStrcasecmp (attrbuf, "left") == 0) - style_attrs.textAlign = dw::core::style::TEXT_ALIGN_LEFT; + props.set (CssProperty::CSS_PROPERTY_TEXT_ALIGN, TEXT_ALIGN_LEFT); else if (dStrcasecmp (attrbuf, "right") == 0) - style_attrs.textAlign = dw::core::style::TEXT_ALIGN_RIGHT; + props.set (CssProperty::CSS_PROPERTY_TEXT_ALIGN, TEXT_ALIGN_RIGHT); else if (dStrcasecmp (attrbuf, "center") == 0) - style_attrs.textAlign = dw::core::style::TEXT_ALIGN_CENTER; + props.set (CssProperty::CSS_PROPERTY_TEXT_ALIGN, TEXT_ALIGN_CENTER); } - if (!prefs.force_my_colors && - (attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) { + /** \todo figure out how to handle shaded colors with CSS */ + props.set (CssProperty::CSS_PROPERTY_BORDER_TOP_COLOR, 0x000000); + props.set (CssProperty::CSS_PROPERTY_BORDER_BOTTOM_COLOR, 0x000000); + props.set (CssProperty::CSS_PROPERTY_BORDER_LEFT_COLOR, 0x000000); + props.set (CssProperty::CSS_PROPERTY_BORDER_RIGHT_COLOR, 0x000000); + + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) { bgcolor = a_Html_color_parse(html, attrbuf, -1); if (bgcolor != -1) { if (bgcolor == 0xffffff && !prefs.allow_white_bg) bgcolor = prefs.bg_color; S_TOP(html)->current_bg_color = bgcolor; - style_attrs.backgroundColor = - Color::createShaded (HT2LT(html), bgcolor); + props.set (CssProperty::CSS_PROPERTY_BACKGROUND_COLOR, bgcolor); } } + html->styleEngine->setNonCssHints (&props); + /* The style for the cells */ - cell_style = Style::create (HT2LT(html), &style_attrs); - style_attrs = *S_TOP(html)->style; - /* When dillo was started with the --debug-rendering option, there - * is always a border around the cells. */ - if (dillo_dbg_rendering) - style_attrs.borderWidth.setVal (1); - else - style_attrs.borderWidth.setVal (border ? 1 : 0); - style_attrs.padding.setVal(cellpadding); - style_attrs.setBorderColor (cell_style->borderColor.top); - style_attrs.setBorderStyle (BORDER_INSET); - - old_style = S_TOP(html)->table_cell_style; - S_TOP(html)->table_cell_style = - Style::create (HT2LT(html), &style_attrs); - if (old_style) - old_style->unref (); + table_cell_props = new CssPropertyList (); + if (border != -1) { + cssLength = CSS_CREATE_LENGTH (border, CSS_LENGTH_TYPE_PX); + table_cell_props->set (CssProperty::CSS_PROPERTY_BORDER_TOP_WIDTH, cssLength); + table_cell_props->set (CssProperty::CSS_PROPERTY_BORDER_BOTTOM_WIDTH, cssLength); + table_cell_props->set (CssProperty::CSS_PROPERTY_BORDER_LEFT_WIDTH, cssLength); + table_cell_props->set (CssProperty::CSS_PROPERTY_BORDER_RIGHT_WIDTH, cssLength); + } + + if (dillo_dbg_rendering && border < 1) { + cssLength = CSS_CREATE_LENGTH (1, CSS_LENGTH_TYPE_PX); + table_cell_props->set (CssProperty::CSS_PROPERTY_BORDER_TOP_WIDTH, cssLength); + table_cell_props->set (CssProperty::CSS_PROPERTY_BORDER_BOTTOM_WIDTH, cssLength); + table_cell_props->set (CssProperty::CSS_PROPERTY_BORDER_LEFT_WIDTH, cssLength); + table_cell_props->set (CssProperty::CSS_PROPERTY_BORDER_RIGHT_WIDTH, cssLength); + } + + if (cellpadding != -1) { + cssLength = CSS_CREATE_LENGTH (cellpadding, CSS_LENGTH_TYPE_PX); + table_cell_props->set (CssProperty::CSS_PROPERTY_PADDING_TOP, cssLength); + table_cell_props->set (CssProperty::CSS_PROPERTY_PADDING_BOTTOM, cssLength); + table_cell_props->set (CssProperty::CSS_PROPERTY_PADDING_LEFT, cssLength); + table_cell_props->set (CssProperty::CSS_PROPERTY_PADDING_RIGHT, cssLength); + } + + /** \todo figure out how to handle shaded colors with CSS */ + table_cell_props->set (CssProperty::CSS_PROPERTY_BORDER_TOP_COLOR, 0x000000); + table_cell_props->set (CssProperty::CSS_PROPERTY_BORDER_BOTTOM_COLOR, 0x000000); + table_cell_props->set (CssProperty::CSS_PROPERTY_BORDER_LEFT_COLOR, 0x000000); + table_cell_props->set (CssProperty::CSS_PROPERTY_BORDER_RIGHT_COLOR, 0x000000); + + if (S_TOP(html)->table_cell_props) + S_TOP(html)->table_cell_props->unref (); + + S_TOP(html)->table_cell_props = table_cell_props; + S_TOP(html)->table_cell_props->ref (); table = new dw::Table(prefs.limit_text_width); - DW2TB(html->dw)->addWidget (table, cell_style); - cell_style->unref (); + DW2TB(html->dw)->addWidget (table, html->styleEngine->style ()); S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TOP; S_TOP(html)->cell_text_align_set = FALSE; @@ -134,10 +167,9 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize) void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize) { const char *attrbuf; - dw::core::style::StyleAttrs style_attrs; - dw::core::style::Style *style, *old_style; int32_t bgcolor = -1; bool new_style = false; + CssPropertyList props, *table_cell_props; #ifdef USE_TABLES switch (S_TOP(html)->table_mode) { @@ -148,44 +180,40 @@ void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize) case DILLO_HTML_TABLE_MODE_TOP: case DILLO_HTML_TABLE_MODE_TR: case DILLO_HTML_TABLE_MODE_TD: - style = NULL; - if (!prefs.force_my_colors && - (attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) { + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) { bgcolor = a_Html_color_parse(html, attrbuf, -1); if (bgcolor != -1) { if (bgcolor == 0xffffff && !prefs.allow_white_bg) bgcolor = prefs.bg_color; - - style_attrs = *S_TOP(html)->style; - style_attrs.backgroundColor = - Color::createShaded (HT2LT(html), bgcolor); - style = Style::create (HT2LT(html), &style_attrs); + props.set (CssProperty::CSS_PROPERTY_BACKGROUND_COLOR, bgcolor); S_TOP(html)->current_bg_color = bgcolor; } } - ((dw::Table*)S_TOP(html)->table)->addRow (style); - if (style) - style->unref (); + html->styleEngine->setNonCssHints (&props); + + ((dw::Table*)S_TOP(html)->table)->addRow (html->styleEngine->style ()); if (a_Html_get_attr (html, tag, tagsize, "align")) { S_TOP(html)->cell_text_align_set = TRUE; - a_Html_tag_set_align_attr (html, tag, tagsize); + a_Html_tag_set_align_attr (html, &props, tag, tagsize); + html->styleEngine->setNonCssHints (&props); } - style_attrs = *S_TOP(html)->table_cell_style; + table_cell_props = new CssPropertyList (*S_TOP(html)->table_cell_props); if (bgcolor != -1) { - style_attrs.backgroundColor =Color::createShaded(HT2LT(html),bgcolor); + table_cell_props->set (CssProperty::CSS_PROPERTY_BACKGROUND_COLOR, bgcolor); new_style = true; } - if (a_Html_tag_set_valign_attr (html, tag, tagsize, &style_attrs)) + if (a_Html_tag_set_valign_attr (html, tag, tagsize, table_cell_props)) new_style = true; if (new_style) { - old_style = S_TOP(html)->table_cell_style; - S_TOP(html)->table_cell_style = - Style::create (HT2LT(html), &style_attrs); - old_style->unref (); + S_TOP(html)->table_cell_props->unref (); + S_TOP(html)->table_cell_props = table_cell_props; + S_TOP(html)->table_cell_props->ref (); + } else { + delete table_cell_props; } break; default: @@ -194,7 +222,7 @@ void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize) S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TR; #else - DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style); + DW2TB(html->dw)->addParbreak (0, html->styleEngine->style ()); #endif } @@ -232,10 +260,9 @@ static void Html_tag_open_table_cell(DilloHtml *html, Widget *col_tb; int colspan = 1, rowspan = 1; const char *attrbuf; - dw::core::style::StyleAttrs style_attrs; - dw::core::style::Style *style, *old_style; int32_t bgcolor; bool_t new_style; + CssPropertyList props (*S_TOP(html)->table_cell_props); switch (S_TOP(html)->table_mode) { case DILLO_HTML_TABLE_MODE_NONE: @@ -259,47 +286,38 @@ static void Html_tag_open_table_cell(DilloHtml *html, rowspan = MAX(1, strtol (attrbuf, NULL, 10)); /* text style */ - old_style = S_TOP(html)->style; - style_attrs = *old_style; - if (!S_TOP(html)->cell_text_align_set) - style_attrs.textAlign = text_align; + if (!S_TOP(html)->cell_text_align_set) { + props.set (CssProperty::CSS_PROPERTY_TEXT_ALIGN, text_align); + } if (a_Html_get_attr(html, tag, tagsize, "nowrap")) - style_attrs.whiteSpace = WHITE_SPACE_NOWRAP; + props.set (CssProperty::CSS_PROPERTY_WHITE_SPACE, WHITE_SPACE_NOWRAP); else - style_attrs.whiteSpace = WHITE_SPACE_NORMAL; - - S_TOP(html)->style = - Style::create (HT2LT(html), &style_attrs); - old_style->unref (); - a_Html_tag_set_align_attr (html, tag, tagsize); + props.set (CssProperty::CSS_PROPERTY_WHITE_SPACE, WHITE_SPACE_NORMAL); - /* cell style */ - style_attrs = *S_TOP(html)->table_cell_style; - new_style = FALSE; + a_Html_tag_set_align_attr (html, &props, tag, tagsize); if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "width"))) { - style_attrs.width = a_Html_parse_length (html, attrbuf); - new_style = TRUE; + props.set (CssProperty::CSS_PROPERTY_WIDTH, + a_Html_parse_length (html, attrbuf)); } - if (a_Html_tag_set_valign_attr (html, tag, tagsize, &style_attrs)) + if (a_Html_tag_set_valign_attr (html, tag, tagsize, &props)) new_style = TRUE; - if (!prefs.force_my_colors && - (attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) { + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) { bgcolor = a_Html_color_parse(html, attrbuf, -1); if (bgcolor != -1) { if (bgcolor == 0xffffff && !prefs.allow_white_bg) bgcolor = prefs.bg_color; - new_style = TRUE; - style_attrs.backgroundColor = - Color::createShaded (HT2LT(html), bgcolor); + props.set (CssProperty::CSS_PROPERTY_BACKGROUND_COLOR, bgcolor); S_TOP(html)->current_bg_color = bgcolor; } } - if (S_TOP(html)->style->textAlign + html->styleEngine->setNonCssHints (&props); + + if (html->styleEngine->style ()->textAlign == TEXT_ALIGN_STRING) col_tb = new dw::TableCell ( ((dw::Table*)S_TOP(html)->table)->getCellRef (), @@ -307,18 +325,14 @@ static void Html_tag_open_table_cell(DilloHtml *html, else col_tb = new Textblock (prefs.limit_text_width); - if (new_style) { - style = dw::core::style::Style::create (HT2LT(html), &style_attrs); - col_tb->setStyle (style); - style->unref (); - } else - col_tb->setStyle (S_TOP(html)->table_cell_style); + col_tb->setStyle (html->styleEngine->style ()); ((dw::Table*)S_TOP(html)->table)->addCell (col_tb, colspan, rowspan); S_TOP(html)->textblock = html->dw = col_tb; /* Handle it when the user clicks on a link */ html->connectSignals(col_tb); + break; default: |