summaryrefslogtreecommitdiff
path: root/content/handlers/css/hints.c
diff options
context:
space:
mode:
Diffstat (limited to 'content/handlers/css/hints.c')
-rw-r--r--content/handlers/css/hints.c1604
1 files changed, 1604 insertions, 0 deletions
diff --git a/content/handlers/css/hints.c b/content/handlers/css/hints.c
new file mode 100644
index 000000000..f394ea836
--- /dev/null
+++ b/content/handlers/css/hints.c
@@ -0,0 +1,1604 @@
+/*
+ * 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 "hints.h"
+#include "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;
+}
+
+/* exported interface documented in content/handlers/css/hints.h */
+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;
+}
+
+
+/******************************************************************************
+ * Hint context management *
+ ******************************************************************************/
+
+#define MAX_HINTS_PER_ELEMENT 32
+
+struct css_hint_ctx {
+ struct css_hint *hints;
+ uint32_t len;
+};
+
+struct css_hint_ctx hint_ctx;
+
+nserror css_hint_init(void)
+{
+ hint_ctx.hints = malloc(sizeof(struct css_hint) *
+ MAX_HINTS_PER_ELEMENT);
+ if (hint_ctx.hints == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ return NSERROR_OK;
+}
+
+void css_hint_fini(void)
+{
+ hint_ctx.len = 0;
+ free(hint_ctx.hints);
+}
+
+static void css_hint_clean(void)
+{
+ hint_ctx.len = 0;
+}
+
+static inline struct css_hint * css_hint_advance(struct css_hint *hint)
+{
+ hint_ctx.len++;
+ assert(hint_ctx.len < MAX_HINTS_PER_ELEMENT);
+
+ return ++hint;
+}
+
+static void css_hint_get_hints(struct css_hint **hints, uint32_t *nhints)
+{
+ *hints = hint_ctx.hints;
+ *nhints = hint_ctx.len;
+}
+
+
+/******************************************************************************
+ * Presentational hint handlers *
+ ******************************************************************************/
+
+static void css_hint_table_cell_border_padding(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ css_qname qs;
+ dom_string *attr = NULL;
+ dom_node *tablenode = NULL;
+ dom_exception exc;
+
+ 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);
+ return;
+ }
+ lwc_string_unref(qs.name);
+
+ if (tablenode == NULL) {
+ return;
+ }
+ /* No need to unref tablenode, named_ancestor_node does not
+ * return a reffed node to the CSS
+ */
+
+ exc = dom_element_get_attribute(tablenode,
+ corestring_dom_border, &attr);
+
+ if (exc == DOM_NO_ERR && attr != NULL) {
+ uint32_t hint_prop;
+ css_hint_length hint_length;
+
+ if (parse_dimension(
+ dom_string_data(attr), false,
+ &hint_length.value,
+ &hint_length.unit) &&
+ INTTOFIX(0) != hint_length.value) {
+
+ for (hint_prop = CSS_PROP_BORDER_TOP_STYLE;
+ hint_prop <= CSS_PROP_BORDER_LEFT_STYLE;
+ hint_prop++) {
+ hint->prop = hint_prop;
+ hint->status = CSS_BORDER_STYLE_INSET;
+ hint = css_hint_advance(hint);
+ }
+
+ for (hint_prop = CSS_PROP_BORDER_TOP_WIDTH;
+ hint_prop <= CSS_PROP_BORDER_LEFT_WIDTH;
+ hint_prop++) {
+ hint->prop = hint_prop;
+ hint->data.length.value = INTTOFIX(1);
+ hint->data.length.unit = CSS_UNIT_PX;
+ hint->status = CSS_BORDER_WIDTH_WIDTH;
+ hint = css_hint_advance(hint);
+ }
+ }
+ dom_string_unref(attr);
+ }
+
+ exc = dom_element_get_attribute(tablenode,
+ corestring_dom_bordercolor, &attr);
+
+ if (exc == DOM_NO_ERR && attr != NULL) {
+ uint32_t hint_prop;
+ css_color hint_color;
+
+ if (nscss_parse_colour(
+ (const char *)dom_string_data(attr),
+ &hint_color)) {
+
+ for (hint_prop = CSS_PROP_BORDER_TOP_COLOR;
+ hint_prop <= CSS_PROP_BORDER_LEFT_COLOR;
+ hint_prop++) {
+ hint->prop = hint_prop;
+ hint->data.color = hint_color;
+ hint->status = CSS_BORDER_COLOR_COLOR;
+ hint = css_hint_advance(hint);
+ }
+ }
+ dom_string_unref(attr);
+ }
+
+ exc = dom_element_get_attribute(tablenode,
+ corestring_dom_cellpadding, &attr);
+
+ if (exc == DOM_NO_ERR && attr != NULL) {
+ uint32_t hint_prop;
+ css_hint_length hint_length;
+
+ if (parse_dimension(
+ dom_string_data(attr), false,
+ &hint_length.value,
+ &hint_length.unit)) {
+
+ for (hint_prop = CSS_PROP_PADDING_TOP;
+ hint_prop <= CSS_PROP_PADDING_LEFT;
+ hint_prop++) {
+ hint->prop = hint_prop;
+ hint->data.length.value = hint_length.value;
+ hint->data.length.unit = hint_length.unit;
+ hint->status = CSS_PADDING_SET;
+ hint = css_hint_advance(hint);
+ }
+ }
+ dom_string_unref(attr);
+ }
+}
+
+static void css_hint_vertical_align_table_cells(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_string *attr = NULL;
+ dom_exception err;
+
+ err = dom_element_get_attribute(node,
+ corestring_dom_valign, &attr);
+
+ if (err == DOM_NO_ERR && attr != NULL) {
+ if (dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_top)) {
+ hint->prop = CSS_PROP_VERTICAL_ALIGN;
+ hint->status = CSS_VERTICAL_ALIGN_TOP;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_middle)) {
+ hint->prop = CSS_PROP_VERTICAL_ALIGN;
+ hint->status = CSS_VERTICAL_ALIGN_MIDDLE;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_bottom)) {
+ hint->prop = CSS_PROP_VERTICAL_ALIGN;
+ hint->status = CSS_VERTICAL_ALIGN_BOTTOM;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_baseline)) {
+ hint->prop = CSS_PROP_VERTICAL_ALIGN;
+ hint->status = CSS_VERTICAL_ALIGN_BASELINE;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(attr);
+ }
+}
+
+static void css_hint_vertical_align_replaced(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_string *attr = NULL;
+ dom_exception err;
+
+ err = dom_element_get_attribute(node,
+ corestring_dom_valign, &attr);
+
+ if (err == DOM_NO_ERR && attr != NULL) {
+ if (dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_top)) {
+ hint->prop = CSS_PROP_VERTICAL_ALIGN;
+ hint->status = CSS_VERTICAL_ALIGN_TOP;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_bottom) ||
+ dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_baseline)) {
+ hint->prop = CSS_PROP_VERTICAL_ALIGN;
+ hint->status = CSS_VERTICAL_ALIGN_BASELINE;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_texttop)) {
+ hint->prop = CSS_PROP_VERTICAL_ALIGN;
+ hint->status = CSS_VERTICAL_ALIGN_TEXT_TOP;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_absmiddle) ||
+ dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_abscenter)) {
+ hint->prop = CSS_PROP_VERTICAL_ALIGN;
+ hint->status = CSS_VERTICAL_ALIGN_MIDDLE;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(attr);
+ }
+}
+
+static void css_hint_text_align_normal(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_string *align = NULL;
+ dom_exception err;
+
+ err = dom_element_get_attribute(node,
+ corestring_dom_align, &align);
+ if (err == DOM_NO_ERR && align != NULL) {
+ if (dom_string_caseless_lwc_isequal(align,
+ corestring_lwc_left)) {
+ hint->prop = CSS_PROP_TEXT_ALIGN;
+ hint->status = CSS_TEXT_ALIGN_LEFT;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(align,
+ corestring_lwc_center)) {
+ hint->prop = CSS_PROP_TEXT_ALIGN;
+ hint->status = CSS_TEXT_ALIGN_CENTER;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(align,
+ corestring_lwc_right)) {
+ hint->prop = CSS_PROP_TEXT_ALIGN;
+ hint->status = CSS_TEXT_ALIGN_RIGHT;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(align,
+ corestring_lwc_justify)) {
+ hint->prop = CSS_PROP_TEXT_ALIGN;
+ hint->status = CSS_TEXT_ALIGN_JUSTIFY;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(align);
+ }
+}
+
+static void css_hint_text_align_center(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+
+ hint->prop = CSS_PROP_TEXT_ALIGN;
+ hint->status = CSS_TEXT_ALIGN_LIBCSS_CENTER;
+ hint = css_hint_advance(hint);
+}
+
+static void css_hint_margin_left_right_align_center(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_string *attr;
+ dom_exception exc;
+
+ exc = dom_element_get_attribute(node,
+ corestring_dom_align, &attr);
+
+ if (exc == DOM_NO_ERR && attr != NULL) {
+ if (dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_center) ||
+ dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_abscenter) ||
+ dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_middle) ||
+ dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_absmiddle)) {
+ hint->prop = CSS_PROP_MARGIN_LEFT;
+ hint->status = CSS_MARGIN_AUTO;
+ hint = css_hint_advance(hint);
+
+ hint->prop = CSS_PROP_MARGIN_RIGHT;
+ hint->status = CSS_MARGIN_AUTO;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(attr);
+ }
+}
+
+static void css_hint_text_align_special(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_string *align = NULL;
+ dom_exception err;
+
+ err = dom_element_get_attribute(node,
+ corestring_dom_align, &align);
+
+ if (err == DOM_NO_ERR && align != NULL) {
+ if (dom_string_caseless_lwc_isequal(align,
+ corestring_lwc_center)) {
+ hint->prop = CSS_PROP_TEXT_ALIGN;
+ hint->status = CSS_TEXT_ALIGN_LIBCSS_CENTER;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(align,
+ corestring_lwc_left)) {
+ hint->prop = CSS_PROP_TEXT_ALIGN;
+ hint->status = CSS_TEXT_ALIGN_LIBCSS_LEFT;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(align,
+ corestring_lwc_right)) {
+ hint->prop = CSS_PROP_TEXT_ALIGN;
+ hint->status = CSS_TEXT_ALIGN_LIBCSS_RIGHT;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(align,
+ corestring_lwc_justify)) {
+ hint->prop = CSS_PROP_TEXT_ALIGN;
+ hint->status = CSS_TEXT_ALIGN_JUSTIFY;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(align);
+ }
+}
+
+static void css_hint_text_align_table_special(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+
+ hint->prop = CSS_PROP_TEXT_ALIGN;
+ hint->status = CSS_TEXT_ALIGN_INHERIT_IF_NON_MAGIC;
+ hint = css_hint_advance(hint);
+}
+
+static void css_hint_margin_hspace_vspace(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_string *attr = NULL;
+ dom_exception exc;
+
+ exc = dom_element_get_attribute(node,
+ corestring_dom_vspace, &attr);
+
+ if (exc == DOM_NO_ERR && attr != NULL) {
+ css_hint_length hint_length;
+ if (parse_dimension(
+ dom_string_data(attr), false,
+ &hint_length.value,
+ &hint_length.unit)) {
+ hint->prop = CSS_PROP_MARGIN_TOP;
+ hint->data.length.value = hint_length.value;
+ hint->data.length.unit = hint_length.unit;
+ hint->status = CSS_MARGIN_SET;
+ hint = css_hint_advance(hint);
+
+ hint->prop = CSS_PROP_MARGIN_BOTTOM;
+ hint->data.length.value = hint_length.value;
+ hint->data.length.unit = hint_length.unit;
+ hint->status = CSS_MARGIN_SET;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(attr);
+ }
+
+ exc = dom_element_get_attribute(node,
+ corestring_dom_hspace, &attr);
+
+ if (exc == DOM_NO_ERR && attr != NULL) {
+ css_hint_length hint_length;
+ if (parse_dimension(
+ dom_string_data(attr), false,
+ &hint_length.value,
+ &hint_length.unit)) {
+ hint->prop = CSS_PROP_MARGIN_LEFT;
+ hint->data.length.value = hint_length.value;
+ hint->data.length.unit = hint_length.unit;
+ hint->status = CSS_MARGIN_SET;
+ hint = css_hint_advance(hint);
+
+ hint->prop = CSS_PROP_MARGIN_RIGHT;
+ hint->data.length.value = hint_length.value;
+ hint->data.length.unit = hint_length.unit;
+ hint->status = CSS_MARGIN_SET;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(attr);
+ }
+}
+
+static void css_hint_margin_left_right_hr(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_string *attr;
+ dom_exception exc;
+
+ exc = dom_element_get_attribute(node,
+ corestring_dom_align, &attr);
+
+ if (exc == DOM_NO_ERR && attr != NULL) {
+ if (dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_left)) {
+ hint->prop = CSS_PROP_MARGIN_LEFT;
+ hint->data.length.value = 0;
+ hint->data.length.unit = CSS_UNIT_PX;
+ hint->status = CSS_MARGIN_SET;
+ hint = css_hint_advance(hint);
+
+ hint->prop = CSS_PROP_MARGIN_RIGHT;
+ hint->status = CSS_MARGIN_AUTO;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_center)) {
+ hint->prop = CSS_PROP_MARGIN_LEFT;
+ hint->status = CSS_MARGIN_AUTO;
+ hint = css_hint_advance(hint);
+
+ hint->prop = CSS_PROP_MARGIN_RIGHT;
+ hint->status = CSS_MARGIN_AUTO;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(attr,
+ corestring_lwc_right)) {
+ hint->prop = CSS_PROP_MARGIN_LEFT;
+ hint->status = CSS_MARGIN_AUTO;
+ hint = css_hint_advance(hint);
+
+ hint->prop = CSS_PROP_MARGIN_RIGHT;
+ hint->data.length.value = 0;
+ hint->data.length.unit = CSS_UNIT_PX;
+ hint->status = CSS_MARGIN_SET;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(attr);
+ }
+}
+
+static void css_hint_table_spacing_border(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_exception exc;
+ dom_string *attr = NULL;
+
+ exc = dom_element_get_attribute(node, corestring_dom_border, &attr);
+
+ if (exc == DOM_NO_ERR && attr != NULL) {
+ uint32_t hint_prop;
+ css_hint_length hint_length;
+
+ for (hint_prop = CSS_PROP_BORDER_TOP_STYLE;
+ hint_prop <= CSS_PROP_BORDER_LEFT_STYLE;
+ hint_prop++) {
+ hint->prop = hint_prop;
+ hint->status = CSS_BORDER_STYLE_OUTSET;
+ hint = css_hint_advance(hint);
+ }
+
+ if (parse_dimension(
+ dom_string_data(attr), false,
+ &hint_length.value,
+ &hint_length.unit)) {
+
+ for (hint_prop = CSS_PROP_BORDER_TOP_WIDTH;
+ hint_prop <= CSS_PROP_BORDER_LEFT_WIDTH;
+ hint_prop++) {
+ hint->prop = hint_prop;
+ hint->data.length.value = hint_length.value;
+ hint->data.length.unit = hint_length.unit;
+ hint->status = CSS_BORDER_WIDTH_WIDTH;
+ hint = css_hint_advance(hint);
+ }
+ }
+ dom_string_unref(attr);
+ }
+
+ exc = dom_element_get_attribute(node,
+ corestring_dom_bordercolor, &attr);
+
+ if (exc == DOM_NO_ERR && attr != NULL) {
+ uint32_t hint_prop;
+ css_color hint_color;
+
+ if (nscss_parse_colour(
+ (const char *)dom_string_data(attr),
+ &hint_color)) {
+
+ for (hint_prop = CSS_PROP_BORDER_TOP_COLOR;
+ hint_prop <= CSS_PROP_BORDER_LEFT_COLOR;
+ hint_prop++) {
+ hint->prop = hint_prop;
+ hint->data.color = hint_color;
+ hint->status = CSS_BORDER_COLOR_COLOR;
+ hint = css_hint_advance(hint);
+ }
+ }
+ dom_string_unref(attr);
+ }
+
+ exc = dom_element_get_attribute(node,
+ corestring_dom_cellspacing, &attr);
+
+ if (exc == DOM_NO_ERR && attr != NULL) {
+ if (parse_dimension(
+ (const char *)dom_string_data(attr), false,
+ &hint->data.position.h.value,
+ &hint->data.position.h.unit)) {
+ hint->prop = CSS_PROP_BORDER_SPACING;
+ hint->data.position.v = hint->data.position.h;
+ hint->status = CSS_BORDER_SPACING_SET;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(attr);
+ }
+}
+
+static void css_hint_height(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_string *attr = NULL;
+ dom_exception err;
+
+ err = dom_element_get_attribute(node,
+ corestring_dom_height, &attr);
+
+ if (err == DOM_NO_ERR && attr != NULL) {
+ if (parse_dimension(
+ (const char *)dom_string_data(attr), false,
+ &hint->data.length.value,
+ &hint->data.length.unit)) {
+ hint->prop = CSS_PROP_HEIGHT;
+ hint->status = CSS_HEIGHT_SET;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(attr);
+ }
+}
+
+static void css_hint_width(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_string *attr = NULL;
+ dom_exception err;
+
+ err = dom_element_get_attribute(node,
+ corestring_dom_width, &attr);
+
+ if (err == DOM_NO_ERR && attr != NULL) {
+ if (parse_dimension(
+ (const char *)dom_string_data(attr), false,
+ &hint->data.length.value,
+ &hint->data.length.unit)) {
+ hint->prop = CSS_PROP_WIDTH;
+ hint->status = CSS_WIDTH_SET;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(attr);
+ }
+}
+
+static void css_hint_height_width_textarea(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_string *attr = NULL;
+ dom_exception err;
+
+ err = dom_element_get_attribute(node,
+ corestring_dom_rows, &attr);
+
+ if (err == DOM_NO_ERR && attr != NULL) {
+ if (parse_dimension(
+ (const char *)dom_string_data(attr), false,
+ &hint->data.length.value,
+ &hint->data.length.unit)) {
+ hint->prop = CSS_PROP_HEIGHT;
+ hint->data.length.unit = CSS_UNIT_EM;
+ hint->status = CSS_HEIGHT_SET;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(attr);
+ }
+
+ err = dom_element_get_attribute(node,
+ corestring_dom_cols, &attr);
+
+ if (err == DOM_NO_ERR && attr != NULL) {
+ if (parse_dimension(
+ (const char *)dom_string_data(attr), false,
+ &hint->data.length.value,
+ &hint->data.length.unit)) {
+ hint->prop = CSS_PROP_WIDTH;
+ hint->data.length.unit = CSS_UNIT_EX;
+ hint->status = CSS_WIDTH_SET;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(attr);
+ }
+}
+
+static void css_hint_width_input(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &(hint_ctx.hints[hint_ctx.len]);
+ dom_string *attr = NULL;
+ dom_exception err;
+
+ err = dom_element_get_attribute(node,
+ corestring_dom_size, &attr);
+
+ if (err == DOM_NO_ERR && attr != NULL) {
+ if (parse_dimension(
+ (const char *)dom_string_data(attr), false,
+ &hint->data.length.value,
+ &hint->data.length.unit)) {
+ dom_string *attr2 = NULL;
+
+ err = dom_element_get_attribute(node,
+ corestring_dom_type, &attr2);
+ if (err == DOM_NO_ERR) {
+
+ hint->prop = CSS_PROP_WIDTH;
+ hint->status = CSS_WIDTH_SET;
+
+ if (attr2 == NULL ||
+ dom_string_caseless_lwc_isequal(
+ attr2,
+ corestring_lwc_text) ||
+ dom_string_caseless_lwc_isequal(
+ attr2,
+ corestring_lwc_search) ||
+ dom_string_caseless_lwc_isequal(
+ attr2,
+ corestring_lwc_password) ||
+ dom_string_caseless_lwc_isequal(
+ attr2,
+ corestring_lwc_file)) {
+ hint->data.length.unit = CSS_UNIT_EX;
+ }
+ if (attr2 != NULL) {
+ dom_string_unref(attr2);
+ }
+ hint = css_hint_advance(hint);
+ }
+ }
+ dom_string_unref(attr);
+ }
+}
+
+static void css_hint_anchor_color(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ css_error error;
+ dom_exception err;
+ dom_string *color;
+ dom_node *bodynode = NULL;
+
+ /* find body node */
+ css_qname qs;
+ 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);
+ return ;
+ }
+ lwc_string_unref(qs.name);
+
+ if (bodynode == NULL) {
+ return;
+ }
+
+ error = node_is_visited(ctx, node, &is_visited);
+ if (error != CSS_OK)
+ return;
+
+ if (is_visited) {
+ err = dom_element_get_attribute(bodynode,
+ corestring_dom_vlink, &color);
+ } else {
+ err = dom_element_get_attribute(bodynode,
+ corestring_dom_link, &color);
+ }
+
+ if (err == DOM_NO_ERR && color != NULL) {
+ if (nscss_parse_colour(
+ (const char *)dom_string_data(color),
+ &hint->data.color)) {
+ hint->prop = CSS_PROP_COLOR;
+ hint->status = CSS_COLOR_COLOR;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(color);
+ }
+}
+
+static void css_hint_body_color(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_exception err;
+ dom_string *color;
+
+ err = dom_element_get_attribute(node, corestring_dom_text, &color);
+
+ if (err == DOM_NO_ERR && color != NULL) {
+ if (nscss_parse_colour(
+ (const char *)dom_string_data(color),
+ &hint->data.color)) {
+ hint->prop = CSS_PROP_COLOR;
+ hint->status = CSS_COLOR_COLOR;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(color);
+ }
+}
+
+static void css_hint_color(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_exception err;
+ dom_string *color;
+
+ err = dom_element_get_attribute(node, corestring_dom_color, &color);
+
+ if (err == DOM_NO_ERR && color != NULL) {
+ if (nscss_parse_colour(
+ (const char *)dom_string_data(color),
+ &hint->data.color)) {
+ hint->prop = CSS_PROP_COLOR;
+ hint->status = CSS_COLOR_COLOR;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(color);
+ }
+}
+
+static void css_hint_font_size(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_exception err;
+ dom_string *size;
+
+ err = dom_element_get_attribute(node, corestring_dom_size, &size);
+ if (err == DOM_NO_ERR && size != NULL) {
+ if (parse_font_size(
+ (const char *)dom_string_data(size),
+ &hint->status,
+ &hint->data.length.value,
+ &hint->data.length.unit)) {
+ hint->prop = CSS_PROP_FONT_SIZE;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(size);
+ }
+}
+
+static void css_hint_float(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_exception err;
+ dom_string *align;
+
+ err = dom_element_get_attribute(node, corestring_dom_align, &align);
+ if (err == DOM_NO_ERR && align != NULL) {
+ if (dom_string_caseless_lwc_isequal(align,
+ corestring_lwc_left)) {
+ hint->prop = CSS_PROP_FLOAT;
+ hint->status = CSS_FLOAT_LEFT;
+ hint = css_hint_advance(hint);
+
+ } else if (dom_string_caseless_lwc_isequal(align,
+ corestring_lwc_right)) {
+ hint->prop = CSS_PROP_FLOAT;
+ hint->status = CSS_FLOAT_RIGHT;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(align);
+ }
+}
+
+static void css_hint_caption_side(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_exception err;
+ dom_string *align = NULL;
+
+ err = dom_element_get_attribute(node, corestring_dom_align, &align);
+ if (err == DOM_NO_ERR && align != NULL) {
+ if (dom_string_caseless_lwc_isequal(align,
+ corestring_lwc_bottom)) {
+ hint->prop = CSS_PROP_CAPTION_SIDE;
+ hint->status = CSS_CAPTION_SIDE_BOTTOM;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(align);
+ }
+}
+
+static void css_hint_bg_color(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &hint_ctx.hints[hint_ctx.len];
+ dom_exception err;
+ dom_string *bgcolor;
+
+ err = dom_element_get_attribute(node,
+ corestring_dom_bgcolor, &bgcolor);
+ if (err == DOM_NO_ERR && bgcolor != NULL) {
+ if (nscss_parse_colour(
+ (const char *)dom_string_data(bgcolor),
+ &hint->data.color)) {
+ hint->prop = CSS_PROP_BACKGROUND_COLOR;
+ hint->status = CSS_BACKGROUND_COLOR_COLOR;
+ hint = css_hint_advance(hint);
+ }
+ dom_string_unref(bgcolor);
+ }
+}
+
+static void css_hint_bg_image(
+ nscss_select_ctx *ctx,
+ dom_node *node)
+{
+ struct css_hint *hint = &(hint_ctx.hints[hint_ctx.len]);
+ dom_exception err;
+ dom_string *attr;
+
+ err = dom_element_get_attribute(node,
+ corestring_dom_background, &attr);
+ if (err == DOM_NO_ERR && attr != NULL) {
+ nsurl *url;
+ nserror error = nsurl_join(ctx->base_url,
+ (const char *)dom_string_data(attr), &url);
+ dom_string_unref(attr);
+
+ if (error == NSERROR_OK) {
+ lwc_string *iurl;
+ lwc_error lerror = lwc_intern_string(nsurl_access(url),
+ nsurl_length(url), &iurl);
+ nsurl_unref(url);
+
+ if (lerror == lwc_error_ok) {
+ hint->prop = CSS_PROP_BACKGROUND_IMAGE;
+ hint->data.string = iurl;
+ hint->status = CSS_BACKGROUND_IMAGE_IMAGE;
+ hint = css_hint_advance(hint);
+ }
+ }
+ }
+}
+
+
+/* Exported function, documeted in css/hints.h */
+css_error node_presentational_hint(void *pw, void *node,
+ uint32_t *nhints, css_hint **hints)
+{
+ dom_exception exc;
+ dom_html_element_type tag_type;
+
+ css_hint_clean();
+
+ exc = dom_html_element_get_tag_type(node, &tag_type);
+ if (exc != DOM_NO_ERR) {
+ tag_type = DOM_HTML_ELEMENT_TYPE__UNKNOWN;
+ }
+
+ switch (tag_type) {
+ case DOM_HTML_ELEMENT_TYPE_TH:
+ case DOM_HTML_ELEMENT_TYPE_TD:
+ css_hint_width(pw, node);
+ css_hint_table_cell_border_padding(pw, node);
+ /* fallthrough */
+ case DOM_HTML_ELEMENT_TYPE_TR:
+ css_hint_height(pw, node);
+ /* fallthrough */
+ case DOM_HTML_ELEMENT_TYPE_THEAD:
+ case DOM_HTML_ELEMENT_TYPE_TBODY:
+ case DOM_HTML_ELEMENT_TYPE_TFOOT:
+ css_hint_text_align_special(pw, node);
+ /* fallthrough */
+ case DOM_HTML_ELEMENT_TYPE_COL:
+ css_hint_vertical_align_table_cells(pw, node);
+ break;
+ case DOM_HTML_ELEMENT_TYPE_APPLET:
+ case DOM_HTML_ELEMENT_TYPE_IMG:
+ css_hint_margin_hspace_vspace(pw, node);
+ /* fallthrough */
+ case DOM_HTML_ELEMENT_TYPE_EMBED:
+ case DOM_HTML_ELEMENT_TYPE_IFRAME:
+ case DOM_HTML_ELEMENT_TYPE_OBJECT:
+ css_hint_height(pw, node);
+ css_hint_width(pw, node);
+ css_hint_vertical_align_replaced(pw, node);
+ css_hint_float(pw, node);
+ break;
+ case DOM_HTML_ELEMENT_TYPE_P:
+ case DOM_HTML_ELEMENT_TYPE_H1:
+ case DOM_HTML_ELEMENT_TYPE_H2:
+ case DOM_HTML_ELEMENT_TYPE_H3:
+ case DOM_HTML_ELEMENT_TYPE_H4:
+ case DOM_HTML_ELEMENT_TYPE_H5:
+ case DOM_HTML_ELEMENT_TYPE_H6:
+ css_hint_text_align_normal(pw, node);
+ break;
+ case DOM_HTML_ELEMENT_TYPE_CENTER:
+ css_hint_text_align_center(pw, node);
+ break;
+ case DOM_HTML_ELEMENT_TYPE_CAPTION:
+ css_hint_caption_side(pw, node);
+ /* fallthrough */
+ case DOM_HTML_ELEMENT_TYPE_DIV:
+ css_hint_text_align_special(pw, node);
+ break;
+ case DOM_HTML_ELEMENT_TYPE_TABLE:
+ css_hint_text_align_table_special(pw, node);
+ css_hint_table_spacing_border(pw, node);
+ css_hint_float(pw, node);
+ css_hint_margin_left_right_align_center(pw, node);
+ css_hint_width(pw, node);
+ break;
+ case DOM_HTML_ELEMENT_TYPE_HR:
+ css_hint_margin_left_right_hr(pw, node);
+ break;
+ case DOM_HTML_ELEMENT_TYPE_TEXTAREA:
+ css_hint_height_width_textarea(pw, node);
+ break;
+ case DOM_HTML_ELEMENT_TYPE_INPUT:
+ css_hint_width_input(pw, node);
+ break;
+ case DOM_HTML_ELEMENT_TYPE_A:
+ css_hint_anchor_color(pw, node);
+ break;
+ case DOM_HTML_ELEMENT_TYPE_FONT:
+ css_hint_font_size(pw, node);
+ break;
+ case DOM_HTML_ELEMENT_TYPE_BODY:
+ css_hint_body_color(pw, node);
+ break;
+ default:
+ break;
+ }
+
+ if (tag_type != DOM_HTML_ELEMENT_TYPE__UNKNOWN) {
+ css_hint_color(pw, node);
+ css_hint_bg_color(pw, node);
+ css_hint_bg_image(pw, node);
+ }
+
+#ifdef LOG_STATS
+ LOG("Properties with hints: %i", hint_ctx.len);
+#endif
+
+ css_hint_get_hints(hints, nhints);
+
+ return CSS_OK;
+}