netsurf/css/hints.c
Michael Drake e42afd9b32 Change hints handling to use new libcss API.
Collect hints and provide them up front.

Note this implementation is minimal effort, and quite inefficient.
We can be faster about it by walking the element's attributes, and
caching things set on ancestor elements which apply to the current
element.  (Mostly table, and body stuff.)
2016-02-05 10:49:58 +00:00

1923 lines
47 KiB
C

/*
* Copyright 2009 John-Mark Bell <jmb@netsurf-browser.org>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
* NetSurf 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; version 2 of the License.
*
* NetSurf is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <strings.h>
#include "utils/nsoption.h"
#include "utils/corestrings.h"
#include "utils/log.h"
#include "utils/nsurl.h"
#include "utils/utils.h"
#include "css/hints.h"
#include "css/select.h"
#define LOG_STATS
#undef LOG_STATS
/******************************************************************************
* Utility functions *
******************************************************************************/
/**
* Determine if a given character is whitespace
*
* \param c Character to consider
* \return true if character is whitespace, false otherwise
*/
static bool isWhitespace(char c)
{
return c == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\n';
}
/**
* Determine if a given character is a valid hex digit
*
* \param c Character to consider
* \return true if character is a valid hex digit, false otherwise
*/
static bool isHex(char c)
{
return ('0' <= c && c <= '9') ||
('A' <= (c & ~0x20) && (c & ~0x20) <= 'F');
}
/**
* Convert a character representing a hex digit to the corresponding hex value
*
* \param c Character to convert
* \return Hex value represented by character
*
* \note This function assumes an ASCII-compatible character set
*/
static uint8_t charToHex(char c)
{
/* 0-9 */
c -= '0';
/* A-F */
if (c > 9)
c -= 'A' - '9' - 1;
/* a-f */
if (c > 15)
c -= 'a' - 'A';
return c;
}
/******************************************************************************
* Common parsing functions *
******************************************************************************/
/**
* Parse a number string
*
* \param data Data to parse (NUL-terminated)
* \param maybe_negative Negative numbers permitted
* \param real Floating point numbers permitted
* \param value Pointer to location to receive numeric value
* \param consumed Pointer to location to receive number of input
* bytes consumed
* \return true on success, false on invalid input
*/
static bool parse_number(const char *data, bool maybe_negative, bool real,
css_fixed *value, size_t *consumed)
{
size_t len;
const uint8_t *ptr;
int32_t intpart = 0;
int32_t fracpart = 0;
int32_t pwr = 1;
int sign = 1;
*consumed = 0;
len = strlen(data);
ptr = (const uint8_t *) data;
if (len == 0)
return false;
/* Skip leading whitespace */
while (len > 0 && isWhitespace(ptr[0])) {
len--;
ptr++;
}
if (len == 0)
return false;
/* Extract sign, if any */
if (ptr[0] == '+') {
len--;
ptr++;
} else if (ptr[0] == '-' && maybe_negative) {
sign = -1;
len--;
ptr++;
}
if (len == 0)
return false;
/* Must have a digit [0,9] */
if ('0' > ptr[0] || ptr[0] > '9')
return false;
/* Now extract intpart, assuming base 10 */
while (len > 0) {
/* Stop on first non-digit */
if (ptr[0] < '0' || '9' < ptr[0])
break;
/* Prevent overflow of 'intpart'; proper clamping below */
if (intpart < (1 << 22)) {
intpart *= 10;
intpart += ptr[0] - '0';
}
ptr++;
len--;
}
/* And fracpart, again, assuming base 10 */
if (real && len > 1 && ptr[0] == '.' &&
('0' <= ptr[1] && ptr[1] <= '9')) {
ptr++;
len--;
while (len > 0) {
if (ptr[0] < '0' || '9' < ptr[0])
break;
if (pwr < 1000000) {
pwr *= 10;
fracpart *= 10;
fracpart += ptr[0] - '0';
}
ptr++;
len--;
}
fracpart = ((1 << 10) * fracpart + pwr/2) / pwr;
if (fracpart >= (1 << 10)) {
intpart++;
fracpart &= (1 << 10) - 1;
}
}
if (sign > 0) {
/* If the result is larger than we can represent,
* then clamp to the maximum value we can store. */
if (intpart >= (1 << 21)) {
intpart = (1 << 21) - 1;
fracpart = (1 << 10) - 1;
}
} else {
/* If the negated result is smaller than we can represent
* then clamp to the minimum value we can store. */
if (intpart >= (1 << 21)) {
intpart = -(1 << 21);
fracpart = 0;
} else {
intpart = -intpart;
if (fracpart) {
fracpart = (1 << 10) - fracpart;
intpart--;
}
}
}
*value = (intpart << 10) | fracpart;
*consumed = ptr - (const uint8_t *) data;
return true;
}
/**
* Parse a dimension string
*
* \param data Data to parse (NUL-terminated)
* \param strict Whether to enforce strict parsing rules
* \param length Pointer to location to receive dimension's length
* \param unit Pointer to location to receive dimension's unit
* \return true on success, false on invalid input
*/
static bool parse_dimension(const char *data, bool strict, css_fixed *length,
css_unit *unit)
{
size_t len;
size_t read;
css_fixed value;
len = strlen(data);
if (parse_number(data, false, true, &value, &read) == false)
return false;
if (strict && value < INTTOFIX(1))
return false;
*length = value;
if (len > read && data[read] == '%')
*unit = CSS_UNIT_PCT;
else
*unit = CSS_UNIT_PX;
return true;
}
/**
* Mapping of colour name to CSS color
*/
struct colour_map {
const char *name;
css_color color;
};
/**
* Name comparator for named colour matching
*
* \param a Name to match
* \param b Colour map entry to consider
* \return 0 on match,
* < 0 if a < b,
* > 0 if b > a.
*/
static int cmp_colour_name(const void *a, const void *b)
{
const char *aa = a;
const struct colour_map *bb = b;
return strcasecmp(aa, bb->name);
}
/**
* Parse a named colour
*
* \param name Name to parse
* \param result Pointer to location to receive css_color
* \return true on success, false on invalid input
*/
static bool parse_named_colour(const char *name, css_color *result)
{
static const struct colour_map named_colours[] = {
{ "aliceblue", 0xfff0f8ff },
{ "antiquewhite", 0xfffaebd7 },
{ "aqua", 0xff00ffff },
{ "aquamarine", 0xff7fffd4 },
{ "azure", 0xfff0ffff },
{ "beige", 0xfff5f5dc },
{ "bisque", 0xffffe4c4 },
{ "black", 0xff000000 },
{ "blanchedalmond", 0xffffebcd },
{ "blue", 0xff0000ff },
{ "blueviolet", 0xff8a2be2 },
{ "brown", 0xffa52a2a },
{ "burlywood", 0xffdeb887 },
{ "cadetblue", 0xff5f9ea0 },
{ "chartreuse", 0xff7fff00 },
{ "chocolate", 0xffd2691e },
{ "coral", 0xffff7f50 },
{ "cornflowerblue", 0xff6495ed },
{ "cornsilk", 0xfffff8dc },
{ "crimson", 0xffdc143c },
{ "cyan", 0xff00ffff },
{ "darkblue", 0xff00008b },
{ "darkcyan", 0xff008b8b },
{ "darkgoldenrod", 0xffb8860b },
{ "darkgray", 0xffa9a9a9 },
{ "darkgreen", 0xff006400 },
{ "darkgrey", 0xffa9a9a9 },
{ "darkkhaki", 0xffbdb76b },
{ "darkmagenta", 0xff8b008b },
{ "darkolivegreen", 0xff556b2f },
{ "darkorange", 0xffff8c00 },
{ "darkorchid", 0xff9932cc },
{ "darkred", 0xff8b0000 },
{ "darksalmon", 0xffe9967a },
{ "darkseagreen", 0xff8fbc8f },
{ "darkslateblue", 0xff483d8b },
{ "darkslategray", 0xff2f4f4f },
{ "darkslategrey", 0xff2f4f4f },
{ "darkturquoise", 0xff00ced1 },
{ "darkviolet", 0xff9400d3 },
{ "deeppink", 0xffff1493 },
{ "deepskyblue", 0xff00bfff },
{ "dimgray", 0xff696969 },
{ "dimgrey", 0xff696969 },
{ "dodgerblue", 0xff1e90ff },
{ "feldspar", 0xffd19275 },
{ "firebrick", 0xffb22222 },
{ "floralwhite", 0xfffffaf0 },
{ "forestgreen", 0xff228b22 },
{ "fuchsia", 0xffff00ff },
{ "gainsboro", 0xffdcdcdc },
{ "ghostwhite", 0xfff8f8ff },
{ "gold", 0xffffd700 },
{ "goldenrod", 0xffdaa520 },
{ "gray", 0xff808080 },
{ "green", 0xff008000 },
{ "greenyellow", 0xffadff2f },
{ "grey", 0xff808080 },
{ "honeydew", 0xfff0fff0 },
{ "hotpink", 0xffff69b4 },
{ "indianred", 0xffcd5c5c },
{ "indigo", 0xff4b0082 },
{ "ivory", 0xfffffff0 },
{ "khaki", 0xfff0e68c },
{ "lavender", 0xffe6e6fa },
{ "lavenderblush", 0xfffff0f5 },
{ "lawngreen", 0xff7cfc00 },
{ "lemonchiffon", 0xfffffacd },
{ "lightblue", 0xffadd8e6 },
{ "lightcoral", 0xfff08080 },
{ "lightcyan", 0xffe0ffff },
{ "lightgoldenrodyellow", 0xfffafad2 },
{ "lightgray", 0xffd3d3d3 },
{ "lightgreen", 0xff90ee90 },
{ "lightgrey", 0xffd3d3d3 },
{ "lightpink", 0xffffb6c1 },
{ "lightsalmon", 0xffffa07a },
{ "lightseagreen", 0xff20b2aa },
{ "lightskyblue", 0xff87cefa },
{ "lightslateblue", 0xff8470ff },
{ "lightslategray", 0xff778899 },
{ "lightslategrey", 0xff778899 },
{ "lightsteelblue", 0xffb0c4de },
{ "lightyellow", 0xffffffe0 },
{ "lime", 0xff00ff00 },
{ "limegreen", 0xff32cd32 },
{ "linen", 0xfffaf0e6 },
{ "magenta", 0xffff00ff },
{ "maroon", 0xff800000 },
{ "mediumaquamarine", 0xff66cdaa },
{ "mediumblue", 0xff0000cd },
{ "mediumorchid", 0xffba55d3 },
{ "mediumpurple", 0xff9370db },
{ "mediumseagreen", 0xff3cb371 },
{ "mediumslateblue", 0xff7b68ee },
{ "mediumspringgreen", 0xff00fa9a },
{ "mediumturquoise", 0xff48d1cc },
{ "mediumvioletred", 0xffc71585 },
{ "midnightblue", 0xff191970 },
{ "mintcream", 0xfff5fffa },
{ "mistyrose", 0xffffe4e1 },
{ "moccasin", 0xffffe4b5 },
{ "navajowhite", 0xffffdead },
{ "navy", 0xff000080 },
{ "oldlace", 0xfffdf5e6 },
{ "olive", 0xff808000 },
{ "olivedrab", 0xff6b8e23 },
{ "orange", 0xffffa500 },
{ "orangered", 0xffff4500 },
{ "orchid", 0xffda70d6 },
{ "palegoldenrod", 0xffeee8aa },
{ "palegreen", 0xff98fb98 },
{ "paleturquoise", 0xffafeeee },
{ "palevioletred", 0xffdb7093 },
{ "papayawhip", 0xffffefd5 },
{ "peachpuff", 0xffffdab9 },
{ "peru", 0xffcd853f },
{ "pink", 0xffffc0cb },
{ "plum", 0xffdda0dd },
{ "powderblue", 0xffb0e0e6 },
{ "purple", 0xff800080 },
{ "red", 0xffff0000 },
{ "rosybrown", 0xffbc8f8f },
{ "royalblue", 0xff4169e1 },
{ "saddlebrown", 0xff8b4513 },
{ "salmon", 0xfffa8072 },
{ "sandybrown", 0xfff4a460 },
{ "seagreen", 0xff2e8b57 },
{ "seashell", 0xfffff5ee },
{ "sienna", 0xffa0522d },
{ "silver", 0xffc0c0c0 },
{ "skyblue", 0xff87ceeb },
{ "slateblue", 0xff6a5acd },
{ "slategray", 0xff708090 },
{ "slategrey", 0xff708090 },
{ "snow", 0xfffffafa },
{ "springgreen", 0xff00ff7f },
{ "steelblue", 0xff4682b4 },
{ "tan", 0xffd2b48c },
{ "teal", 0xff008080 },
{ "thistle", 0xffd8bfd8 },
{ "tomato", 0xffff6347 },
{ "turquoise", 0xff40e0d0 },
{ "violet", 0xffee82ee },
{ "violetred", 0xffd02090 },
{ "wheat", 0xfff5deb3 },
{ "white", 0xffffffff },
{ "whitesmoke", 0xfff5f5f5 },
{ "yellow", 0xffffff00 },
{ "yellowgreen", 0xff9acd32 }
};
const struct colour_map *entry;
entry = bsearch(name, named_colours,
sizeof(named_colours) / sizeof(named_colours[0]),
sizeof(named_colours[0]),
cmp_colour_name);
if (entry != NULL)
*result = entry->color;
return entry != NULL;
}
/**
* Parser for colours specified in attribute values.
*
* \param data Data to parse (NUL-terminated)
* \param result Pointer to location to receive resulting css_color
* \return true on success, false on invalid input
*/
bool nscss_parse_colour(const char *data, css_color *result)
{
size_t len = strlen(data);
uint8_t r, g, b;
/* 2 */
if (len == 0)
return false;
/* 3 */
if (len == SLEN("transparent") && strcasecmp(data, "transparent") == 0)
return false;
/* 4 */
if (parse_named_colour(data, result))
return true;
/** \todo Implement HTML5's utterly insane legacy colour parsing */
if (data[0] == '#') {
data++;
len--;
}
if (len == 3 && isHex(data[0]) && isHex(data[1]) && isHex(data[2])) {
r = charToHex(data[0]);
g = charToHex(data[1]);
b = charToHex(data[2]);
r |= (r << 4);
g |= (g << 4);
b |= (b << 4);
*result = (0xff << 24) | (r << 16) | (g << 8) | b;
return true;
} else if (len == 6 && isHex(data[0]) && isHex(data[1]) &&
isHex(data[2]) && isHex(data[3]) && isHex(data[4]) &&
isHex(data[5])) {
r = (charToHex(data[0]) << 4) | charToHex(data[1]);
g = (charToHex(data[2]) << 4) | charToHex(data[3]);
b = (charToHex(data[4]) << 4) | charToHex(data[5]);
*result = (0xff << 24) | (r << 16) | (g << 8) | b;
return true;
}
return false;
}
/**
* Parse a font \@size attribute
*
* \param size Data to parse (NUL-terminated)
* \param val Pointer to location to receive enum value
* \param len Pointer to location to receive length
* \param unit Pointer to location to receive unit
* \return True on success, false on failure
*/
static bool parse_font_size(const char *size, uint8_t *val,
css_fixed *len, css_unit *unit)
{
static const uint8_t size_map[] = {
CSS_FONT_SIZE_XX_SMALL,
CSS_FONT_SIZE_SMALL,
CSS_FONT_SIZE_MEDIUM,
CSS_FONT_SIZE_LARGE,
CSS_FONT_SIZE_X_LARGE,
CSS_FONT_SIZE_XX_LARGE,
CSS_FONT_SIZE_DIMENSION /* xxx-large (see below) */
};
const char *p = size;
char mode;
int value = 0;
/* Skip whitespace */
while (*p != '\0' && isWhitespace(*p))
p++;
mode = *p;
/* Skip +/- */
if (mode == '+' || mode == '-')
p++;
/* Need at least one digit */
if (*p < '0' || *p > '9') {
return false;
}
/* Consume digits, computing value */
while ('0' <= *p && *p <= '9') {
value = value * 10 + (*p - '0');
p++;
}
/* Resolve relative sizes */
if (mode == '+')
value += 3;
else if (mode == '-')
value = 3 - value;
/* Clamp to range [1,7] */
if (value < 1)
value = 1;
else if (value > 7)
value = 7;
if (value == 7) {
/* Manufacture xxx-large */
*len = FDIV(FMUL(INTTOFIX(3), INTTOFIX(nsoption_int(font_size))),
F_10);
} else {
/* Len is irrelevant */
*len = 0;
}
*unit = CSS_UNIT_PT;
*val = size_map[value - 1];
return true;
}
/******************************************************************************
* Presentational hint handlers *
******************************************************************************/
static css_error node_presentational_hint_vertical_align(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_string *name;
dom_string *valign = NULL;
dom_exception err;
err = dom_node_get_node_name(node, &name);
if (err != DOM_NO_ERR)
return CSS_PROPERTY_NOT_SET;
if (dom_string_caseless_lwc_isequal(name, corestring_lwc_col) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_thead) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_tbody) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_tfoot) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_tr) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_td) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_th)) {
err = dom_element_get_attribute(node,
corestring_dom_valign, &valign);
if (err != DOM_NO_ERR || valign == NULL) {
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
if (dom_string_caseless_lwc_isequal(valign,
corestring_lwc_top)) {
hint->status = CSS_VERTICAL_ALIGN_TOP;
} else if (dom_string_caseless_lwc_isequal(valign,
corestring_lwc_middle)) {
hint->status = CSS_VERTICAL_ALIGN_MIDDLE;
} else if (dom_string_caseless_lwc_isequal(valign,
corestring_lwc_bottom)) {
hint->status = CSS_VERTICAL_ALIGN_BOTTOM;
} else if (dom_string_caseless_lwc_isequal(valign,
corestring_lwc_baseline)) {
hint->status = CSS_VERTICAL_ALIGN_BASELINE;
} else {
dom_string_unref(valign);
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
dom_string_unref(valign);
dom_string_unref(name);
return CSS_OK;
} else if (dom_string_caseless_lwc_isequal(name,
corestring_lwc_applet) ||
dom_string_caseless_lwc_isequal(name,
corestring_lwc_embed) ||
dom_string_caseless_lwc_isequal(name,
corestring_lwc_iframe) ||
dom_string_caseless_lwc_isequal(name,
corestring_lwc_img) ||
dom_string_caseless_lwc_isequal(name,
corestring_lwc_object)) {
/** \todo input[type=image][align=*] - $11.3.3 */
err = dom_element_get_attribute(node,
corestring_dom_align, &valign);
if (err != DOM_NO_ERR || valign == NULL) {
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
if (dom_string_caseless_lwc_isequal(valign,
corestring_lwc_top)) {
hint->status = CSS_VERTICAL_ALIGN_TOP;
} else if (dom_string_caseless_lwc_isequal(valign,
corestring_lwc_bottom) ||
dom_string_caseless_lwc_isequal(valign,
corestring_lwc_baseline)) {
hint->status = CSS_VERTICAL_ALIGN_BASELINE;
} else if (dom_string_caseless_lwc_isequal(valign,
corestring_lwc_texttop)) {
hint->status = CSS_VERTICAL_ALIGN_TEXT_TOP;
} else if (dom_string_caseless_lwc_isequal(valign,
corestring_lwc_absmiddle) ||
dom_string_caseless_lwc_isequal(valign,
corestring_lwc_abscenter)) {
hint->status = CSS_VERTICAL_ALIGN_MIDDLE;
} else {
dom_string_unref(valign);
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
dom_string_unref(valign);
dom_string_unref(name);
return CSS_OK;
}
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
static css_error node_presentational_hint_text_align(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_string *name;
dom_string *align = NULL;
dom_exception err;
err = dom_node_get_node_name(node, &name);
if (err != DOM_NO_ERR)
return CSS_PROPERTY_NOT_SET;
if (dom_string_caseless_lwc_isequal(name, corestring_lwc_p) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_h1) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_h2) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_h3) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_h4) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_h5) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_h6)) {
err = dom_element_get_attribute(node,
corestring_dom_align, &align);
if (err != DOM_NO_ERR || align == NULL) {
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
if (dom_string_caseless_lwc_isequal(align,
corestring_lwc_left)) {
hint->status = CSS_TEXT_ALIGN_LEFT;
} else if (dom_string_caseless_lwc_isequal(align,
corestring_lwc_center)) {
hint->status = CSS_TEXT_ALIGN_CENTER;
} else if (dom_string_caseless_lwc_isequal(align,
corestring_lwc_right)) {
hint->status = CSS_TEXT_ALIGN_RIGHT;
} else if (dom_string_caseless_lwc_isequal(align,
corestring_lwc_justify)) {
hint->status = CSS_TEXT_ALIGN_JUSTIFY;
} else {
dom_string_unref(align);
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
dom_string_unref(align);
dom_string_unref(name);
return CSS_OK;
} else if (dom_string_caseless_lwc_isequal(name,
corestring_lwc_center)) {
hint->status = CSS_TEXT_ALIGN_LIBCSS_CENTER;
dom_string_unref(name);
return CSS_OK;
} else if (dom_string_caseless_lwc_isequal(name,
corestring_lwc_caption)) {
err = dom_element_get_attribute(node,
corestring_dom_align, &align);
if (err != DOM_NO_ERR) {
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
if (align == NULL || dom_string_caseless_lwc_isequal(align,
corestring_lwc_center)) {
hint->status = CSS_TEXT_ALIGN_LIBCSS_CENTER;
} else if (dom_string_caseless_lwc_isequal(align,
corestring_lwc_left)) {
hint->status = CSS_TEXT_ALIGN_LIBCSS_LEFT;
} else if (dom_string_caseless_lwc_isequal(align,
corestring_lwc_right)) {
hint->status = CSS_TEXT_ALIGN_LIBCSS_RIGHT;
} else if (dom_string_caseless_lwc_isequal(align,
corestring_lwc_justify)) {
hint->status = CSS_TEXT_ALIGN_JUSTIFY;
} else {
dom_string_unref(align);
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
if (align != NULL)
dom_string_unref(align);
dom_string_unref(name);
return CSS_OK;
} else if (dom_string_caseless_lwc_isequal(name,
corestring_lwc_div) ||
dom_string_caseless_lwc_isequal(name,
corestring_lwc_thead) ||
dom_string_caseless_lwc_isequal(name,
corestring_lwc_tbody) ||
dom_string_caseless_lwc_isequal(name,
corestring_lwc_tfoot) ||
dom_string_caseless_lwc_isequal(name,
corestring_lwc_tr) ||
dom_string_caseless_lwc_isequal(name,
corestring_lwc_td) ||
dom_string_caseless_lwc_isequal(name,
corestring_lwc_th)) {
err = dom_element_get_attribute(node,
corestring_dom_align, &align);
if (err != DOM_NO_ERR || align == NULL) {
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
if (dom_string_caseless_lwc_isequal(align,
corestring_lwc_center)) {
hint->status = CSS_TEXT_ALIGN_LIBCSS_CENTER;
} else if (dom_string_caseless_lwc_isequal(align,
corestring_lwc_left)) {
hint->status = CSS_TEXT_ALIGN_LIBCSS_LEFT;
} else if (dom_string_caseless_lwc_isequal(align,
corestring_lwc_right)) {
hint->status = CSS_TEXT_ALIGN_LIBCSS_RIGHT;
} else if (dom_string_caseless_lwc_isequal(align,
corestring_lwc_justify)) {
hint->status = CSS_TEXT_ALIGN_JUSTIFY;
} else {
dom_string_unref(align);
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
dom_string_unref(align);
dom_string_unref(name);
return CSS_OK;
} else if (dom_string_caseless_lwc_isequal(name,
corestring_lwc_table)) {
/* Tables usually reset alignment */
hint->status = CSS_TEXT_ALIGN_INHERIT_IF_NON_MAGIC;
dom_string_unref(name);
return CSS_OK;
} else {
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
}
static css_error node_presentational_hint_padding_trbl(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_string *name;
dom_exception exc;
css_error result = CSS_PROPERTY_NOT_SET;
exc = dom_node_get_node_name(node, &name);
if (exc != DOM_NO_ERR)
return CSS_BADPARM;
if (dom_string_caseless_lwc_isequal(name, corestring_lwc_td) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_th)) {
css_qname qs;
dom_node *tablenode = NULL;
dom_string *cellpadding = NULL;
qs.ns = NULL;
qs.name = lwc_string_ref(corestring_lwc_table);
if (named_ancestor_node(ctx, node, &qs,
(void *)&tablenode) != CSS_OK) {
/* Didn't find, or had error */
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
lwc_string_unref(qs.name);
if (tablenode != NULL) {
exc = dom_element_get_attribute(tablenode,
corestring_dom_cellpadding,
&cellpadding);
if (exc != DOM_NO_ERR) {
dom_string_unref(name);
return CSS_BADPARM;
}
}
/* No need to unref tablenode, named_ancestor_node does not
* return a reffed node to the CSS
*/
if (cellpadding != NULL) {
if (parse_dimension(dom_string_data(cellpadding), false,
&hint->data.length.value,
&hint->data.length.unit)) {
hint->status = CSS_PADDING_SET;
result = CSS_OK;
}
dom_string_unref(cellpadding);
}
}
dom_string_unref(name);
return result;
}
static css_error node_presentational_hint_margin_rl(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint,
uint32_t property)
{
dom_string *n;
dom_exception exc;
exc = dom_node_get_node_name(node, &n);
if (exc != DOM_NO_ERR)
return CSS_BADPARM;
if (dom_string_caseless_lwc_isequal(n, corestring_lwc_img) ||
dom_string_caseless_lwc_isequal(n, corestring_lwc_applet)) {
dom_string_unref(n);
exc = dom_element_get_attribute(node,
corestring_dom_hspace, &n);
if (exc != DOM_NO_ERR) {
return CSS_BADPARM;
}
if (n == NULL)
return CSS_PROPERTY_NOT_SET;
if (parse_dimension(dom_string_data(n), false,
&hint->data.length.value,
&hint->data.length.unit)) {
hint->status = CSS_MARGIN_SET;
} else {
dom_string_unref(n);
return CSS_PROPERTY_NOT_SET;
}
dom_string_unref(n);
return CSS_OK;
} else if (dom_string_caseless_lwc_isequal(n, corestring_lwc_table) ||
dom_string_caseless_lwc_isequal(n, corestring_lwc_align)) {
dom_string_unref(n);
exc = dom_element_get_attribute(node,
corestring_dom_align, &n);
if (exc != DOM_NO_ERR) {
return CSS_BADPARM;
}
if (n == NULL)
return CSS_PROPERTY_NOT_SET;
if (dom_string_caseless_lwc_isequal(n,
corestring_lwc_center) ||
dom_string_caseless_lwc_isequal(n,
corestring_lwc_abscenter) ||
dom_string_caseless_lwc_isequal(n,
corestring_lwc_middle) ||
dom_string_caseless_lwc_isequal(n,
corestring_lwc_absmiddle)) {
hint->status = CSS_MARGIN_AUTO;
} else {
dom_string_unref(n);
return CSS_PROPERTY_NOT_SET;
}
dom_string_unref(n);
return CSS_OK;
} else if (dom_string_caseless_lwc_isequal(n, corestring_lwc_hr)) {
dom_string_unref(n);
exc = dom_element_get_attribute(node,
corestring_dom_align, &n);
if (exc != DOM_NO_ERR)
return CSS_BADPARM;
if (n == NULL)
return CSS_PROPERTY_NOT_SET;
if (dom_string_caseless_lwc_isequal(n,
corestring_lwc_left)) {
if (property == CSS_PROP_MARGIN_LEFT) {
hint->data.length.value = 0;
hint->data.length.unit = CSS_UNIT_PX;
hint->status = CSS_MARGIN_SET;
} else {
hint->status = CSS_MARGIN_AUTO;
}
} else if (dom_string_caseless_lwc_isequal(n,
corestring_lwc_center)) {
hint->status = CSS_MARGIN_AUTO;
} else if (dom_string_caseless_lwc_isequal(n,
corestring_lwc_right)) {
if (property == CSS_PROP_MARGIN_RIGHT) {
hint->data.length.value = 0;
hint->data.length.unit = CSS_UNIT_PX;
hint->status = CSS_MARGIN_SET;
} else {
hint->status = CSS_MARGIN_AUTO;
}
} else {
dom_string_unref(n);
return CSS_PROPERTY_NOT_SET;
}
dom_string_unref(n);
return CSS_OK;
}
dom_string_unref(n);
return CSS_PROPERTY_NOT_SET;
}
static css_error node_presentational_hint_margin_tb(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_string *name, *vspace = NULL;
dom_exception exc;
exc = dom_node_get_node_name(node, &name);
if (exc != DOM_NO_ERR)
return CSS_BADPARM;
if (dom_string_caseless_lwc_isequal(name, corestring_lwc_img) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_applet)) {
exc = dom_element_get_attribute(node, corestring_dom_vspace,
&vspace);
if (exc != DOM_NO_ERR) {
dom_string_unref(name);
return CSS_BADPARM;
}
}
dom_string_unref(name);
if (vspace == NULL)
return CSS_PROPERTY_NOT_SET;
if (parse_dimension(dom_string_data(vspace), false,
&hint->data.length.value,
&hint->data.length.unit)) {
hint->status = CSS_MARGIN_SET;
} else {
dom_string_unref(vspace);
return CSS_PROPERTY_NOT_SET;
}
dom_string_unref(vspace);
return CSS_OK;
}
static css_error node_presentational_hint_border_trbl_width(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_string *name;
dom_exception exc;
dom_string *width = NULL;
bool is_table_cell = false;
exc = dom_node_get_node_name(node, &name);
if (exc != DOM_NO_ERR)
return CSS_BADPARM;
if (dom_string_caseless_lwc_isequal(name, corestring_lwc_td) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_th)) {
css_qname qs;
dom_node *tablenode = NULL;
qs.ns = NULL;
qs.name = lwc_string_ref(corestring_lwc_table);
if (named_ancestor_node(ctx, node, &qs,
(void *)&tablenode) != CSS_OK) {
/* Didn't find, or had error */
lwc_string_unref(qs.name);
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
lwc_string_unref(qs.name);
if (tablenode != NULL) {
exc = dom_element_get_attribute(tablenode,
corestring_dom_border, &width);
if (exc != DOM_NO_ERR) {
dom_string_unref(name);
return CSS_BADPARM;
}
}
/* No need to unref tablenode, named_ancestor_node does not
* return a reffed node to the CSS
*/
is_table_cell = true;
} else if (dom_string_caseless_lwc_isequal(name,
corestring_lwc_table)) {
exc = dom_element_get_attribute(node, corestring_dom_border,
&width);
if (exc != DOM_NO_ERR) {
dom_string_unref(name);
return CSS_BADPARM;
}
}
dom_string_unref(name);
if (width == NULL)
return CSS_PROPERTY_NOT_SET;
if (parse_dimension(dom_string_data(width), false,
&hint->data.length.value,
&hint->data.length.unit)) {
if (is_table_cell &&
INTTOFIX(0) !=
hint->data.length.value) {
hint->data.length.value = INTTOFIX(1);
hint->data.length.unit = CSS_UNIT_PX;
}
hint->status = CSS_BORDER_WIDTH_WIDTH;
} else {
dom_string_unref(width);
return CSS_PROPERTY_NOT_SET;
}
dom_string_unref(width);
return CSS_OK;
}
static css_error node_presentational_hint_border_trbl_style(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_string *name;
dom_exception exc;
exc = dom_node_get_node_name(node, &name);
if (exc != DOM_NO_ERR)
return CSS_BADPARM;
if (dom_string_caseless_lwc_isequal(name, corestring_lwc_td) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_th)) {
css_qname qs;
dom_node *tablenode = NULL;
qs.ns = NULL;
qs.name = lwc_string_ref(corestring_lwc_table);
if (named_ancestor_node(ctx, node, &qs,
(void *)&tablenode) != CSS_OK) {
/* Didn't find, or had error */
lwc_string_unref(qs.name);
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
lwc_string_unref(qs.name);
if (tablenode != NULL) {
bool has_border = false;
exc = dom_element_has_attribute(tablenode,
corestring_dom_border,
&has_border);
if (exc != DOM_NO_ERR) {
dom_string_unref(name);
return CSS_BADPARM;
}
if (has_border) {
hint->status = CSS_BORDER_STYLE_INSET;
dom_string_unref(name);
return CSS_OK;
}
}
/* No need to unref tablenode, named_ancestor_node does not
* return a reffed node to the CSS
*/
} else if (dom_string_caseless_lwc_isequal(name,
corestring_lwc_table)) {
bool has_border = false;
exc = dom_element_has_attribute(node,
corestring_dom_border,
&has_border);
if (exc != DOM_NO_ERR) {
dom_string_unref(name);
return CSS_BADPARM;
}
if (has_border) {
hint->status = CSS_BORDER_STYLE_OUTSET;
dom_string_unref(name);
return CSS_OK;
}
}
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
static css_error node_presentational_hint_border_trbl_color(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_string *name;
dom_string *bordercolor = NULL;
dom_exception err;
err = dom_node_get_node_name(node, &name);
if (err != DOM_NO_ERR)
return CSS_PROPERTY_NOT_SET;
if (dom_string_caseless_lwc_isequal(name, corestring_lwc_td) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_th)) {
css_qname qs;
dom_node *tablenode = NULL;
qs.ns = NULL;
qs.name = lwc_string_ref(corestring_lwc_table);
if (named_ancestor_node(ctx, node, &qs,
(void *)&tablenode) != CSS_OK) {
/* Didn't find, or had error */
lwc_string_unref(qs.name);
dom_string_unref(name);
return CSS_PROPERTY_NOT_SET;
}
lwc_string_unref(qs.name);
if (tablenode != NULL) {
err = dom_element_get_attribute(node,
corestring_dom_bordercolor,
&bordercolor);
}
/* No need to unref tablenode, named_ancestor_node does not
* return a reffed node to the CSS
*/
} else if (dom_string_caseless_lwc_isequal(name,
corestring_lwc_table)) {
err = dom_element_get_attribute(node,
corestring_dom_bordercolor,
&bordercolor);
}
dom_string_unref(name);
if ((err != DOM_NO_ERR) || (bordercolor == NULL)) {
return CSS_PROPERTY_NOT_SET;
}
if (nscss_parse_colour((const char *)dom_string_data(bordercolor),
&hint->data.color)) {
hint->status = CSS_BORDER_COLOR_COLOR;
dom_string_unref(bordercolor);
return CSS_OK;
}
dom_string_unref(bordercolor);
return CSS_PROPERTY_NOT_SET;
}
static css_error node_presentational_hint_border_spacing(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_exception err;
dom_string *node_name = NULL;
dom_string *cellspacing = NULL;
err = dom_node_get_node_name(node, &node_name);
if ((err != DOM_NO_ERR) || (node_name == NULL)) {
return CSS_PROPERTY_NOT_SET;
}
if (!dom_string_caseless_lwc_isequal(node_name,
corestring_lwc_table)) {
dom_string_unref(node_name);
return CSS_PROPERTY_NOT_SET;
}
dom_string_unref(node_name);
err = dom_element_get_attribute(node,
corestring_dom_cellspacing, &cellspacing);
if ((err != DOM_NO_ERR) || (cellspacing == NULL)) {
return CSS_PROPERTY_NOT_SET;
}
if (parse_dimension((const char *)dom_string_data(cellspacing),
false,
&hint->data.position.h.value,
&hint->data.position.h.unit)) {
hint->data.position.v = hint->data.position.h;
hint->status = CSS_BORDER_SPACING_SET;
dom_string_unref(cellspacing);
return CSS_OK;
}
dom_string_unref(cellspacing);
return CSS_PROPERTY_NOT_SET;
}
static css_error node_presentational_hint_width(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_string *name;
dom_string *width = NULL;
dom_exception err;
bool textarea = false;
bool input = false;
err = dom_node_get_node_name(node, &name);
if (err != DOM_NO_ERR)
return CSS_PROPERTY_NOT_SET;
if (dom_string_caseless_lwc_isequal(name, corestring_lwc_hr) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_iframe) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_img) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_object) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_table) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_td) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_th) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_applet)) {
err = dom_element_get_attribute(node,
corestring_dom_width, &width);
} else if (dom_string_caseless_lwc_isequal(name,
corestring_lwc_textarea)) {
textarea = true;
err = dom_element_get_attribute(node,
corestring_dom_cols, &width);
} else if (dom_string_caseless_lwc_isequal(name,
corestring_lwc_input)) {
input = true;
err = dom_element_get_attribute(node,
corestring_dom_size, &width);
}
dom_string_unref(name);
if ((err != DOM_NO_ERR) || (width == NULL)) {
return CSS_PROPERTY_NOT_SET;
}
if (parse_dimension((const char *)dom_string_data(width),
false,
&hint->data.length.value,
&hint->data.length.unit)) {
hint->status = CSS_WIDTH_SET;
dom_string_unref(width);
if (textarea) {
hint->data.length.unit = CSS_UNIT_EX;
}
if (input) {
err = dom_element_get_attribute(node,
corestring_dom_type, &width);
if (err != DOM_NO_ERR) {
return CSS_PROPERTY_NOT_SET;
}
if ((width == NULL) ||
dom_string_caseless_lwc_isequal(width,
corestring_lwc_text) ||
dom_string_caseless_lwc_isequal(width,
corestring_lwc_search) ||
dom_string_caseless_lwc_isequal(width,
corestring_lwc_file) ||
dom_string_caseless_lwc_isequal(width,
corestring_lwc_password)) {
hint->data.length.unit = CSS_UNIT_EX;
}
dom_string_unref(width);
}
return CSS_OK;
}
dom_string_unref(width);
return CSS_PROPERTY_NOT_SET;
}
static css_error node_presentational_hint_height(nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_string *name;
dom_string *height = NULL;
dom_exception err;
bool textarea = false;
err = dom_node_get_node_name(node, &name);
if (err != DOM_NO_ERR)
return CSS_PROPERTY_NOT_SET;
if (dom_string_caseless_lwc_isequal(name, corestring_lwc_iframe) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_td) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_th) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_tr) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_img) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_object) ||
dom_string_caseless_lwc_isequal(name, corestring_lwc_applet)) {
err = dom_element_get_attribute(node,
corestring_dom_height, &height);
} else if (dom_string_caseless_lwc_isequal(name,
corestring_lwc_textarea)) {
textarea = true;
err = dom_element_get_attribute(node,
corestring_dom_rows, &height);
}
dom_string_unref(name);
if ((err != DOM_NO_ERR) || (height == NULL)) {
return CSS_PROPERTY_NOT_SET;
}
if (parse_dimension((const char *)dom_string_data(height),
false,
&hint->data.length.value,
&hint->data.length.unit)) {
hint->status = CSS_HEIGHT_SET;
if (textarea) {
hint->data.length.unit = CSS_UNIT_EM;
}
dom_string_unref(height);
return CSS_OK;
}
dom_string_unref(height);
return CSS_PROPERTY_NOT_SET;
}
static css_error node_presentational_hint_font_size(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_exception err;
dom_string *node_name = NULL;
dom_string *size;
err = dom_node_get_node_name(node, &node_name);
if ((err != DOM_NO_ERR) || (node_name == NULL)) {
return CSS_NOMEM;
}
if (!dom_string_caseless_lwc_isequal(node_name,
corestring_lwc_font)) {
dom_string_unref(node_name);
return CSS_PROPERTY_NOT_SET;
}
dom_string_unref(node_name);
err = dom_element_get_attribute(node, corestring_dom_size, &size);
if ((err != DOM_NO_ERR) || (size == NULL)) {
return CSS_PROPERTY_NOT_SET;
}
if (parse_font_size((const char *)dom_string_data(size),
&hint->status,
&hint->data.length.value,
&hint->data.length.unit)) {
dom_string_unref(size);
return CSS_OK;
}
dom_string_unref(size);
return CSS_PROPERTY_NOT_SET;
}
static css_error node_presentational_hint_float(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_exception err;
dom_string *node_name = NULL;
dom_string *align;
err = dom_node_get_node_name(node, &node_name);
if ((err != DOM_NO_ERR) || (node_name == NULL)) {
return CSS_NOMEM;
}
/** \todo input[type=image][align=*] - $11.3.3 */
if (!dom_string_caseless_lwc_isequal(node_name,
corestring_lwc_table) &&
!dom_string_caseless_lwc_isequal(node_name,
corestring_lwc_applet) &&
!dom_string_caseless_lwc_isequal(node_name,
corestring_lwc_embed) &&
!dom_string_caseless_lwc_isequal(node_name,
corestring_lwc_iframe) &&
!dom_string_caseless_lwc_isequal(node_name,
corestring_lwc_img) &&
!dom_string_caseless_lwc_isequal(node_name,
corestring_lwc_object)) {
dom_string_unref(node_name);
return CSS_PROPERTY_NOT_SET;
}
dom_string_unref(node_name);
err = dom_element_get_attribute(node, corestring_dom_align, &align);
if ((err != DOM_NO_ERR) || (align == NULL)) {
return CSS_PROPERTY_NOT_SET;
}
if (dom_string_caseless_lwc_isequal(align,
corestring_lwc_left)) {
hint->status = CSS_FLOAT_LEFT;
dom_string_unref(align);
return CSS_OK;
} else if (dom_string_caseless_lwc_isequal(align,
corestring_lwc_right)) {
hint->status = CSS_FLOAT_RIGHT;
dom_string_unref(align);
return CSS_OK;
}
dom_string_unref(align);
return CSS_PROPERTY_NOT_SET;
}
static css_error node_presentational_hint_color(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
css_error error;
dom_exception err;
dom_string *node_name = NULL;
dom_string *color;
err = dom_node_get_node_name(node, &node_name);
if ((err != DOM_NO_ERR) || (node_name == NULL)) {
return CSS_NOMEM;
}
if (dom_string_caseless_lwc_isequal(node_name, corestring_lwc_a)) {
/* find body node */
css_qname qs;
dom_node *bodynode = NULL;
bool is_visited;
qs.ns = NULL;
qs.name = lwc_string_ref(corestring_lwc_body);
if (named_ancestor_node(ctx, node, &qs,
(void *)&bodynode) != CSS_OK) {
/* Didn't find, or had error */
lwc_string_unref(qs.name);
dom_string_unref(node_name);
return CSS_PROPERTY_NOT_SET;
}
lwc_string_unref(qs.name);
/* deal with missing body ancestor */
if (bodynode == NULL) {
dom_string_unref(node_name);
return CSS_BADPARM;
}
error = node_is_visited(ctx, node, &is_visited);
if (error != CSS_OK)
return error;
if (is_visited) {
err = dom_element_get_attribute(bodynode,
corestring_dom_vlink, &color);
if ((err != DOM_NO_ERR) || (color == NULL)) {
dom_string_unref(node_name);
return CSS_PROPERTY_NOT_SET;
}
} else {
err = dom_element_get_attribute(bodynode,
corestring_dom_link, &color);
if ((err != DOM_NO_ERR) || (color == NULL)) {
dom_string_unref(node_name);
return CSS_PROPERTY_NOT_SET;
}
}
} else {
err = dom_element_get_attribute(node,
corestring_dom_color, &color);
if ((err != DOM_NO_ERR) || (color == NULL)) {
dom_string_unref(node_name);
return CSS_PROPERTY_NOT_SET;
}
}
if (!nscss_parse_colour((const char *)dom_string_data(color),
&hint->data.color)) {
dom_string_unref(color);
dom_string_unref(node_name);
return CSS_PROPERTY_NOT_SET;
}
hint->status = CSS_COLOR_COLOR;
dom_string_unref(color);
dom_string_unref(node_name);
return CSS_OK;
}
static css_error node_presentational_hint_caption_side(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_exception err;
dom_string *node_name = NULL;
dom_string *align = NULL;
err = dom_node_get_node_name(node, &node_name);
if ((err != DOM_NO_ERR) || (node_name == NULL)) {
return CSS_PROPERTY_NOT_SET;
}
if (!dom_string_caseless_lwc_isequal(node_name,
corestring_lwc_caption)) {
dom_string_unref(node_name);
return CSS_PROPERTY_NOT_SET;
}
dom_string_unref(node_name);
err = dom_element_get_attribute(node, corestring_dom_align, &align);
if ((err != DOM_NO_ERR) || (align == NULL)) {
return CSS_PROPERTY_NOT_SET;
}
if (dom_string_caseless_lwc_isequal(align, corestring_lwc_bottom)) {
hint->status = CSS_CAPTION_SIDE_BOTTOM;
dom_string_unref(align);
return CSS_OK;
}
dom_string_unref(align);
return CSS_PROPERTY_NOT_SET;
}
static css_error node_presentational_hint_background_color(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_exception err;
dom_string *bgcolor;
err = dom_element_get_attribute(node,
corestring_dom_bgcolor, &bgcolor);
if ((err != DOM_NO_ERR) || (bgcolor == NULL)) {
return CSS_PROPERTY_NOT_SET;
}
if (nscss_parse_colour((const char *)dom_string_data(bgcolor),
&hint->data.color)) {
hint->status = CSS_BACKGROUND_COLOR_COLOR;
dom_string_unref(bgcolor);
return CSS_OK;
}
dom_string_unref(bgcolor);
return CSS_PROPERTY_NOT_SET;
}
static css_error node_presentational_hint_background_image(
nscss_select_ctx *ctx,
dom_node *node,
css_hint *hint)
{
dom_exception err;
dom_string *atr_val;
nserror error;
nsurl *url;
lwc_string *iurl;
lwc_error lerror;
err = dom_element_get_attribute(node,
corestring_dom_background, &atr_val);
if ((err != DOM_NO_ERR) || (atr_val == NULL)) {
return CSS_PROPERTY_NOT_SET;
}
error = nsurl_join(ctx->base_url,
(const char *)dom_string_data(atr_val), &url);
dom_string_unref(atr_val);
if (error != NSERROR_OK) {
return CSS_NOMEM;
}
lerror = lwc_intern_string(nsurl_access(url),
nsurl_length(url), &iurl);
nsurl_unref(url);
if (lerror == lwc_error_oom) {
return CSS_NOMEM;
}
if (lerror == lwc_error_ok) {
hint->data.string = iurl;
hint->status = CSS_BACKGROUND_IMAGE_IMAGE;
return CSS_OK;
}
return CSS_PROPERTY_NOT_SET;
}
struct css_hint_ctx {
struct css_hint *hints;
uint32_t alloc;
uint32_t len;
};
struct css_hint_ctx hint_ctx;
static void css_hint_destroy(struct css_hint_ctx *hint)
{
hint->alloc = 0;
hint->len = 0;
free(hint->hints);
}
static nserror css_hint_extend(struct css_hint_ctx *hint)
{
uint32_t alloc = (hint->alloc == 0) ? 32 : hint->alloc * 2;
struct css_hint *temp;
temp = realloc(hint->hints, sizeof(struct css_hint) * alloc);
if (temp != NULL) {
hint->hints = temp;
hint->alloc = alloc;
return NSERROR_OK;
}
return NSERROR_NOMEM;
}
nserror css_hint_init(void)
{
nserror err;
err = css_hint_extend(&hint_ctx);
if (err != NSERROR_OK) {
return err;
}
return NSERROR_OK;
}
void css_hint_fini(void)
{
css_hint_destroy(&hint_ctx);
}
/* Exported function, documeted in css/hints.h */
static inline css_error node_presentational_hint_internal(
void *pw, void *node, struct css_hint_ctx *hints)
{
struct css_hint_ctx *ctx = hints;
uint32_t property;
nserror nserr;
css_error err;
ctx->len = 0;
for (property = 0; property < CSS_N_PROPERTIES; property++) {
css_hint *hint = &(ctx->hints[ctx->len]);
if (ctx->alloc == 0 || ctx->len == ctx->alloc - 1) {
nserr = css_hint_extend(ctx);
if (nserr != NSERROR_OK) {
return NSERROR_NOMEM;
}
}
switch (property) {
case CSS_PROP_BACKGROUND_IMAGE:
err = node_presentational_hint_background_image(
pw, node, hint);
break;
case CSS_PROP_BACKGROUND_COLOR:
err = node_presentational_hint_background_color(
pw, node, hint);
break;
case CSS_PROP_CAPTION_SIDE:
err = node_presentational_hint_caption_side(
pw, node, hint);
break;
case CSS_PROP_COLOR:
err = node_presentational_hint_color(
pw, node, hint);
break;
case CSS_PROP_FLOAT:
err = node_presentational_hint_float(
pw, node, hint);
break;
case CSS_PROP_FONT_SIZE:
err = node_presentational_hint_font_size(
pw, node, hint);
break;
case CSS_PROP_HEIGHT:
err = node_presentational_hint_height(
pw, node, hint);
break;
case CSS_PROP_WIDTH:
err = node_presentational_hint_width(
pw, node, hint);
break;
case CSS_PROP_BORDER_SPACING:
err = node_presentational_hint_border_spacing(
pw, node, hint);
break;
case CSS_PROP_BORDER_TOP_COLOR:
case CSS_PROP_BORDER_RIGHT_COLOR:
case CSS_PROP_BORDER_BOTTOM_COLOR:
case CSS_PROP_BORDER_LEFT_COLOR:
err = node_presentational_hint_border_trbl_color(
pw, node, hint);
break;
case CSS_PROP_BORDER_TOP_STYLE:
case CSS_PROP_BORDER_RIGHT_STYLE:
case CSS_PROP_BORDER_BOTTOM_STYLE:
case CSS_PROP_BORDER_LEFT_STYLE:
err = node_presentational_hint_border_trbl_style(
pw, node, hint);
break;
case CSS_PROP_BORDER_TOP_WIDTH:
case CSS_PROP_BORDER_RIGHT_WIDTH:
case CSS_PROP_BORDER_BOTTOM_WIDTH:
case CSS_PROP_BORDER_LEFT_WIDTH:
err = node_presentational_hint_border_trbl_width(
pw, node, hint);
break;
case CSS_PROP_MARGIN_TOP:
case CSS_PROP_MARGIN_BOTTOM:
err = node_presentational_hint_margin_tb(
pw, node, hint);
break;
case CSS_PROP_MARGIN_RIGHT:
case CSS_PROP_MARGIN_LEFT:
err = node_presentational_hint_margin_rl(
pw, node, hint, property);
break;
case CSS_PROP_PADDING_TOP:
case CSS_PROP_PADDING_RIGHT:
case CSS_PROP_PADDING_BOTTOM:
case CSS_PROP_PADDING_LEFT:
err = node_presentational_hint_padding_trbl(
pw, node, hint);
break;
case CSS_PROP_TEXT_ALIGN:
err = node_presentational_hint_text_align(
pw, node, hint);
break;
case CSS_PROP_VERTICAL_ALIGN:
err = node_presentational_hint_vertical_align(
pw, node, hint);
break;
default:
err = CSS_PROPERTY_NOT_SET;
break;
}
if (err == CSS_OK) {
hint->prop = property;
ctx->len++;
}
}
return CSS_OK;
}
/* Exported function, documeted in css/hints.h */
css_error node_presentational_hint(void *pw, void *node,
uint32_t *nhints, css_hint **hints)
{
css_error err;
err = node_presentational_hint_internal(pw, node, &hint_ctx);
if (err != CSS_OK) {
return err;
}
#ifdef LOG_STATS
LOG("Properties with hints: %i", hint_ctx.len);
#endif
*nhints = hint_ctx.len;
*hints = hint_ctx.hints;
return CSS_OK;
}