diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 7 | ||||
-rw-r--r-- | src/colors.c | 9 | ||||
-rw-r--r-- | src/css.cc | 340 | ||||
-rw-r--r-- | src/css.hh | 313 | ||||
-rw-r--r-- | src/cssparser.cc | 1163 | ||||
-rw-r--r-- | src/cssparser.hh | 63 | ||||
-rw-r--r-- | src/doctree.hh | 20 | ||||
-rw-r--r-- | src/form.cc | 31 | ||||
-rw-r--r-- | src/html.cc | 848 | ||||
-rw-r--r-- | src/html_common.hh | 21 | ||||
-rw-r--r-- | src/nav.c | 1 | ||||
-rw-r--r-- | src/plain.cc | 4 | ||||
-rw-r--r-- | src/styleengine.cc | 424 | ||||
-rw-r--r-- | src/styleengine.hh | 75 | ||||
-rw-r--r-- | src/table.cc | 188 | ||||
-rw-r--r-- | src/web.cc | 4 |
16 files changed, 2965 insertions, 546 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 8da631ea..e87c8d74 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -67,6 +67,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/colors.c b/src/colors.c index 9a4c8c8e..6a684dde 100644 --- a/src/colors.c +++ b/src/colors.c @@ -204,7 +204,7 @@ static const struct key { #define NCOLORS (sizeof(color_keyword) / sizeof(struct key)) /* - * Parse a color in hex (RRGGBB) + * Parse a color in hex (RRGGBB) or (RGB) * * Return Value: * parsed color if successful (err = 0), @@ -219,7 +219,12 @@ static int32_t Color_parse_hex (const char *s, int32_t default_color, int *err) ret_color = strtol(s, &tail, 16); if (tail - s == 6) *err = 0; - else + else if (tail - s == 3) { /* #RGB as allowed by CSS */ + *err = 0; + ret_color = ((ret_color & 0xf00) << 12) | ((ret_color & 0xf00) << 8) | + ((ret_color & 0x0f0) << 8) | ((ret_color & 0x0f0) << 4) | + ((ret_color & 0x00f) << 4) | ((ret_color & 0x00f) << 0); + } else ret_color = default_color; return ret_color; diff --git a/src/css.cc b/src/css.cc new file mode 100644 index 00000000..4982bc4b --- /dev/null +++ b/src/css.cc @@ -0,0 +1,340 @@ +/* + * 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; + selectorList = new lout::misc::SimpleVector + <struct CombinatorAndSelector> (1); + selectorList->increase (); + top ()->element = element; + top ()->klass = klass; + top ()->pseudo = pseudo; + top ()->id = id; +}; + +CssSelector::~CssSelector () { + delete selectorList; +} + +bool CssSelector::match (Doctree *docTree) { + CssSimpleSelector *sel; + Combinator comb; + const DoctreeNode *node = docTree->top (); + + assert (selectorList->size () > 0); + + sel = top (); + + if (! sel->match (node)) + return false; + + for (int i = selectorList->size () - 2; i >= 0; i--) { + sel = &selectorList->getRef (i)->selector; + comb = selectorList->getRef (i + 1)->combinator; + node = docTree->parent (node); + + if (node == NULL) + return false; + + switch (comb) { + case CHILD: + if (!sel->match (node)) + return false; + break; + case DESCENDENT: + while (!sel->match (node)) { + node = docTree->parent (node); + if (node == NULL) + return false; + } + break; + default: + return false; // \todo implement other combinators + } + } + + return true; +} + +void CssSelector::addSimpleSelector (Combinator c, int element, + const char *klass, const char *pseudo, + const char *id) { + selectorList->increase (); + + selectorList->getRef (selectorList->size () - 1)->combinator = c; + top ()->element = element; + top ()->klass = klass; + top ()->pseudo = pseudo; + top ()->id = id; + +} + +void CssSelector::print () { + for (int i = 0; i < selectorList->size () - 1; i++) { + selectorList->getRef (i)->selector.print (); + + if (i < selectorList->size () - 2) { + switch (selectorList->getRef (i)->combinator) { + case CHILD: + fprintf (stderr, ">"); + break; + case DESCENDENT: + fprintf (stderr, " "); + break; + default: + fprintf (stderr, "?"); + break; + } + } + } + + top ()->print (); + fprintf (stderr, "\n"); +} + +bool CssSimpleSelector::match (const DoctreeNode *n) { + if (element != ELEMENT_ANY && 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 CssSimpleSelector::print () { + fprintf (stderr, "Element %d, class %s, pseudo %s, id %s ", + element, klass, pseudo, id); +} + +CssRule::CssRule (CssSelector *selector, CssPropertyList *props) { + refCount = 0; + this->selector = selector; + this->selector->ref (); + this->props = props; + this->props->ref (); +}; + +CssRule::~CssRule () { + selector->unref (); + props->unref (); +}; + +void CssRule::apply (CssPropertyList *props, Doctree *docTree) { + if (selector->match (docTree)) + this->props->apply (props); +} + +void CssRule::print () { + selector->print (); + props->print (); +} + +CssStyleSheet::CssStyleSheet () { + for (int i = 0; i < ntags; i++) + ruleTable[i] = new lout::misc::SimpleVector <CssRule*> (1); +} + +CssStyleSheet::~CssStyleSheet () { + for (int i = 0; i < ntags; i++) { + for (int j = 0; j < ruleTable[i]->size (); j++) + ruleTable[i]->get (j)->unref (); + + delete ruleTable[i]; + } +} + +void CssStyleSheet::addRule (CssRule *rule) { + int topElement = rule->selector->top ()->element; + + if (topElement == CssSimpleSelector::ELEMENT_ANY) { + for (int i = 0; i < ntags; i++) { + ruleTable[i]->increase (); + *ruleTable[i]->getRef (ruleTable[i]->size () - 1) = rule; + rule->ref (); + } + } else if (topElement >= 0 && topElement < ntags) { + ruleTable[topElement]->increase (); + *ruleTable[topElement]->getRef (ruleTable[topElement]->size()-1) = rule; + rule->ref (); + } +} + +void CssStyleSheet::addRule (CssSelector *selector, CssPropertyList *props) { + CssRule *rule = new CssRule (selector, props); + addRule (rule); +} + +void CssStyleSheet::apply (CssPropertyList *props, Doctree *docTree) { + lout::misc::SimpleVector <CssRule*> *ruleList; + + ruleList = ruleTable[docTree->top ()->element]; + for (int i = 0; i < ruleList->size (); i++) + ruleList->get (i)->apply (props, docTree); +} + +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 }" + "blockquote, dd {margin-left: 40px; margin-right: 40px}" + "center { text-align: center }" + ":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, address {font-style: italic; } " + "img:link, img:visited {border: 1px solid } " + "frameset, ul, ol, dir {margin-left: 40px} " + "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;} " + "hr {width: 100%; border: 1px inset} " + "pre {white-space: pre} " + "u {text-decoration: underline } " + "small, sub, sup { font-size: 0.83em } " + "sub { vertical-align: sub } " + "sup { vertical-align: super } " + "s, strike, del { text-decoration: line-through }" + "table {border-style: outset; border-spacing: 1px} " + "td {border-style: inset; padding: 2px;} " + "thead, tbody, tfoot { vertical-align: middle }" + "th { font-weight: bolder; text-align: center }" + "code, tt, pre, samp, kbd {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..515831e9 --- /dev/null +++ b/src/css.hh @@ -0,0 +1,313 @@ +#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_RELATIVE, /* This does not exist in CSS but + is used in HTML */ + 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_INDENT, + 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_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; } +}; + +class CssSimpleSelector { + public: + enum { + ELEMENT_NONE = -1, + ELEMENT_ANY = -2, + }; + + int element; + const char *klass, *pseudo, *id; + + bool match (const DoctreeNode *node); + void print (); +}; + +/** + * \brief CSS selector class. + * \todo Implement missing selector options. + */ +class CssSelector { + public: + typedef enum { + DESCENDENT, + CHILD, + ADJACENT_SIBLING, + } Combinator; + + private: + struct CombinatorAndSelector { + Combinator combinator; + CssSimpleSelector selector; + }; + + int refCount; + lout::misc::SimpleVector <struct CombinatorAndSelector> *selectorList; + + public: + CssSelector (int element = CssSimpleSelector::ELEMENT_ANY, + const char *klass = NULL, + const char *pseudo = NULL, const char *id = NULL); + ~CssSelector (); + void addSimpleSelector (Combinator c, + int element = CssSimpleSelector::ELEMENT_ANY, + const char *klass = NULL, + const char *pseudo = NULL, const char *id=NULL); + inline CssSimpleSelector *top () { + return &selectorList->getRef (selectorList->size () - 1)->selector; + }; + + 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: + int refCount; + CssPropertyList *props; + + public: + CssSelector *selector; + + CssRule (CssSelector *selector, CssPropertyList *props); + ~CssRule (); + + void apply (CssPropertyList *props, Doctree *docTree); + inline void ref () { refCount++; } + inline void unref () { if(--refCount == 0) delete this; } + void print (); +}; + +/** + * \brief A list of CssRules. + * In apply () all matching rules are applied. + */ +class CssStyleSheet { + private: + static const int ntags = 90; // \todo replace 90 + lout::misc::SimpleVector <CssRule*> *ruleTable[ntags]; + + public: + CssStyleSheet(); + ~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..6bea06cb --- /dev/null +++ b/src/cssparser.cc @@ -0,0 +1,1163 @@ +#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-indent", CSS_TYPE_UNUSED, NULL}, + {"text-shadow", CSS_TYPE_UNUSED, NULL}, + {"text-transform", CSS_TYPE_UNUSED, NULL}, + {"top", CSS_TYPE_UNUSED, NULL}, + {"unicode-bidi", CSS_TYPE_UNUSED, NULL}, + {"vertical-align", CSS_TYPE_ENUM, Css_vertical_align_vals}, + {"visibility", CSS_TYPE_UNUSED, NULL}, + {"white-space", CSS_TYPE_ENUM, Css_white_space_vals}, + {"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: + * must have length 12 + * 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 +}; + +CssProperty::Name Css_border_properties[12] = { + CssProperty::CSS_PROPERTY_BORDER_TOP_WIDTH, + CssProperty::CSS_PROPERTY_BORDER_TOP_STYLE, + CssProperty::CSS_PROPERTY_BORDER_TOP_COLOR, + CssProperty::CSS_PROPERTY_BORDER_BOTTOM_WIDTH, + CssProperty::CSS_PROPERTY_BORDER_BOTTOM_STYLE, + CssProperty::CSS_PROPERTY_BORDER_BOTTOM_COLOR, + CssProperty::CSS_PROPERTY_BORDER_LEFT_WIDTH, + CssProperty::CSS_PROPERTY_BORDER_LEFT_STYLE, + CssProperty::CSS_PROPERTY_BORDER_LEFT_COLOR, + CssProperty::CSS_PROPERTY_BORDER_RIGHT_WIDTH, + CssProperty::CSS_PROPERTY_BORDER_RIGHT_STYLE, + CssProperty::CSS_PROPERTY_BORDER_RIGHT_COLOR +}; + +static CssShorthandInfo Css_shorthand_info[CSS_SHORTHAND_NUM] = { + {"background", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE, + Css_background_properties}, + {"border", CssShorthandInfo::CSS_SHORTHAND_BORDER, + Css_border_properties}, + {"border-bottom", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE, + Css_border_bottom_properties}, + {"border-color", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS, + Css_border_color_properties}, + {"border-left", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE, + Css_border_left_properties}, + {"border-right", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE, + Css_border_right_properties}, + {"border-style", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS, + Css_border_style_properties}, + {"border-top", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE, + Css_border_top_properties}, + {"border-width", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS, + Css_border_width_properties}, + {"font", CssShorthandInfo::CSS_SHORTHAND_FONT, 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; + bool space_separated; /* used when parsing CSS selectors */ +} 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; + + parser->space_separated = false; + + c = Css_getc(parser); + + while (true) { + if (isspace(c)) { // ignore whitespace + parser->space_separated = true; + c = Css_getc(parser); + } else if (c == '/') { // ignore comments + d = Css_getc(parser); + if (d == '*') { + c = Css_getc(parser); + d = Css_getc(parser); + while (d != EOF && (c != '*' || d != '/')) { + c = d; + d = Css_getc(parser); + } + c = Css_getc(parser); + } else { + Css_ungetc(parser); + break; + } + } else { + break; + } + } + + 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 (c != EOF && (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, j, n; + int dir_set[4][4] = { + /* 1 value */ {0, 0, 0, 0}, + /* 2 values */ {0, 0, 1, 1}, + /* 3 values */ {0, 2, 1, 1}, + /* 4 values */ {0, 2, 3, 1} + }; + + if (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: + do { + for (found = false, i = 0; + !found && i < 3; + i++) + if (Css_token_matches_property(parser, + Css_shorthand_info + [sh_index]. + properties[i])) { + found = true; + if (Css_parse_value(parser, + Css_shorthand_info[sh_index] + .properties[i], &val)) { + weight = Css_parse_weight(parser); + for (j = 0; j < 4; j++) + if (weight) + importantProps-> + set(Css_shorthand_info[sh_index]. + properties[j * 3 + i], val); + else + props->set(Css_shorthand_info[sh_index]. + properties[j * 3 + i], val); + } + } + } while (found); + 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 bool Css_parse_simple_selector(CssParser * parser, + CssSimpleSelector *selector) { + const char *p, **pp; + + if (parser->ttype == CSS_TK_SYMBOL) { + selector->element = a_Html_tag_index(parser->tval); + Css_next_token(parser); + if (parser->space_separated) + return true; + } else if (parser->ttype == CSS_TK_CHAR && parser->tval[0] == '*') { + selector->element = CssSimpleSelector::ELEMENT_ANY; + Css_next_token(parser); + if (parser->space_separated) + return true; + } else if (parser->ttype == CSS_TK_CHAR && parser->tval[0] != '#' && + parser->tval[0] != '.' && parser->tval[0] != ':') { + return false; + } + + 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->space_separated) + return true; + + 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); + } + if (parser->space_separated) + return true; + } + } while (pp); + + DEBUG_MSG(DEBUG_PARSE_LEVEL, "end of simple selector (%s, %s, %s, %d)\n", + selector->id, selector->klass, + selector->pseudo, selector->element); + + return true; +} + +static CssSelector *Css_parse_selector(CssParser * parser) { + CssSelector *selector = new CssSelector (); + + while (true) { + if (! Css_parse_simple_selector (parser, selector->top ())) + Css_next_token(parser); /* make sure we advance at least one token */ + + if (parser->ttype == CSS_TK_CHAR && + (parser->tval[0] == ',' || parser->tval[0] == '{')) { + return selector; + } else if (parser->ttype == CSS_TK_CHAR && parser->tval[0] == '>') { + selector->addSimpleSelector (CssSelector::CHILD); + } else if (parser->ttype != CSS_TK_END && parser->space_separated) { + selector->addSimpleSelector (CssSelector::DESCENDENT); + } else { + delete selector; + return NULL; + } + } + + return selector; +} + +static void Css_parse_ruleset(CssParser * parser) +{ + lout::misc::SimpleVector < CssSelector * >*list; + CssPropertyList *props, *importantProps; + CssSelector *selector; + + list = new lout::misc::SimpleVector < CssSelector * >(1); + + while (true) { + selector = Css_parse_selector(parser); + + if (selector) { + selector->ref(); + 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; + parser.space_separated = false; + + Css_next_token(&parser); + while (parser.ttype != CSS_TK_END) + Css_parse_ruleset(&parser); +} diff --git a/src/cssparser.hh b/src/cssparser.hh new file mode 100644 index 00000000..6638496d --- /dev/null +++ b/src/cssparser.hh @@ -0,0 +1,63 @@ +#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); + +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 76f2c32b..c442c017 100644 --- a/src/form.cc +++ b/src/form.cc @@ -287,7 +287,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->wordStyle ()); if (html->InFlags & IN_FORM) { BUG_MSG("nested forms\n"); @@ -545,10 +545,10 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize) if (prefs.standard_widget_colors) bg = NULL; else - bg = Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color); + bg = Color::create (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); @@ -581,10 +581,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->wordStyle ()); 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->wordStyle ()); ResourceFactory *factory = HT2LT(html)->getResourceFactory(); EntryResource *entryResource = factory->createEntryResource (20, false); @@ -595,9 +595,9 @@ void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize) if (prefs.standard_widget_colors) bg = NULL; else - bg = Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color); + bg = Color::create (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; @@ -663,10 +663,10 @@ void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize) if (prefs.standard_widget_colors) bg = NULL; else - bg = Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color); + bg = Color::create (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); } @@ -747,7 +747,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"))) @@ -866,12 +866,12 @@ 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); style_attrs.backgroundColor = - Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color); + Color::create (HT2LT(html), S_TOP(html)->current_bg_color); style = Style::create (HT2LT(html), &style_attrs); page = new Textblock (prefs.limit_text_width); @@ -1917,14 +1917,13 @@ 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); + style::Color::create (HT2LT(html), S_TOP(html)->current_bg_color); /* create new image and add it to the button */ - if ((Image = a_Html_add_new_image(html, tag, tagsize, url, &style_attrs, - false))) { + if ((Image = a_Html_add_new_image(html, tag, tagsize, url, false))) { Style *style = Style::create (HT2LT(html), &style_attrs); IM2DW(Image)->setStyle (style); ResourceFactory *factory = HT2LT(html)->getResourceFactory(); diff --git a/src/html.cc b/src/html.cc index 1c3652ba..a03cc38d 100644 --- a/src/html.cc +++ b/src/html.cc @@ -106,14 +106,10 @@ static const char *Html_get_attr2(DilloHtml *html, int tagsize, const char *attrname, int tag_parsing_flags); -static void Html_add_widget(DilloHtml *html, Widget *widget, - char *width_str, char *height_str, - StyleAttrs *style_attrs); 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); /*----------------------------------------------------------------------------- @@ -305,9 +301,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) @@ -319,6 +316,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 } /* @@ -326,25 +324,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 @@ -358,8 +357,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); } } @@ -368,19 +367,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; @@ -388,55 +390,23 @@ bool a_Html_tag_set_valign_attr(DilloHtml *html, const char *tag, /* - * Add a new DwPage into the current DwPage, for indentation. - * left and right are the horizontal indentation amounts, space is the - * vertical space around the block. + * Create and add a new Textblock to the current Textblock */ -static void Html_add_indented_widget(DilloHtml *html, Widget *textblock, - int left, int right, int space) +static void Html_add_textblock(DilloHtml *html, int space) { - StyleAttrs style_attrs; - Style *style; - - style_attrs = *S_TOP(html)->style; - - style_attrs.margin.setVal (0); - style_attrs.borderWidth.setVal (0); - style_attrs.padding.setVal(0); - - /* Activate this for debugging */ -#if 0 - style_attrs.borderWidth.setVal (1); - style_attrs.setBorderColor ( - Color::createShaded (HT2LT(html), style_attrs.color->getColor()); - style_attrs.setBorderStyle (BORDER_DASHED); -#endif - - style_attrs.margin.left = left; - style_attrs.margin.right = right; - style = Style::create (HT2LT(html), &style_attrs); + Textblock *textblock = new Textblock (prefs.limit_text_width); - DW2TB(html->dw)->addParbreak (space, style); - DW2TB(html->dw)->addWidget (textblock, style); - DW2TB(html->dw)->addParbreak (space, style); + DW2TB(html->dw)->addParbreak (space, html->styleEngine->wordStyle ()); + DW2TB(html->dw)->addWidget (textblock, html->styleEngine->style ()); + DW2TB(html->dw)->addParbreak (space, html->styleEngine->wordStyle ()); S_TOP(html)->textblock = html->dw = textblock; S_TOP(html)->hand_over_break = true; - style->unref (); /* Handle it when the user clicks on a link */ html->connectSignals(textblock); } /* - * Create and add a new indented DwPage to the current DwPage - */ -static void Html_add_indented(DilloHtml *html, int left, int right, int space) -{ - Textblock *textblock = new Textblock (prefs.limit_text_width); - Html_add_indented_widget (html, textblock, left, right, space); -} - -/* * Given a font_size, this will return the correct 'level'. * (or the closest, if the exact level isn't found). */ @@ -497,6 +467,9 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url, a_Misc_parse_content_type(content_type, NULL, NULL, &charset); stop_parser = false; + stop_parser_after_head = false; + repush_after_head = false; + repush_after_stylesheet = false; CurrTagOfs = 0; OldTagOfs = 0; @@ -507,8 +480,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; @@ -521,6 +493,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(""); @@ -548,8 +522,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(); @@ -562,26 +536,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); @@ -627,6 +587,8 @@ DilloHtml::~DilloHtml() } delete (images); + delete styleEngine; + //a_Dw_image_map_list_free(&maps); } @@ -681,7 +643,6 @@ int DilloHtml::getCurTagLineNumber() */ void DilloHtml::freeParseData() { - (stack->getRef(0)->style)->unref (); /* template style */ delete(stack); dStr_free(Stash, TRUE); @@ -1137,11 +1098,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->wordStyle ()); dFree(spc); spaceCnt = 0; } - DW2TB(html->dw)->addLinebreak (S_TOP(html)->style); + DW2TB(html->dw)->addLinebreak (html->styleEngine->wordStyle ()); html->pre_column = 0; } html->PreFirstChar = false; @@ -1169,7 +1130,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->wordStyle ()); dFree(spc); } @@ -1177,7 +1138,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->wordStyle ()); html->PrevWasSPC = true; } @@ -1230,7 +1191,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->wordStyle ()); Pword[i] = ch; html->pre_column += i - start; html->PreFirstChar = false; @@ -1240,7 +1201,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->wordStyle ()); } else { /* Collapse white-space entities inside the word (except ) */ Pword = a_Html_parse_entities(html, word, size); @@ -1248,7 +1209,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->wordStyle ()); dFree(Pword); } } @@ -1284,7 +1245,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; } @@ -1303,10 +1264,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; } @@ -1316,6 +1275,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); } @@ -1326,9 +1286,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); @@ -1403,10 +1363,10 @@ static void Html_tag_cleanup_at_close(DilloHtml *html, int TagIdx) /* * Used by a_Html_parse_length */ -static Length Html_parse_length_or_multi_length (const char *attr, - char **endptr) +static CssLength Html_parse_length_or_multi_length (const char *attr, + char **endptr) { - Length l; + CssLength l; double v; char *end; @@ -1414,12 +1374,12 @@ static Length Html_parse_length_or_multi_length (const char *attr, switch (*end) { case '%': end++; - l = createPerLength (v / 100); + l = CSS_CREATE_LENGTH (v / 100, CSS_LENGTH_TYPE_PERCENTAGE); break; case '*': end++; - l = createRelLength (v); + l = CSS_CREATE_LENGTH (v, CSS_LENGTH_TYPE_RELATIVE); break; /* The "px" suffix seems not allowed by HTML4.01 SPEC. @@ -1428,7 +1388,7 @@ static Length Html_parse_length_or_multi_length (const char *attr, end += 2; */ default: - l = createAbsLength ((int)v); + l = CSS_CREATE_LENGTH (v, CSS_LENGTH_TYPE_PX); break; } @@ -1442,24 +1402,24 @@ static Length Html_parse_length_or_multi_length (const char *attr, * Returns a length or a percentage, or UNDEF_LENGTH in case * of an error, or if attr is NULL. */ -Length a_Html_parse_length (DilloHtml *html, const char *attr) +CssLength a_Html_parse_length (DilloHtml *html, const char *attr) { - Length l; + CssLength l; char *end; l = Html_parse_length_or_multi_length (attr, &end); - if (isRelLength (l)) + if (CSS_LENGTH_TYPE (l) == CSS_LENGTH_TYPE_RELATIVE) /* not allowed as &Length; */ - return LENGTH_AUTO; + l = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO); else { /* allow only whitespaces */ if (*end && !isspace (*end)) { BUG_MSG("Garbage after length: %s\n", attr); - return LENGTH_AUTO; + l = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO); } } - _MSG("a_Html_parse_length: \"%s\" %d\n", attr, absLengthVal(l)); + _MSG("a_Html_parse_length: \"%s\" %d\n", attr, CSS_LENGTH_VALUE(l)); return l; } @@ -1636,14 +1596,22 @@ static void Html_tag_open_head(DilloHtml *html, const char *tag, int tagsize) * Handle close HEAD element * Note: as a side effect of Html_test_section() this function is called * twice when the head element is closed implicitly. + * Note2: HEAD is parsed once completely got. This asserts that a + * linked stylesheet will always arrive after HEAD contents. */ static void Html_tag_close_head(DilloHtml *html, int TagIdx) { if (html->InFlags & IN_HEAD) { + MSG("Closing HEAD section\n"); if (html->Num_TITLE == 0) BUG_MSG("HEAD section lacks the TITLE element\n"); - + html->InFlags &= ~IN_HEAD; + + if (html->stop_parser_after_head) + html->stop_parser = true; + if (html->repush_after_head) + a_Nav_repush(html->bw); } } @@ -1693,11 +1661,27 @@ static void Html_tag_close_script(DilloHtml *html, int TagIdx) /* * Handle open STYLE - * store the contents to the stash where (in the future) the style - * sheet interpreter can get it. + * Store contents in the stash where the style sheet interpreter can get it. */ static void Html_tag_open_style(DilloHtml *html, const char *tag, int tagsize) { + const char *attrbuf; + + if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) { + BUG_MSG("type attribute is required for <style>\n"); + } else if (dStrcasecmp(attrbuf, "text/css")) { + MSG("Shouldn't be applying <style type=\"%s\">\n", attrbuf); + /* We need to inform close_style() */ + } + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "media")) && + dStrcasecmp(attrbuf, "all") && !dStristr(attrbuf, "screen")) { + /* HTML 4.01 sec. 6.13 says that media descriptors are case-sensitive, + * but sec. 14.2.3 says that the attribute is case-insensitive. + * TODO can be a comma-separated list. + * TODO handheld. + */ + MSG("Shouldn't be applying <style media=\"%s\">\n", attrbuf); + } a_Html_stash_init(html); S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM; } @@ -1707,7 +1691,8 @@ static void Html_tag_open_style(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_close_style(DilloHtml *html, int TagIdx) { - /* eventually the stash will be sent to an interpreter for parsing */ + html->styleEngine->parse(html->Stash->str, html->Stash->len, + 0, CSS_ORIGIN_AUTHOR); } /* @@ -1717,8 +1702,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)) @@ -1736,43 +1720,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; } @@ -1794,13 +1772,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->wordStyle ()); } - a_Html_tag_set_align_attr (html, tag, tagsize); + a_Html_tag_set_align_attr (html, &props, tag, tagsize); + html->styleEngine->setNonCssHints (&props); } /* @@ -1828,27 +1809,27 @@ 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 = - Color::createSimple (HT2LT(html), html->visited_color); + Color::create (HT2LT(html), html->visited_color); } else { /* unvisited frame */ - style_attrs.color = Color::createSimple (HT2LT(html), html->link_color); + style_attrs.color = Color::create (HT2LT(html), html->link_color); } style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE; style_attrs.x_link = Html_set_new_link(html, &url); 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->wordStyle ()); /* 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->wordStyle ()); if (tolower(tag[1]) == 'i') { /* IFRAME usually comes with very long advertising/spying URLS, @@ -1865,7 +1846,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->wordStyle ()); link_style->unref (); dFree(src); @@ -1879,9 +1860,9 @@ 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); - Html_add_indented(html, 40, 0, 5); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->wordStyle ()); + DW2TB(html->dw)->addText("--FRAME--", html->styleEngine->wordStyle ()); + Html_add_textblock(html, 5); } /* @@ -1889,18 +1870,19 @@ 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; + + + html->styleEngine->inheritBackgroundColor (); + a_Html_tag_set_align_attr (html, &props, tag, tagsize); + html->styleEngine->setNonCssHints (&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->wordStyle ()); /* 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; @@ -1912,7 +1894,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->wordStyle ()); } /* @@ -1921,20 +1903,21 @@ static void Html_tag_close_h(DilloHtml *html, int TagIdx) static void Html_tag_open_big_small(DilloHtml *html, const char *tag, int tagsize) { - int level; +} - level = - Html_fontsize_to_level(S_TOP(html)->style->font->size) + - ((dStrncasecmp(tag+1, "big", 3)) ? -1 : 1); - a_Html_set_top_font(html, NULL, Html_level_to_fontsize(level), 0, 0); +static void Html_tag_open_span(DilloHtml *html, + const char *tag, int tagsize) +{ + html->styleEngine->inheritBackgroundColor(); } + /* * <BR> */ 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->wordStyle ()); } /* @@ -1942,39 +1925,27 @@ 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 +// \todo reenable font face handling when font selection is implemented +// 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); } /* @@ -1996,7 +1967,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); } /* @@ -2004,7 +1974,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); } /* @@ -2012,7 +1981,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); } /* @@ -2020,7 +1988,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); } /* @@ -2028,7 +1995,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); } /* @@ -2036,8 +2002,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); - HTML_SET_TOP_ATTR(html, textAlign, TEXT_ALIGN_CENTER); + DW2TB(html->dw)->addParbreak (0, html->styleEngine->wordStyle ()); } /* @@ -2046,8 +2011,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); - a_Html_set_top_font(html, NULL, 0, 2, 2); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->wordStyle ()); } /* @@ -2055,7 +2019,6 @@ static void Html_tag_open_address(DilloHtml *html, */ static void Html_tag_open_tt(DilloHtml *html, const char *tag, int tagsize) { - a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0); } /* @@ -2064,7 +2027,6 @@ static void Html_tag_open_tt(DilloHtml *html, const char *tag, int tagsize) */ DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag, int tagsize, DilloUrl *url, - dw::core::style::StyleAttrs *style_attrs, bool add) { const int MAX_W = 6000, MAX_H = 6000; @@ -2073,8 +2035,9 @@ DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag, char *width_ptr, *height_ptr, *alt_ptr; const char *attrbuf; Length l_w, l_h; - int space, w = 0, h = 0; + int space, border, w = 0, h = 0; bool load_now; + CssPropertyList props; // if (prefs.show_tooltip && // (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) @@ -2091,17 +2054,24 @@ DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag, // TODO: the same for percentage and relative lengths. if (width_ptr) { l_w = a_Html_parse_length (html, width_ptr); - w = isAbsLength(l_w) ? absLengthVal(l_w) : 0; + w = (int) CSS_LENGTH_TYPE(l_w) == CSS_LENGTH_TYPE_PX ? + CSS_LENGTH_VALUE(l_w) : 0; } if (height_ptr) { l_h = a_Html_parse_length (html, height_ptr); - h = isAbsLength(l_h) ? absLengthVal(l_h) : 0; + h = (int) CSS_LENGTH_TYPE(l_h) == CSS_LENGTH_TYPE_PX ? + CSS_LENGTH_VALUE(l_h) : 0; } if (w < 0 || h < 0 || abs(w*h) > MAX_W * MAX_H) { dFree(width_ptr); dFree(height_ptr); width_ptr = height_ptr = NULL; MSG("a_Html_add_new_image: suspicious image size request %dx%d\n", w, h); + } else { + if (width_ptr) + props.set (CssProperty::CSS_PROPERTY_WIDTH, l_w); + if (height_ptr) + props.set (CssProperty::CSS_PROPERTY_HEIGHT, l_h); } /* TODO: we should scale the image respecting its ratio. @@ -2114,27 +2084,52 @@ DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag, /* Spacing to the left and right */ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "hspace"))) { space = strtol(attrbuf, NULL, 10); - if (space > 0) - style_attrs->margin.left = style_attrs->margin.right = space; + if (space > 0) { + space = CSS_CREATE_LENGTH(space, CSS_LENGTH_TYPE_PX); + props.set (CssProperty::CSS_PROPERTY_MARGIN_LEFT, space); + props.set (CssProperty::CSS_PROPERTY_MARGIN_RIGHT, space); + } } /* Spacing at the top and bottom */ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "vspace"))) { space = strtol(attrbuf, NULL, 10); - if (space > 0) - style_attrs->margin.top = style_attrs->margin.bottom = space; + if (space > 0) { + space = CSS_CREATE_LENGTH(space, CSS_LENGTH_TYPE_PX); + props.set (CssProperty::CSS_PROPERTY_MARGIN_TOP, space); + props.set (CssProperty::CSS_PROPERTY_MARGIN_BOTTOM, space); + } + } + + /* Border */ + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "border"))) { + border = strtol(attrbuf, NULL, 10); + if (border >= 0) { + border = CSS_CREATE_LENGTH(border, CSS_LENGTH_TYPE_PX); + props.set (CssProperty::CSS_PROPERTY_BORDER_TOP_WIDTH, border); + props.set (CssProperty::CSS_PROPERTY_BORDER_BOTTOM_WIDTH, border); + props.set (CssProperty::CSS_PROPERTY_BORDER_LEFT_WIDTH, border); + props.set (CssProperty::CSS_PROPERTY_BORDER_RIGHT_WIDTH, border); + + props.set (CssProperty::CSS_PROPERTY_BORDER_TOP_STYLE, BORDER_SOLID); + props.set (CssProperty::CSS_PROPERTY_BORDER_BOTTOM_STYLE, + BORDER_SOLID); + props.set (CssProperty::CSS_PROPERTY_BORDER_LEFT_STYLE, BORDER_SOLID); + props.set (CssProperty::CSS_PROPERTY_BORDER_RIGHT_STYLE,BORDER_SOLID); + } } /* x_img is an index to a list of {url,image} pairs. * We know Html_add_new_linkimage() will use size() as its next index */ - style_attrs->x_img = html->images->size(); + props.set (CssProperty::PROPERTY_X_IMG, html->images->size()); + + html->styleEngine->setNonCssHints(&props); /* Add a new image widget to this page */ Image = a_Image_new(0, 0, alt_ptr, S_TOP(html)->current_bg_color); - if (add) { - Html_add_widget(html, (Widget*)Image->dw, width_ptr, height_ptr, - style_attrs); - } + if (add) + DW2TB(html->dw)->addWidget((Widget*)Image->dw, + html->styleEngine->style()); load_now = a_UIcmd_get_images_enabled(html->bw) || (a_Capi_get_flags(url) & CAPI_IsCached); @@ -2178,9 +2173,7 @@ static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize) DilloImage *Image; DilloUrl *url, *usemap_url; Textblock *textblock; - StyleAttrs style_attrs; const char *attrbuf; - int border; /* This avoids loading images. Useful for viewing suspicious HTML email. */ if (URL_FLAGS(html->base_url) & URL_SpamSafe) @@ -2197,34 +2190,13 @@ static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize) /* TODO: usemap URLs outside of the document are not used. */ 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 || - 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) { - /* In this case we can use the text color */ - style_attrs.setBorderColor ( - Color::createShaded (HT2LT(html), style_attrs.color->getColor())); - } else { - style_attrs.setBorderColor ( - Color::createShaded (HT2LT(html), html->link_color)); - } - style_attrs.setBorderStyle (BORDER_SOLID); - style_attrs.borderWidth.setVal (border); - } - - Image = a_Html_add_new_image(html, tag, tagsize, url, &style_attrs, true); + Image = a_Html_add_new_image(html, tag, tagsize, url, true); /* Image maps */ 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. */ @@ -2405,22 +2377,22 @@ 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 ( + style_attrs.color = Color::create ( HT2LT(html), 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 { - style_attrs.color = Color::createSimple(HT2LT(html), - html->link_color); + style_attrs.color = Color::create (HT2LT(html), html->link_color); } style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE; @@ -2463,7 +2435,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 @@ -2480,9 +2452,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 */ @@ -2497,38 +2468,24 @@ 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->setPseudoVisited (); + 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->setPseudoLink (); + 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); + props.set (CssProperty::PROPERTY_X_LINK, Html_set_new_link(html, &url)); - style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE; - style_attrs.x_link = Html_set_new_link(html, &url); - style_attrs.cursor = CURSOR_POINTER; - - S_TOP(html)->style = - Style::create (HT2LT(html), &style_attrs); - old_style->unref (); + html->styleEngine->setNonCssHints (&props); } + html->styleEngine->inheritBackgroundColor (); + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) { if (prefs.show_extra_warnings) Html_check_name_val(html, attrbuf, "name"); @@ -2551,15 +2508,6 @@ static void Html_tag_close_a(DilloHtml *html, int TagIdx) */ static void Html_tag_open_u(DilloHtml *html, const char *tag, int tagsize) { - Style *style; - StyleAttrs style_attrs; - - style = S_TOP(html)->style; - style_attrs = *style; - style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE; - S_TOP(html)->style = - Style::create (HT2LT(html), &style_attrs); - style->unref (); } /* @@ -2567,15 +2515,6 @@ static void Html_tag_open_u(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_strike(DilloHtml *html, const char *tag, int tagsize) { - Style *style; - StyleAttrs style_attrs; - - style = S_TOP(html)->style; - style_attrs = *style; - style_attrs.textDecoration |= TEXT_DECORATION_LINE_THROUGH; - S_TOP(html)->style = - Style::create (HT2LT(html), &style_attrs); - style->unref (); } /* @@ -2584,8 +2523,8 @@ 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); - Html_add_indented(html, 40, 40, 9); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->wordStyle ()); + Html_add_textblock(html, 9); } /* @@ -2599,7 +2538,7 @@ static void Html_tag_open_q(DilloHtml *html, const char *tag, int tagsize) */ const char *U201C = "\xe2\x80\x9c"; - DW2TB(html->dw)->addText (U201C, S_TOP(html)->style); + DW2TB(html->dw)->addText (U201C, html->styleEngine->wordStyle ()); } /* @@ -2610,7 +2549,7 @@ static void Html_tag_close_q(DilloHtml *html, int TagIdx) /* Right Double Quotation Mark */ const char *U201D = "\xe2\x80\x9d"; - DW2TB(html->dw)->addText (U201D, S_TOP(html)->style); + DW2TB(html->dw)->addText (U201D, html->styleEngine->wordStyle ()); } /* @@ -2621,8 +2560,8 @@ 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); - Html_add_indented(html, 40, 0, 9); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->wordStyle ()); + Html_add_textblock(html, 9); if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) { /* list_style_type explicitly defined */ @@ -2641,7 +2580,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; @@ -2672,11 +2611,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->wordStyle ()); - 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; @@ -2699,28 +2635,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->wordStyle ()); + Html_add_textblock(html, 9); + S_TOP(html)->list_type = HTML_LIST_ORDERED; if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "start")) && @@ -2753,7 +2691,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->wordStyle (); style_attrs = *word_style; //style_attrs.backgroundColor = Color::createShaded (HT2LT(html), 0xffff40); //style_attrs.setBorderColor (Color::createSimple (HT2LT(html), 0x000000)); @@ -2779,7 +2717,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; @@ -2809,58 +2748,50 @@ static void Html_tag_close_li(DilloHtml *html, int TagIdx) static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize) { Widget *hruler; - StyleAttrs style_attrs; - Style *style; + CssPropertyList props; char *width_ptr; const char *attrbuf; int32_t size = 0; - style_attrs = *S_TOP(html)->style; - - width_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "width", "100%"); - style_attrs.width = a_Html_parse_length (html, width_ptr); - dFree(width_ptr); + width_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "width", NULL); + if (width_ptr) { + props.set (CssProperty::CSS_PROPERTY_WIDTH, + a_Html_parse_length (html, width_ptr)); + dFree(width_ptr); + } if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "size"))) size = strtol(attrbuf, NULL, 10); - - if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "align"))) { - if (dStrcasecmp (attrbuf, "left") == 0) - style_attrs.textAlign = TEXT_ALIGN_LEFT; - else if (dStrcasecmp (attrbuf, "right") == 0) - style_attrs.textAlign = TEXT_ALIGN_RIGHT; - else if (dStrcasecmp (attrbuf, "center") == 0) - style_attrs.textAlign = TEXT_ALIGN_CENTER; - } + + a_Html_tag_set_align_attr(html, &props, tag, tagsize); /* TODO: evaluate attribute */ if (a_Html_get_attr(html, tag, tagsize, "noshade")) { - style_attrs.setBorderStyle (BORDER_SOLID); - style_attrs.setBorderColor ( - Color::createShaded (HT2LT(html), style_attrs.color->getColor())); - if (size < 1) + props.set (CssProperty::CSS_PROPERTY_BORDER_TOP_STYLE, BORDER_SOLID); + props.set (CssProperty::CSS_PROPERTY_BORDER_BOTTOM_STYLE, BORDER_SOLID); + props.set (CssProperty::CSS_PROPERTY_BORDER_LEFT_STYLE, BORDER_SOLID); + props.set (CssProperty::CSS_PROPERTY_BORDER_RIGHT_STYLE, BORDER_SOLID); + + if (size <= 0) size = 1; - } else { - style_attrs.setBorderStyle (BORDER_INSET); - style_attrs.setBorderColor - (Color::createShaded (HT2LT(html), - S_TOP(html)->current_bg_color)); - if (size < 2) - size = 2; } - - style_attrs.borderWidth.top = - style_attrs.borderWidth.left = (size + 1) / 2; - style_attrs.borderWidth.bottom = - style_attrs.borderWidth.right = size / 2; - style = Style::create (HT2LT(html), &style_attrs); + + if (size > 0) { + CssLength size_top = CSS_CREATE_LENGTH ((size+1)/2, CSS_LENGTH_TYPE_PX); + CssLength size_bottom = CSS_CREATE_LENGTH (size / 2, CSS_LENGTH_TYPE_PX); + props.set (CssProperty::CSS_PROPERTY_BORDER_TOP_WIDTH, size_top); + props.set (CssProperty::CSS_PROPERTY_BORDER_LEFT_WIDTH, size_top); + props.set (CssProperty::CSS_PROPERTY_BORDER_BOTTOM_WIDTH, size_bottom); + props.set (CssProperty::CSS_PROPERTY_BORDER_RIGHT_WIDTH, size_bottom); + } + + DW2TB(html->dw)->addParbreak (5, html->styleEngine->wordStyle ()); - DW2TB(html->dw)->addParbreak (5, S_TOP(html)->style); + html->styleEngine->setNonCssHints (&props); hruler = new Ruler(); - hruler->setStyle (style); - DW2TB(html->dw)->addWidget (hruler, style); - style->unref (); - DW2TB(html->dw)->addParbreak (5, S_TOP(html)->style); + hruler->setStyle (html->styleEngine->style ()); + DW2TB(html->dw)->addWidget (hruler, html->styleEngine->style ()); + DW2TB(html->dw)->addParbreak (5, html->styleEngine->wordStyle ()); } /* @@ -2869,7 +2800,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->wordStyle ()); } /* @@ -2877,7 +2808,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->wordStyle ()); a_Html_set_top_font(html, NULL, 0, 1, 1); } @@ -2886,8 +2817,8 @@ 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); - Html_add_indented(html, 40, 40, 9); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->wordStyle ()); + Html_add_textblock(html, 9); } /* @@ -2895,12 +2826,10 @@ 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); - a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0); + DW2TB(html->dw)->addParbreak (9, html->styleEngine->wordStyle ()); /* Is the placement of this statement right? */ S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_PRE; - HTML_SET_TOP_ATTR (html, whiteSpace, WHITE_SPACE_PRE); html->pre_column = 0; html->PreFirstChar = true; html->InFlags |= IN_PRE; @@ -2912,7 +2841,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->wordStyle ()); } /* @@ -2928,7 +2857,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]) @@ -3009,14 +2938,99 @@ static void Html_tag_open_meta(DilloHtml *html, const char *tag, int tagsize) * this code in another bw might have already changed it for us. */ if (a_Misc_content_type_cmp(html->content_type, new_content)) { - a_Nav_repush(html->bw); - html->stop_parser = true; + html->stop_parser_after_head = true; + html->repush_after_head = true; } } } } } +static void Html_css_load_callback(int Op, CacheClient_t *Client) +{ + MSG("Css_callback: Op=%d\n", Op); + if (Op) { /* EOF */ + // May check num_style_sheets here... + a_Nav_repush(((DilloWeb *)Client->Web)->bw); + } +} + +/* + * Tell cache to retrieve a stylesheet + */ +static void Html_load_stylesheet(DilloHtml *html, DilloUrl *url) +{ + char *data; + int len; + if (a_Nav_get_buf(url, &data, &len)) { + /* Haven't looked into what origin_count is */ + if (a_Capi_get_flags(url) & CAPI_Completed) + html->styleEngine->parse(data, len, 0, CSS_ORIGIN_AUTHOR); + a_Nav_unref_buf(url); + } else if (!html->repush_after_head) { + /* Fill a Web structure for the cache query */ + int ClientKey; + DilloWeb *Web = a_Web_new(url); + Web->bw = html->bw; + //Web->flags |= WEB_Stylesheet; + if ((ClientKey = a_Capi_open_url(Web, Html_css_load_callback, NULL))) { + html->repush_after_stylesheet = true; + a_Bw_add_client(html->bw, ClientKey, 0); + a_Bw_add_url(html->bw, url); + } + } +} + +/* + * Parse the LINK element (Only CSS stylesheets by now). + * (If it either hits or misses, is not relevant here; that's up to the + * cache functions) + */ +static void Html_tag_open_link(DilloHtml *html, const char *tag, int tagsize) +{ + DilloUrl *url; + const char *attrbuf; + + //char *tag_str = dStrndup(tag, tagsize); + //MSG("Html_tag_open_link(): %s\n", tag_str); + //dFree(tag_str); + + /* When viewing suspicious HTML email, don't load LINK */ + if (URL_FLAGS(html->base_url) & URL_SpamSafe) + return; + /* Ignore LINK outside HEAD */ + if (!(html->InFlags & IN_HEAD)) { + BUG_MSG("the LINK element must be inside the HEAD section\n"); + return; + } + /* Load only one stylesheet by now... */ + if (html->repush_after_stylesheet) + return; + + /* TODO: How will we know when to use "handheld"? Ask the html->bw->ui for + screen dimensions, or a dillorc preference. */ + + /* CSS stylesheet link */ + if ((!(attrbuf = a_Html_get_attr(html, tag, tagsize, "rel")) || + dStrcasecmp(attrbuf, "stylesheet")) || + (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "href")) || + !(url = a_Html_url_new(html, attrbuf, NULL, 0)))) + return; + /* IMPLIED attributes? */ + if (((attrbuf = a_Html_get_attr(html, tag, tagsize, "type")) && + dStrcasecmp(attrbuf, "text/css")) || + ((attrbuf = a_Html_get_attr(html, tag, tagsize, "media")) && + !dStristr(attrbuf, "screen") && dStrcasecmp(attrbuf, "all"))) + return; + + MSG(" Html_tag_open_link(): URL=%s\n", URL_STR(url)); + MSG(" repush after HEAD=%d SHEET=%d\n", + html->repush_after_head, html->repush_after_stylesheet); + + Html_load_stylesheet(html, url); + a_Url_free(url); +} + /* * Set the history of the menu to be consistent with the active menuitem. */ @@ -3065,7 +3079,6 @@ static void Html_tag_open_base(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_code(DilloHtml *html, const char *tag, int tagsize) { - a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0); } /* @@ -3081,7 +3094,6 @@ static void Html_tag_open_dfn(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_kbd(DilloHtml *html, const char *tag, int tagsize) { - a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0); } /* @@ -3089,7 +3101,6 @@ static void Html_tag_open_kbd(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_samp(DilloHtml *html, const char *tag, int tagsize) { - a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0); } /* @@ -3105,7 +3116,6 @@ static void Html_tag_open_var(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_sub(DilloHtml *html, const char *tag, int tagsize) { - HTML_SET_TOP_ATTR (html, valign, VALIGN_SUB); } /* @@ -3113,7 +3123,6 @@ static void Html_tag_open_sub(DilloHtml *html, const char *tag, int tagsize) */ static void Html_tag_open_sup(DilloHtml *html, const char *tag, int tagsize) { - HTML_SET_TOP_ATTR (html, valign, VALIGN_SUPER); } /* @@ -3121,8 +3130,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; + + a_Html_tag_set_align_attr (html, &props, tag, tagsize); + html->styleEngine->setNonCssHints (&props); + Html_add_textblock(html, 0); } /* @@ -3130,7 +3142,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->wordStyle ()); } /* @@ -3145,7 +3157,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->wordStyle ()); } @@ -3232,7 +3244,7 @@ const TagInfo Tags[] = { /* label 010101 */ /* legend 01?? */ {"li", B8(011110),'O',1, Html_tag_open_li, Html_tag_close_li}, - /* link 100000 'F' */ + {"link", B8(100001),'F',0, Html_tag_open_link, Html_tag_close_default}, {"map", B8(011001),'R',2, Html_tag_open_map, Html_tag_close_map}, /* menu 1010 -- TODO: not exactly 1010, it can contain LI and inline */ {"menu", B8(011010),'R',2, Html_tag_open_menu, Html_tag_close_par}, @@ -3252,7 +3264,7 @@ const TagInfo Tags[] = { {"script", B8(111001),'R',2, Html_tag_open_script, Html_tag_close_script}, {"select", B8(010101),'R',2, Html_tag_open_select, Html_tag_close_select}, {"small", B8(010101),'R',2, Html_tag_open_big_small, Html_tag_close_default}, - /* span 0101 */ + {"span", B8(010101),'R',2, Html_tag_open_span, Html_tag_close_default}, {"strike", B8(010101),'R',2, Html_tag_open_strike, Html_tag_close_default}, {"strong", B8(010101),'R',2, Html_tag_open_strong, Html_tag_close_default}, {"style", B8(100101),'R',2, Html_tag_open_style, Html_tag_close_style}, @@ -3296,7 +3308,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; @@ -3327,17 +3339,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) { @@ -3434,7 +3446,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); @@ -3447,7 +3459,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); @@ -3460,11 +3472,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); Html_tag_cleanup_at_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); @@ -3486,7 +3498,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)) { @@ -3520,11 +3532,8 @@ 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 */ + html->styleEngine->startElement (ni); _MSG("Open : %*s%s\n", html->stack->size(), " ", Tags[ni].name); - Tags[ni].open (html, tag, tagsize); - if (html->stop_parser) - break; /* Now parse attributes that can appear on any tag */ if (tagsize >= 8 && /* length of "<t id=i>" */ @@ -3536,6 +3545,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)) { @@ -3551,6 +3563,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; + /* Request inmediate close for elements with forbidden close tag. */ /* TODO: XHTML always requires close tags. A simple implementation * of the commented clause below will make it work. */ @@ -3713,29 +3744,6 @@ char *a_Html_get_attr_wdef(DilloHtml *html, } /* - * Add a widget to the page. - */ -static void Html_add_widget(DilloHtml *html, - Widget *widget, - char *width_str, - char *height_str, - StyleAttrs *style_attrs) -{ - StyleAttrs new_style_attrs; - Style *style; - - new_style_attrs = *style_attrs; - new_style_attrs.width = width_str ? - a_Html_parse_length (html, width_str) : LENGTH_AUTO; - new_style_attrs.height = height_str ? - a_Html_parse_length (html, height_str) : LENGTH_AUTO; - style = Style::create (HT2LT(html), &new_style_attrs); - DW2TB(html->dw)->addWidget (widget, style); - style->unref (); -} - - -/* * Dispatch the apropriate function for 'Op' * This function is a Cache client and gets called whenever new data arrives * Op : operation to perform. diff --git a/src/html_common.hh b/src/html_common.hh index 302d9ece..ead9cf14 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; @@ -167,6 +172,10 @@ public: //BUG: for now everything is public char *content_type, *charset; bool stop_parser; + bool stop_parser_after_head; + bool repush_after_head; + bool repush_after_stylesheet; + size_t CurrTagOfs; size_t OldTagOfs, OldTagLine; @@ -174,6 +183,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 */ @@ -232,6 +242,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, @@ -249,7 +261,6 @@ DilloUrl *a_Html_url_new(DilloHtml *html, DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag, int tagsize, DilloUrl *url, - dw::core::style::StyleAttrs *style_attrs, bool add); char *a_Html_parse_entities(DilloHtml *html, const char *token, int toksize); @@ -259,11 +270,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); @@ -382,6 +382,7 @@ static void Nav_repush_callback(void *data) void a_Nav_repush(BrowserWindow *bw) { dReturn_if_fail (bw != NULL); + MSG(">>> a_Nav_repush <<<<\n"); a_Timeout_add(0.0, Nav_repush_callback, (void*)bw); } diff --git a/src/plain.cc b/src/plain.cc index b75dbea5..8826dafe 100644 --- a/src/plain.cc +++ b/src/plain.cc @@ -108,9 +108,9 @@ DilloPlain::DilloPlain(BrowserWindow *p_bw, const DilloUrl *p_url) styleAttrs.initValues (); styleAttrs.margin.setVal (5); styleAttrs.font = style::Font::create (layout, &fontAttrs); - styleAttrs.color = style::Color::createSimple (layout, prefs.text_color); + styleAttrs.color = style::Color::create (layout, prefs.text_color); styleAttrs.backgroundColor = - style::Color::createSimple (layout, prefs.bg_color); + style::Color::create (layout, prefs.bg_color); widgetStyle = style::Style::create (layout, &styleAttrs); /* The context menu */ diff --git a/src/styleengine.cc b/src/styleengine.cc new file mode 100644 index 00000000..fec34eb3 --- /dev/null +++ b/src/styleengine.cc @@ -0,0 +1,424 @@ +/* + * 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::create (layout, 0); + style_attrs.backgroundColor = Color::create (layout, 0xffffff); + + n->style = Style::create (layout, &style_attrs); + n->wordStyle = NULL; + n->pseudo = NULL; +} + +StyleEngine::~StyleEngine () { + /* \todo clear stack if not empty */ + 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->wordStyle = NULL; + n->depth = stack->size () - 1; + n->element = element; + n->id = NULL; + n->klass = NULL; + n->pseudo = stack->getRef (stack->size () - 2)->pseudo; // inherit pseudo + n->styleAttribute = NULL; + n->inheritBackgroundColor = false; +} + +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 Use of the background color of the parent style as default. + * This is only used in table code to allow for colors specified for + * table rows as table rows are currently no widgets and therefore + * don't draw any background. + */ +void StyleEngine::inheritBackgroundColor () { + stack->getRef (stack->size () - 1)->inheritBackgroundColor = true; +} + +/** + * \brief set the CSS pseudo class :link. + */ +void StyleEngine::setPseudoLink () { + Node *n = stack->getRef (stack->size () - 1); + n->pseudo = "link"; +} + +/** + * \brief set the CSS pseudo class :visited. + */ +void StyleEngine::setPseudoVisited () { + Node *n = stack->getRef (stack->size () - 1); + n->pseudo = "visited"; +} + +/** + * \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->wordStyle) + n->wordStyle->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; + computeValue (&fontAttrs.size, p->value.intVal, parentFont, + parentFont->size); + 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::create (layout, p->value.intVal); + break; + case CssProperty::CSS_PROPERTY_BORDER_TOP_COLOR: + attrs->borderColor.top = + Color::create (layout, p->value.intVal); + break; + case CssProperty::CSS_PROPERTY_BORDER_BOTTOM_COLOR: + attrs->borderColor.bottom = + Color::create (layout, p->value.intVal); + break; + case CssProperty::CSS_PROPERTY_BORDER_LEFT_COLOR: + attrs->borderColor.left = + Color::create (layout, p->value.intVal); + break; + case CssProperty::CSS_PROPERTY_BORDER_RIGHT_COLOR: + attrs->borderColor.right = + Color::create (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: + computeValue (&attrs->borderWidth.bottom, p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_BORDER_LEFT_WIDTH: + computeValue (&attrs->borderWidth.left, p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_BORDER_RIGHT_WIDTH: + computeValue (&attrs->borderWidth.right, p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_BORDER_TOP_WIDTH: + computeValue (&attrs->borderWidth.top, p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_BORDER_SPACING: + computeValue (&attrs->hBorderSpacing, p->value.intVal, attrs->font); + computeValue (&attrs->vBorderSpacing, p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_COLOR: + attrs->color = Color::create (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: + computeValue (&attrs->margin.bottom, p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_MARGIN_LEFT: + computeValue (&attrs->margin.left, p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_MARGIN_RIGHT: + computeValue (&attrs->margin.right, p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_MARGIN_TOP: + computeValue (&attrs->margin.top, p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_PADDING_TOP: + computeValue (&attrs->padding.top, p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_PADDING_BOTTOM: + computeValue (&attrs->padding.bottom, p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_PADDING_LEFT: + computeValue (&attrs->padding.left, p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_PADDING_RIGHT: + computeValue (&attrs->padding.right, 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; + case CssProperty::CSS_PROPERTY_WHITE_SPACE: + attrs->whiteSpace = (WhiteSpace) p->value.intVal; + break; + case CssProperty::CSS_PROPERTY_WIDTH: + computeLength (&attrs->width, p->value.intVal, attrs->font); + break; + case CssProperty::CSS_PROPERTY_HEIGHT: + computeLength (&attrs->height, p->value.intVal, attrs->font); + 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; + } + } + + /* make sure border colors are set */ + if (attrs->borderColor.top == NULL) + attrs->borderColor.top = attrs->color; + if (attrs->borderColor.bottom == NULL) + attrs->borderColor.bottom = attrs->color; + if (attrs->borderColor.left == NULL) + attrs->borderColor.left = attrs->color; + if (attrs->borderColor.right == NULL) + attrs->borderColor.right = attrs->color; + +} + +/** + * \brief Resolve relative lengths to absolute values. + */ +bool StyleEngine::computeValue (int *dest, CssLength value, Font *font) { + 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: + *dest = (int) CSS_LENGTH_VALUE (value); + return true; + case CSS_LENGTH_TYPE_MM: + *dest = (int) (CSS_LENGTH_VALUE (value) * dpmm); + return true; + case CSS_LENGTH_TYPE_EM: + *dest = (int) (CSS_LENGTH_VALUE (value) * font->size); + return true; + case CSS_LENGTH_TYPE_EX: + *dest = (int) (CSS_LENGTH_VALUE(value) * font->xHeight); + return true; + default: + break; + } + + return false; +} + +bool StyleEngine::computeValue (int *dest, CssLength value, Font *font, + int percentageBase) { + if (CSS_LENGTH_TYPE (value) == CSS_LENGTH_TYPE_PERCENTAGE) { + *dest = (int) (CSS_LENGTH_VALUE (value) * percentageBase); + return true; + } else + return computeValue (dest, value, font); +} + +bool StyleEngine::computeLength (dw::core::style::Length *dest, + CssLength value, Font *font) { + int v; + + if (CSS_LENGTH_TYPE (value) == CSS_LENGTH_TYPE_PERCENTAGE) { + *dest = createPerLength (CSS_LENGTH_VALUE (value)); + return true; + } else if (computeValue (&v, value, font)) { + *dest = createAbsLength (v); + return true; + } + + return false; +} + +/** + * \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 (); + + if (stack->getRef (stack->size () - 2)->inheritBackgroundColor) + attrs.backgroundColor = + stack->getRef (stack->size () - 2)->style->backgroundColor; + + 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; +} + +Style * StyleEngine::wordStyle0 (CssPropertyList *nonCssProperties) { + StyleAttrs attrs = *style (); + attrs.resetValues (); + + if (stack->getRef (stack->size () - 1)->inheritBackgroundColor) + attrs.backgroundColor = style ()->backgroundColor; + + stack->getRef (stack->size () - 1)->wordStyle = Style::create (layout, &attrs); + return stack->getRef (stack->size () - 1)->wordStyle; +} + +void StyleEngine::parse (const char *buf, int buflen, + int order_count, CssOrigin origin) { + + a_Css_parse (cssContext, buf, buflen, order_count, origin); +} diff --git a/src/styleengine.hh b/src/styleengine.hh new file mode 100644 index 00000000..d063fb27 --- /dev/null +++ b/src/styleengine.hh @@ -0,0 +1,75 @@ +#ifndef __STYLEENGINE_HH__ +#define __STYLEENGINE_HH__ + +#include "dw/core.hh" +#include "doctree.hh" +#include "css.hh" +#include "cssparser.hh" + +class StyleEngine : public Doctree { + private: + class Node : public DoctreeNode { + public: + dw::core::style::Style *style; + dw::core::style::Style *wordStyle; + const char *styleAttribute; + bool inheritBackgroundColor; + }; + + dw::core::Layout *layout; + lout::misc::SimpleVector <Node> *stack; + CssContext *cssContext; + + dw::core::style::Style *style0 (CssPropertyList *nonCssHints = NULL); + dw::core::style::Style *wordStyle0 (CssPropertyList *nonCssHints = NULL); + void apply (dw::core::style::StyleAttrs *attrs, CssPropertyList *props); + bool computeValue (int *dest, CssLength value, dw::core::style::Font *font); + bool computeValue (int *dest, CssLength value, dw::core::style::Font *font, + int percentageBase); + bool computeLength (dw::core::style::Length *dest, 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 > 1) + return stack->getRef (n->depth - 1); + else + return NULL; + }; + + void parse (const char *buf, int buflen, int order_count, CssOrigin origin); + void startElement (int tag); + void setId (const char *id); + void setClass (const char *klass); + void setStyle (const char *style); + void endElement (int tag); + void setPseudoLink (); + void setPseudoVisited (); + void setNonCssHints (CssPropertyList *nonCssHints); + void inheritBackgroundColor (); /* \todo get rid of this somehow */ + + inline dw::core::style::Style *style () { + dw::core::style::Style *s = stack->getRef (stack->size () - 1)->style; + if (s) + return s; + else + return style0 (); + }; + inline dw::core::style::Style *wordStyle () { + dw::core::style::Style *s = stack->getRef (stack->size () - 1)->wordStyle; + if (s) + return s; + else + return wordStyle0 (); + }; + +}; + +#endif diff --git a/src/table.cc b/src/table.cc index a72411d6..e06849a5 100644 --- a/src/table.cc +++ b/src/table.cc @@ -18,12 +18,11 @@ #include "prefs.h" #include "msg.h" +#include "css.hh" /* Undefine if you want to unroll tables. For instance for PDAs */ #define USE_TABLES -#define dillo_dbg_rendering 0 - using namespace dw; using namespace dw::core; using namespace dw::core::style; @@ -43,13 +42,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->wordStyle ()); #ifdef USE_TABLES if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "border"))) @@ -59,68 +58,70 @@ 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; - - /* 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); + 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); + } - 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 (cellspacing != -1) { + cssLength = CSS_CREATE_LENGTH (cellspacing, CSS_LENGTH_TYPE_PX); + props.set (CssProperty::CSS_PROPERTY_BORDER_SPACING, 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"))) { + 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); + table_cell_props = new CssPropertyList (); + if (border > 0) { + 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); + } + + if (S_TOP(html)->table_cell_props) + S_TOP(html)->table_cell_props->unref (); - 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 (); + 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 +135,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 +148,41 @@ 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->inheritBackgroundColor (); + 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 +191,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->wordStyle ()); #endif } @@ -232,10 +229,15 @@ 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; + + // \todo any shorter way to do this? + if (S_TOP(html)->table_cell_props != NULL) + props = new CssPropertyList (*S_TOP(html)->table_cell_props); + else + props = new CssPropertyList (); switch (S_TOP(html)->table_mode) { case DILLO_HTML_TABLE_MODE_NONE: @@ -259,47 +261,39 @@ 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; + props->set (CssProperty::CSS_PROPERTY_WHITE_SPACE, 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); - - /* 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); + delete props; + + if (html->styleEngine->style ()->textAlign == TEXT_ALIGN_STRING) col_tb = new dw::TableCell ( ((dw::Table*)S_TOP(html)->table)->getCellRef (), @@ -307,18 +301,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: @@ -80,9 +80,9 @@ int a_Web_dispatch_by_type (const char *Type, DilloWeb *Web, styleAttrs.initValues (); styleAttrs.margin.setVal (5); styleAttrs.font = style::Font::create (layout, &fontAttrs); - styleAttrs.color = style::Color::createSimple (layout, 0xff0000); + styleAttrs.color = style::Color::create (layout, 0xff0000); styleAttrs.backgroundColor = - style::Color::createSimple (layout, prefs.bg_color); + style::Color::create (layout, prefs.bg_color); widgetStyle = style::Style::create (layout, &styleAttrs); dw->setStyle (widgetStyle); widgetStyle->unref (); |