From 64d8f9d5edca090ff3102da72d724905dc1676fd Mon Sep 17 00:00:00 2001 From: James Bursa Date: Sat, 26 Mar 2005 01:12:27 +0000 Subject: [project @ 2005-03-26 01:12:27 by bursa] Split box.c into box_construct.c, box_normalise.c, and box.c. svn path=/import/netsurf/; revision=1583 --- render/box.c | 3241 +++------------------------------------------------------- 1 file changed, 116 insertions(+), 3125 deletions(-) (limited to 'render/box.c') diff --git a/render/box.c b/render/box.c index 3cc8d805f..8d21d7a01 100644 --- a/render/box.c +++ b/render/box.c @@ -2,220 +2,30 @@ * This file is part of NetSurf, http://netsurf.sourceforge.net/ * Licensed under the GNU General Public License, * http://www.opensource.org/licenses/gpl-license - * Copyright 2004 James Bursa + * Copyright 2005 James Bursa * Copyright 2003 Phil Mellor - * Copyright 2003 John M Bell + * Copyright 2005 John M Bell */ /** \file - * Conversion of XML tree to box tree (implementation). + * Box tree manipulation (implementation). */ -#define _GNU_SOURCE /* for strndup */ #include -#include -#include #include -#include #include -#include "libxml/HTMLparser.h" -#include "netsurf/utils/config.h" #include "netsurf/content/content.h" #include "netsurf/css/css.h" -#include "netsurf/desktop/options.h" #include "netsurf/render/box.h" -#include "netsurf/render/font.h" #include "netsurf/render/form.h" -#include "netsurf/render/html.h" -#ifdef riscos -#include "netsurf/desktop/gui.h" -#endif -#ifdef WITH_PLUGIN -#include "netsurf/riscos/plugin.h" -#endif -#define NDEBUG -#include "netsurf/utils/log.h" -#include "netsurf/utils/messages.h" #include "netsurf/utils/pool.h" -#include "netsurf/utils/url.h" -#include "netsurf/utils/utils.h" - - -/** Status of box tree construction. */ -struct box_status { - struct content *content; - char *href; - char *title; - struct form *current_form; - char *id; -}; - -/** Return type for special case element functions. */ -struct box_result { - /** Box for element, if any, 0 otherwise. */ - struct box *box; - /** Children of this element should be converted. */ - bool convert_children; - /** Memory was exhausted when handling the element. */ - bool memory_error; -}; - -/** MultiLength, as defined by HTML 4.01. */ -struct box_multi_length { - enum { LENGTH_PX, LENGTH_PERCENT, LENGTH_RELATIVE } type; - float value; -}; - -static const content_type image_types[] = { -#ifdef WITH_JPEG - CONTENT_JPEG, -#endif -#ifdef WITH_GIF - CONTENT_GIF, -#endif -#ifdef WITH_PNG - CONTENT_PNG, -#endif -#ifdef WITH_MNG - CONTENT_JNG, - CONTENT_MNG, -#endif -#ifdef WITH_SPRITE - CONTENT_SPRITE, -#endif -#ifdef WITH_DRAW - CONTENT_DRAW, -#endif - CONTENT_UNKNOWN }; - -#define MAX_SPAN ( 100 ) - -struct span_info { - unsigned int row_span; - bool auto_row; - bool auto_column; -}; - -struct columns { - unsigned int current_column; - bool extra; - /* Number of columns in main part of table 1..max columns */ - unsigned int num_columns; - /* Information about columns in main table, - array 0 to num_columns - 1 */ - struct span_info *spans; - /* Number of columns that have cells after a colspan 0 */ - unsigned int extra_columns; - /* Number of rows in table */ - unsigned int num_rows; -}; - -static bool convert_xml_to_box(xmlNode *n, struct content *content, - struct css_style *parent_style, - struct box *parent, struct box **inline_container, - struct box_status status); -struct css_style * box_get_style(struct content *c, - struct css_style *parent_style, - xmlNode *n); -static void box_text_transform(char *s, unsigned int len, - css_text_transform tt); -static struct box_result box_a(xmlNode *n, struct box_status *status, - struct css_style *style); -static struct box_result box_body(xmlNode *n, struct box_status *status, - struct css_style *style); -static struct box_result box_br(xmlNode *n, struct box_status *status, - struct css_style *style); -static struct box_result box_image(xmlNode *n, struct box_status *status, - struct css_style *style); -static struct box_result box_form(xmlNode *n, struct box_status *status, - struct css_style *style); -static struct box_result box_textarea(xmlNode *n, struct box_status *status, - struct css_style *style); -static struct box_result box_select(xmlNode *n, struct box_status *status, - struct css_style *style); -static struct box_result box_input(xmlNode *n, struct box_status *status, - struct css_style *style); -static struct box *box_input_text(xmlNode *n, struct box_status *status, - struct css_style *style, bool password); -static struct box_result box_button(xmlNode *n, struct box_status *status, - struct css_style *style); -static struct box_result box_frameset(xmlNode *n, struct box_status *status, - struct css_style *style); -static bool box_select_add_option(struct form_control *control, xmlNode *n); -static bool box_normalise_block(struct box *block, pool box_pool); -static bool box_normalise_table(struct box *table, pool box_pool); -static void box_normalise_table_spans( struct box *table ); -static bool box_normalise_table_row_group(struct box *row_group, - struct columns *col_info, - pool box_pool); -static bool box_normalise_table_row(struct box *row, - struct columns *col_info, - pool box_pool); -static bool calculate_table_row(struct columns *col_info, - unsigned int col_span, unsigned int row_span, - unsigned int *start_column); -static bool box_normalise_inline_container(struct box *cont, pool box_pool); -static void box_free_box(struct box *box); -static void box_free_object_params(struct object_params *op); -static struct box_result box_object(xmlNode *n, struct box_status *status, - struct css_style *style); -static struct box_result box_embed(xmlNode *n, struct box_status *status, - struct css_style *style); -static struct box_result box_applet(xmlNode *n, struct box_status *status, - struct css_style *style); -static struct box_result box_iframe(xmlNode *n, struct box_status *status, - struct css_style *style); -static bool plugin_decode(struct content* content, struct box* box); -static struct box_multi_length *box_parse_multi_lengths(const char *s, - unsigned int *count); + + static bool box_contains_point(struct box *box, int x, int y); #define box_is_float(box) (box->type == BOX_FLOAT_LEFT || \ box->type == BOX_FLOAT_RIGHT) -/* element_table must be sorted by name */ -struct element_entry { - char name[10]; /* element type */ - struct box_result (*convert)(xmlNode *n, struct box_status *status, - struct css_style *style); -}; -static const struct element_entry element_table[] = { - {"a", box_a}, -/* {"applet", box_applet},*/ - {"body", box_body}, - {"br", box_br}, - {"button", box_button}, - {"embed", box_embed}, - {"form", box_form}, - {"frameset", box_frameset}, - {"iframe", box_iframe}, - {"img", box_image}, - {"input", box_input}, - {"object", box_object}, - {"select", box_select}, - {"textarea", box_textarea} -}; -#define ELEMENT_TABLE_COUNT (sizeof(element_table) / sizeof(element_table[0])) - - -/** - * Add a child to a box tree node. - */ - -void box_add_child(struct box * parent, struct box * child) -{ - if (parent->children != 0) { /* has children already */ - parent->last->next = child; - child->prev = parent->last; - } else { /* this is the first child */ - parent->children = child; - child->prev = 0; - } - - parent->last = child; - child->parent = parent; -} - /** * Create a box tree node. @@ -228,7 +38,7 @@ void box_add_child(struct box * parent, struct box * child) * \return allocated and initialised box, or 0 on memory exhaustion */ -struct box * box_create(struct css_style * style, +struct box * box_create(struct css_style *style, const char *href, const char *title, const char *id, pool box_pool) { @@ -301,2136 +111,54 @@ struct box * box_create(struct css_style * style, /** - * Insert a new box as a sibling to a box in a tree. - */ - -void box_insert_sibling(struct box *box, struct box *new_box) -{ - new_box->parent = box->parent; - new_box->prev = box; - new_box->next = box->next; - box->next = new_box; - if (new_box->next) - new_box->next->prev = new_box; - else if (new_box->parent) - new_box->parent->last = new_box; -} - - -/** - * Construct a box tree from an xml tree and stylesheets. - * - * \param n xml tree - * \param c content of type CONTENT_HTML to construct box tree in - * \return true on success, false on memory exhaustion - */ - -bool xml_to_box(xmlNode *n, struct content *c) -{ - struct box root; - struct box_status status = {c, 0, 0, 0, 0}; - struct box *inline_container = 0; - - assert(c->type == CONTENT_HTML); - - root.type = BOX_BLOCK; - root.style = NULL; - root.next = NULL; - root.prev = NULL; - root.children = NULL; - root.last = NULL; - root.parent = NULL; - root.float_children = NULL; - root.next_float = NULL; - - c->data.html.style = css_duplicate_style(&css_base_style); - if (!c->data.html.style) - return false; - c->data.html.style->font_size.value.length.value = - option_font_size * 0.1; - - c->data.html.object_count = 0; - c->data.html.object = 0; - - if (!convert_xml_to_box(n, c, c->data.html.style, &root, - &inline_container, status)) - return false; - if (!box_normalise_block(&root, c->data.html.box_pool)) - return false; - - c->data.html.layout = root.children; - c->data.html.layout->parent = NULL; - - return true; -} - - -/* mapping from CSS display to box type - * this table must be in sync with css/css_enums */ -static const box_type box_map[] = { - 0, /*CSS_DISPLAY_INHERIT,*/ - BOX_INLINE, /*CSS_DISPLAY_INLINE,*/ - BOX_BLOCK, /*CSS_DISPLAY_BLOCK,*/ - BOX_BLOCK, /*CSS_DISPLAY_LIST_ITEM,*/ - BOX_INLINE, /*CSS_DISPLAY_RUN_IN,*/ - BOX_INLINE_BLOCK, /*CSS_DISPLAY_INLINE_BLOCK,*/ - BOX_TABLE, /*CSS_DISPLAY_TABLE,*/ - BOX_TABLE, /*CSS_DISPLAY_INLINE_TABLE,*/ - BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_ROW_GROUP,*/ - BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_HEADER_GROUP,*/ - BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_FOOTER_GROUP,*/ - BOX_TABLE_ROW, /*CSS_DISPLAY_TABLE_ROW,*/ - BOX_INLINE, /*CSS_DISPLAY_TABLE_COLUMN_GROUP,*/ - BOX_INLINE, /*CSS_DISPLAY_TABLE_COLUMN,*/ - BOX_TABLE_CELL, /*CSS_DISPLAY_TABLE_CELL,*/ - BOX_INLINE /*CSS_DISPLAY_TABLE_CAPTION,*/ -}; - - -/** - * Recursively construct a box tree from an xml tree and stylesheets. - * - * \param n fragment of xml tree - * \param content content of type CONTENT_HTML that is being processed - * \param parent_style style at this point in xml tree - * \param parent parent in box tree - * \param inline_container current inline container box, or 0, updated to - * new current inline container on exit - * \param status status for forms etc. - * \return true on success, false on memory exhaustion - */ - -bool convert_xml_to_box(xmlNode *n, struct content *content, - struct css_style *parent_style, - struct box *parent, struct box **inline_container, - struct box_status status) -{ - struct box *box = 0; - struct box *inline_container_c; - struct css_style *style = 0; - xmlNode *c; - char *s; - xmlChar *title0, *id0; - char *title = 0, *id = 0; - bool convert_children = true; - char *href_in = status.href; - - assert(n); - assert(parent_style); - assert(parent); - assert(inline_container); - - if (n->type == XML_ELEMENT_NODE) { - struct element_entry *element; - - gui_multitask(); - - style = box_get_style(content, parent_style, n); - if (!style) - goto no_memory; - if (style->display == CSS_DISPLAY_NONE) { - css_free_style(style); - goto end; - } - /* floats are treated as blocks */ - if (style->float_ == CSS_FLOAT_LEFT || - style->float_ == CSS_FLOAT_RIGHT) - if (style->display == CSS_DISPLAY_INLINE) - style->display = CSS_DISPLAY_BLOCK; - - /* extract title attribute, if present */ - if ((title0 = xmlGetProp(n, (const xmlChar *) "title"))) { - status.title = title = squash_whitespace(title0); - xmlFree(title0); - if (!title) - goto no_memory; - } - - /* extract id attribute, if present */ - if ((id0 = xmlGetProp(n, (const xmlChar *) "id"))) { - status.id = id = squash_whitespace(id0); - xmlFree(id0); - if (!id) - goto no_memory; - } - - /* special elements */ - element = bsearch((const char *) n->name, element_table, - ELEMENT_TABLE_COUNT, sizeof(element_table[0]), - (int (*)(const void *, const void *)) strcmp); - if (element) { - /* a special convert function exists for this element */ - struct box_result res = - element->convert(n, &status, style); - box = res.box; - convert_children = res.convert_children; - if (res.memory_error) - goto no_memory; - if (!box) { - /* no box for this element */ - assert(!convert_children); - css_free_style(style); - goto end; - } - } else { - /* general element */ - box = box_create(style, status.href, title, id, - content->data.html.box_pool); - if (!box) - goto no_memory; - } - /* set box type from style if it has not been set already */ - if (box->type == BOX_INLINE) - box->type = box_map[style->display]; - - } else if (n->type == XML_TEXT_NODE) { - /* text node: added to inline container below */ - - } else { - /* not an element or text node: ignore it (eg. comment) */ - goto end; - } - - content->size += sizeof(struct box) + sizeof(struct css_style); - - if (n->type == XML_TEXT_NODE && - (parent_style->white_space == CSS_WHITE_SPACE_NORMAL || - parent_style->white_space == CSS_WHITE_SPACE_NOWRAP)) { - char *text = squash_whitespace(n->content); - if (!text) - goto no_memory; - - /* if the text is just a space, combine it with the preceding - * text node, if any */ - if (text[0] == ' ' && text[1] == 0) { - if (*inline_container) { - assert((*inline_container)->last != 0); - (*inline_container)->last->space = 1; - } - free(text); - goto end; - } - - if (!*inline_container) { - /* this is the first inline node: make a container */ - *inline_container = box_create(0, 0, 0, 0, - content->data.html.box_pool); - if (!*inline_container) { - free(text); - goto no_memory; - } - (*inline_container)->type = BOX_INLINE_CONTAINER; - box_add_child(parent, *inline_container); - } - - box = box_create(parent_style, status.href, title, id, - content->data.html.box_pool); - if (!box) { - free(text); - goto no_memory; - } - box->text = text; - box->style_clone = 1; - box->length = strlen(text); - /* strip ending space char off */ - if (box->length > 1 && text[box->length - 1] == ' ') { - box->space = 1; - box->length--; - } - if (parent_style->text_transform != CSS_TEXT_TRANSFORM_NONE) - box_text_transform(box->text, box->length, - parent_style->text_transform); - if (parent_style->white_space == CSS_WHITE_SPACE_NOWRAP) { - unsigned int i; - for (i = 0; i != box->length && text[i] != ' '; ++i) - ; /* no body */ - if (i != box->length) { - /* there is a space in text block and we - * want all spaces to be converted to NBSP - */ - box->text = cnv_space2nbsp(text); - if (!box->text) { - free(text); - goto no_memory; - } - box->length = strlen(box->text); - } - } - - box_add_child(*inline_container, box); - if (box->text[0] == ' ') { - box->length--; - memmove(box->text, &box->text[1], box->length); - if (box->prev != NULL) - box->prev->space = 1; - } - goto end; - - } else if (n->type == XML_TEXT_NODE) { - /* white-space: pre */ - char *text = cnv_space2nbsp(n->content); - char *current; - /* note: pre-wrap/pre-line are unimplemented */ - assert(parent_style->white_space == CSS_WHITE_SPACE_PRE || - parent_style->white_space == - CSS_WHITE_SPACE_PRE_LINE || - parent_style->white_space == - CSS_WHITE_SPACE_PRE_WRAP); - if (!text) - goto no_memory; - if (parent_style->text_transform != CSS_TEXT_TRANSFORM_NONE) - box_text_transform(text, strlen(text), - parent_style->text_transform); - current = text; - do { - size_t len = strcspn(current, "\r\n"); - char old = current[len]; - current[len] = 0; - if (!*inline_container) { - *inline_container = box_create(0, 0, 0, 0, - content->data.html.box_pool); - if (!*inline_container) { - free(text); - goto no_memory; - } - (*inline_container)->type = - BOX_INLINE_CONTAINER; - box_add_child(parent, *inline_container); - } - box = box_create(parent_style, status.href, title, - id, content->data.html.box_pool); - if (!box) { - free(text); - goto no_memory; - } - box->type = BOX_INLINE; - box->style_clone = 1; - box->text = strdup(current); - if (!box->text) { - free(text); - goto no_memory; - } - box->length = strlen(box->text); - box_add_child(*inline_container, box); - current[len] = old; - current += len; - if (current[0] == '\r' && current[1] == '\n') { - current += 2; - *inline_container = 0; - } else if (current[0] != 0) { - current++; - *inline_container = 0; - } - } while (*current); - free(text); - goto end; - - } else if (box->type == BOX_INLINE || - box->type == BOX_INLINE_BLOCK || - style->float_ == CSS_FLOAT_LEFT || - style->float_ == CSS_FLOAT_RIGHT || - box->type == BOX_BR) { - /* this is an inline box */ - if (!*inline_container) { - /* this is the first inline node: make a container */ - *inline_container = box_create(0, 0, 0, 0, - content->data.html.box_pool); - if (!*inline_container) - goto no_memory; - (*inline_container)->type = BOX_INLINE_CONTAINER; - box_add_child(parent, *inline_container); - } - - if (box->type == BOX_INLINE || box->type == BOX_BR) { - /* inline box: add to tree and recurse */ - box_add_child(*inline_container, box); - if (convert_children) { - for (c = n->children; c != 0; c = c->next) - if (!convert_xml_to_box(c, content, - style, parent, - inline_container, - status)) - goto no_memory; - } - goto end; - } else if (box->type == BOX_INLINE_BLOCK) { - /* inline block box: add to tree and recurse */ - box_add_child(*inline_container, box); - if (convert_children) { - inline_container_c = 0; - for (c = n->children; c != 0; c = c->next) - if (!convert_xml_to_box(c, content, - style, box, - &inline_container_c, - status)) - goto no_memory; - } - goto end; - } else { - /* float: insert a float box between the parent and - * current node */ - assert(style->float_ == CSS_FLOAT_LEFT || - style->float_ == CSS_FLOAT_RIGHT); - parent = box_create(0, status.href, title, id, - content->data.html.box_pool); - if (!parent) - goto no_memory; - if (style->float_ == CSS_FLOAT_LEFT) - parent->type = BOX_FLOAT_LEFT; - else - parent->type = BOX_FLOAT_RIGHT; - box_add_child(*inline_container, parent); - if (box->type == BOX_INLINE || - box->type == BOX_INLINE_BLOCK) - box->type = BOX_BLOCK; - } - } - - assert(n->type == XML_ELEMENT_NODE); - - /* non-inline box: add to tree and recurse */ - box_add_child(parent, box); - if (convert_children) { - inline_container_c = 0; - for (c = n->children; c != 0; c = c->next) - if (!convert_xml_to_box(c, content, style, - box, &inline_container_c, status)) - goto no_memory; - } - if (style->float_ == CSS_FLOAT_NONE) - /* new inline container unless this is a float */ - *inline_container = 0; - - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "colspan")) != NULL) { - box->columns = strtol(s, NULL, 10); - if ( MAX_SPAN < box->columns ) { - box->columns = 1; - } - xmlFree(s); - } - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "rowspan")) != NULL) { - box->rows = strtol(s, NULL, 10); - if ( MAX_SPAN < box->rows ) { - box->rows = 1; - } - xmlFree(s); - } - -end: - free(title); - free(id); - if (!href_in) - xmlFree(status.href); - - /* Now fetch any background image for this box */ - if (box && box->style && box->style->background_image.type == - CSS_BACKGROUND_IMAGE_URI) { - char *url = strdup(box->style->background_image.uri); - if (!url) - return false; - /* start fetch */ - if (!html_fetch_object(content, url, box, image_types, - content->available_width, 1000, true)) - return false; - } - - return true; - -no_memory: - free(title); - free(id); - if (!href_in) - xmlFree(status.href); - if (style && !box) - css_free_style(style); - - return false; -} - - -/** - * Get the style for an element. - * - * \param c content of type CONTENT_HTML that is being processed - * \param parent_style style at this point in xml tree - * \param n node in xml tree - * \return the new style, or 0 on memory exhaustion - * - * The style is collected from three sources: - * 1. any styles for this element in the document stylesheet(s) - * 2. non-CSS HTML attributes - * 3. the 'style' attribute - */ - -struct css_style * box_get_style(struct content *c, - struct css_style *parent_style, - xmlNode *n) -{ - char *s; - unsigned int i; - unsigned int stylesheet_count = c->data.html.stylesheet_count; - struct content **stylesheet = c->data.html.stylesheet_content; - struct css_style *style; - struct css_style *style_new; - char *url; - url_func_result res; - - style = css_duplicate_style(parent_style); - if (!style) - return 0; - - style_new = css_duplicate_style(&css_blank_style); - if (!style_new) { - css_free_style(style); - return 0; - } - - for (i = 0; i != stylesheet_count; i++) { - if (stylesheet[i]) { - assert(stylesheet[i]->type == CONTENT_CSS); - css_get_style(stylesheet[i], n, style_new); - } - } - css_cascade(style, style_new); - - /* style_new isn't needed past this point */ - css_free_style(style_new); - - /* This property only applies to the body element, if you believe - * the spec. Many browsers seem to allow it on other elements too, - * so let's be generic ;) */ - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "background"))) { - res = url_join(s, c->data.html.base_url, &url); - if (res == URL_FUNC_NOMEM) { - css_free_style(style); - return 0; - } else if (res == URL_FUNC_OK) { - /* if url is equivalent to the parent's url, - * we've got infinite inclusion: ignore */ - if (strcmp(url, c->data.html.base_url) == 0) - free(url); - else { - style->background_image.type = - CSS_BACKGROUND_IMAGE_URI; - style->background_image.uri = url; - } - } - xmlFree(s); - } - - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "bgcolor")) != NULL) { - unsigned int r, g, b; - if (s[0] == '#' && sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3) - style->background_color = (b << 16) | (g << 8) | r; - else if (s[0] != '#') - style->background_color = named_colour(s); - xmlFree(s); - } - - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "color")) != NULL) { - unsigned int r, g, b; - if (s[0] == '#' && sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3) - style->color = (b << 16) | (g << 8) | r; - else if (s[0] != '#') - style->color = named_colour(s); - xmlFree(s); - } - - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "height")) != NULL) { - float value = atof(s); - if (value < 0 || strlen(s) == 0) { - /* ignore negative values and height="" */ - } else if (strrchr(s, '%')) { - /* the specification doesn't make clear what - * percentage heights mean, so ignore them */ - } else { - style->height.height = CSS_HEIGHT_LENGTH; - style->height.length.unit = CSS_UNIT_PX; - style->height.length.value = value; - } - xmlFree(s); - } - - if (strcmp((const char *) n->name, "input") == 0) { - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "size")) != NULL) { - int size = atoi(s); - if (0 < size) { - char *type = (char *) xmlGetProp(n, (const xmlChar *) "type"); - style->width.width = CSS_WIDTH_LENGTH; - if (!type || strcasecmp(type, "text") == 0 || - strcasecmp(type, "password") == 0) - /* in characters for text, password, file */ - style->width.value.length.unit = CSS_UNIT_EX; - else if (strcasecmp(type, "file") != 0) - /* in pixels otherwise */ - style->width.value.length.unit = CSS_UNIT_PX; - style->width.value.length.value = size; - if (type) - xmlFree(type); - } - xmlFree(s); - } - } - - if (strcmp((const char *) n->name, "body") == 0) { - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "text")) != NULL) { - unsigned int r, g, b; - if (s[0] == '#' && sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3) - style->color = (b << 16) | (g << 8) | r; - else if (s[0] != '#') - style->color = named_colour(s); - xmlFree(s); - } - } - - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "width")) != NULL) { - float value = atof(s); - if (value < 0 || strlen(s) == 0) { - /* ignore negative values and width="" */ - } else if (strrchr(s, '%')) { - style->width.width = CSS_WIDTH_PERCENT; - style->width.value.percent = value; - } else { - style->width.width = CSS_WIDTH_LENGTH; - style->width.value.length.unit = CSS_UNIT_PX; - style->width.value.length.value = value; - } - xmlFree(s); - } - - if (strcmp((const char *) n->name, "textarea") == 0) { - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "rows")) != NULL) { - int value = atoi(s); - if (0 < value) { - style->height.height = CSS_HEIGHT_LENGTH; - style->height.length.unit = CSS_UNIT_EM; - style->height.length.value = value; - } - xmlFree(s); - } - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "cols")) != NULL) { - int value = atoi(s); - if (0 < value) { - style->width.width = CSS_WIDTH_LENGTH; - style->width.value.length.unit = CSS_UNIT_EX; - style->width.value.length.value = value; - } - xmlFree(s); - } - } - - if (strcmp((const char *) n->name, "table") == 0) { - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "cellspacing"))) { - if (!strrchr(s, '%')) { /* % not implemented */ - int value = atoi(s); - if (0 <= value) { - style->border_spacing.border_spacing = - CSS_BORDER_SPACING_LENGTH; - style->border_spacing.horz.unit = - style->border_spacing.vert.unit = - CSS_UNIT_PX; - style->border_spacing.horz.value = - style->border_spacing.vert.value = - value; - } - } - } - style->html_style.cellpadding.type = CSS_CELLPADDING_VALUE; - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "cellpadding"))) { - if (!strrchr(s, '%')) { /* % not implemented */ - int value = atoi(s); - if (0 <= value) { - style->html_style.cellpadding.value = value; - /* todo: match and rules and don't set if they are */ - for (i = 0; i < 4; i++) - style->padding[i].override_cellpadding = false; - } - } - } else { - style->html_style.cellpadding.value = 1; - } - } - - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "style")) != NULL) { - struct css_style *astyle; - astyle = css_duplicate_style(&css_empty_style); - if (!astyle) { - xmlFree(s); - css_free_style(style); - return 0; - } - css_parse_property_list(c, astyle, s); - css_cascade(style, astyle); - css_free_style(astyle); - xmlFree(s); - } - - return style; -} - - -/** - * Apply the CSS text-transform property to given text for its ASCII chars. - * - * \param s string to transform - * \param len length of s - * \param tt transform type - */ - -void box_text_transform(char *s, unsigned int len, - css_text_transform tt) -{ - unsigned int i; - if (len == 0) - return; - switch (tt) { - case CSS_TEXT_TRANSFORM_UPPERCASE: - for (i = 0; i < len; ++i) - if (s[i] < 0x80) - s[i] = toupper(s[i]); - break; - case CSS_TEXT_TRANSFORM_LOWERCASE: - for (i = 0; i < len; ++i) - if (s[i] < 0x80) - s[i] = tolower(s[i]); - break; - case CSS_TEXT_TRANSFORM_CAPITALIZE: - if (s[0] < 0x80) - s[0] = toupper(s[0]); - for (i = 1; i < len; ++i) - if (s[i] < 0x80 && isspace(s[i - 1])) - s[i] = toupper(s[i]); - break; - default: - break; - } -} - - -/* - * Special case elements - * - * These functions are called by convert_xml_to_box when an element is being - * converted, according to the entries in element_table (top of file). - * - * The parameters are the xmlNode, a status structure for the conversion, and - * the style found for the element. - * - * If a box is created, it is returned in the result structure. The - * convert_children field should be 1 if convert_xml_to_box should convert the - * node's children recursively, 0 if it should ignore them (presumably they - * have been processed in some way by the function). If box is 0, no box will - * be created for that element, and convert_children must be 0. - */ -struct box_result box_a(xmlNode *n, struct box_status *status, - struct css_style *style) -{ - struct box *box; - char *s, *s1; - char *id = status->id; - - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "href")) != NULL) - status->href = s; - - /* name and id share the same namespace */ - if ((s1 = (char *) xmlGetProp(n, (const xmlChar *) "name")) != NULL) { - if (status->id && strcmp(status->id, s1) == 0) { - /* both specified and they match => ok */ - id = status->id; - } - else if (!status->id) { - /* only name specified */ - id = squash_whitespace(s1); - if (!id) { - xmlFree(s1); - return (struct box_result) {0, false, true}; - } - } else - /* both specified but no match */ - id = 0; - - xmlFree(s1); - } - - box = box_create(style, status->href, status->title, id, - status->content->data.html.box_pool); - - if (id && id != status->id) - free(id); - - if (!box) - return (struct box_result) {0, false, true}; - - return (struct box_result) {box, true, false}; -} - -struct box_result box_body(xmlNode *n, struct box_status *status, - struct css_style *style) -{ - struct box *box; - status->content->data.html.background_colour = style->background_color; - box = box_create(style, status->href, status->title, status->id, - status->content->data.html.box_pool); - if (!box) - return (struct box_result) {0, false, true}; - return (struct box_result) {box, true, false}; -} - -struct box_result box_br(xmlNode *n, struct box_status *status, - struct css_style *style) -{ - struct box *box; - box = box_create(style, status->href, status->title, status->id, - status->content->data.html.box_pool); - if (!box) - return (struct box_result) {0, false, true}; - box->type = BOX_BR; - return (struct box_result) {box, false, false}; -} - -struct box_result box_image(xmlNode *n, struct box_status *status, - struct css_style *style) -{ - struct box *box; - char *s, *url, *s1, *map; - xmlChar *s2; - url_func_result res; - - box = box_create(style, status->href, status->title, status->id, - status->content->data.html.box_pool); - if (!box) - return (struct box_result) {0, false, true}; - - /* handle alt text */ - if ((s2 = xmlGetProp(n, (const xmlChar *) "alt")) != NULL) { - box->text = squash_whitespace(s2); - xmlFree(s2); - if (!box->text) - return (struct box_result) {0, false, true}; - box->length = strlen(box->text); - } - - /* imagemap associated with this image */ - if ((map = xmlGetProp(n, (const xmlChar *) "usemap")) != NULL) { - if (map[0] == '#') - box->usemap = strdup(map + 1); - else - box->usemap = strdup(map); - xmlFree(map); - if (!box->usemap) { - free(box->text); - return (struct box_result) {0, false, true}; - } - } - - /* img without src is an error */ - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "src")) == NULL) - return (struct box_result) {box, false, false}; - - /* remove leading and trailing whitespace */ - s1 = strip(s); - res = url_join(s1, status->content->data.html.base_url, &url); - xmlFree(s); - if (res == URL_FUNC_NOMEM) { - free(box->text); - return (struct box_result) {0, false, true}; - } else if (res == URL_FUNC_FAILED) { - return (struct box_result) {box, false, false}; - } - - if (strcmp(url, status->content->data.html.base_url) == 0) - /* if url is equivalent to the parent's url, - * we've got infinite inclusion: ignore */ - return (struct box_result) {box, false, false}; - - /* start fetch */ - if (!html_fetch_object(status->content, url, box, image_types, - status->content->available_width, 1000, false)) - return (struct box_result) {0, false, true}; - - return (struct box_result) {box, false, false}; -} - -struct box_result box_form(xmlNode *n, struct box_status *status, - struct css_style *style) -{ - char *action, *method, *enctype; - form_method fmethod; - struct box *box; - struct form *form; - - box = box_create(style, status->href, status->title, status->id, - status->content->data.html.box_pool); - if (!box) - return (struct box_result) {0, false, true}; - - if (!(action = (char *) xmlGetProp(n, (const xmlChar *) "action"))) { - /* the action attribute is required */ - return (struct box_result) {box, true, false}; - } - - fmethod = method_GET; - if ((method = (char *) xmlGetProp(n, (const xmlChar *) "method"))) { - if (strcasecmp(method, "post") == 0) { - fmethod = method_POST_URLENC; - if ((enctype = (char *) xmlGetProp(n, - (const xmlChar *) "enctype"))) { - if (strcasecmp(enctype, - "multipart/form-data") == 0) - fmethod = method_POST_MULTIPART; - xmlFree(enctype); - } - } - xmlFree(method); - } - - status->current_form = form = form_new(action, fmethod); - if (!form) { - xmlFree(action); - return (struct box_result) {0, false, true}; - } - - return (struct box_result) {box, true, false}; -} - -struct box_result box_textarea(xmlNode *n, struct box_status *status, - struct css_style *style) -{ - /* A textarea is an INLINE_BLOCK containing a single INLINE_CONTAINER, - * which contains the text as runs of INLINE separated by BR. There is - * at least one INLINE. The first and last boxes are INLINE. - * Consecutive BR may not be present. These constraints are satisfied - * by using a 0-length INLINE for blank lines. */ - - xmlChar *content, *current; - struct box *box, *inline_container, *inline_box, *br_box; - char *s; - size_t len; - - box = box_create(style, NULL, 0, status->id, - status->content->data.html.box_pool); - if (!box) - return (struct box_result) {0, false, true}; - box->type = BOX_INLINE_BLOCK; - box->gadget = form_new_control(GADGET_TEXTAREA); - if (!box->gadget) - return (struct box_result) {0, false, true}; - box->gadget->box = box; - - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "name")) != NULL) { - box->gadget->name = strdup(s); - xmlFree(s); - if (!box->gadget->name) - return (struct box_result) {0, false, true}; - } - - inline_container = box_create(0, 0, 0, 0, - status->content->data.html.box_pool); - if (!inline_container) - return (struct box_result) {0, false, true}; - inline_container->type = BOX_INLINE_CONTAINER; - box_add_child(box, inline_container); - - current = content = xmlNodeGetContent(n); - while (1) { - /* BOX_INLINE */ - len = strcspn(current, "\r\n"); - s = strndup(current, len); - if (!s) { - box_free(box); - xmlFree(content); - return (struct box_result) {NULL, false, false}; - } - - inline_box = box_create(style, 0, 0, 0, - status->content->data.html.box_pool); - if (!inline_box) - return (struct box_result) {0, false, true}; - inline_box->type = BOX_INLINE; - inline_box->style_clone = 1; - inline_box->text = s; - inline_box->length = len; - box_add_child(inline_container, inline_box); - - current += len; - if (current[0] == 0) - /* finished */ - break; - - /* BOX_BR */ - br_box = box_create(style, 0, 0, 0, - status->content->data.html.box_pool); - if (!br_box) - return (struct box_result) {0, false, true}; - br_box->type = BOX_BR; - br_box->style_clone = 1; - box_add_child(inline_container, br_box); - - if (current[0] == '\r' && current[1] == '\n') - current += 2; - else - current++; - } - xmlFree(content); - - if (status->current_form) - form_add_control(status->current_form, box->gadget); - - return (struct box_result) {box, false, false}; -} - -struct box_result box_select(xmlNode *n, struct box_status *status, - struct css_style *style) -{ - struct box *box; - struct box *inline_container; - struct box *inline_box; - struct form_control *gadget; - char* s; - xmlNode *c, *c2; - - gadget = form_new_control(GADGET_SELECT); - if (!gadget) - return (struct box_result) {0, false, true}; - - gadget->data.select.multiple = false; - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "multiple")) != NULL) { - gadget->data.select.multiple = true; - xmlFree(s); - } - - gadget->data.select.items = NULL; - gadget->data.select.last_item = NULL; - gadget->data.select.num_items = 0; - gadget->data.select.num_selected = 0; - - for (c = n->children; c; c = c->next) { - if (strcmp((const char *) c->name, "option") == 0) { - if (!box_select_add_option(gadget, c)) - goto no_memory; - } else if (strcmp((const char *) c->name, "optgroup") == 0) { - for (c2 = c->children; c2; c2 = c2->next) { - if (strcmp((const char *) c2->name, - "option") == 0) { - if (!box_select_add_option(gadget, c2)) - goto no_memory; - } - } - } - } - - if (gadget->data.select.num_items == 0) { - /* no options: ignore entire select */ - form_free_control(gadget); - return (struct box_result) {0, false, false}; - } - - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "name")) != NULL) { - gadget->name = strdup(s); - xmlFree(s); - if (!gadget->name) - goto no_memory; - } - - box = box_create(style, NULL, 0, status->id, - status->content->data.html.box_pool); - if (!box) - goto no_memory; - box->type = BOX_INLINE_BLOCK; - box->gadget = gadget; - gadget->box = box; - - inline_container = box_create(0, 0, 0, 0, - status->content->data.html.box_pool); - if (!inline_container) - goto no_memory; - inline_container->type = BOX_INLINE_CONTAINER; - inline_box = box_create(style, 0, 0, 0, - status->content->data.html.box_pool); - if (!inline_box) - goto no_memory; - inline_box->type = BOX_INLINE; - inline_box->style_clone = 1; - box_add_child(inline_container, inline_box); - box_add_child(box, inline_container); - - if (!gadget->data.select.multiple && - gadget->data.select.num_selected == 0) { - gadget->data.select.current = gadget->data.select.items; - gadget->data.select.current->initial_selected = - gadget->data.select.current->selected = true; - gadget->data.select.num_selected = 1; - } - - if (gadget->data.select.num_selected == 0) - inline_box->text = strdup(messages_get("Form_None")); - else if (gadget->data.select.num_selected == 1) - inline_box->text = strdup(gadget->data.select.current->text); - else - inline_box->text = strdup(messages_get("Form_Many")); - if (!inline_box->text) - goto no_memory; - - inline_box->length = strlen(inline_box->text); - - if (status->current_form) - form_add_control(status->current_form, gadget); - - return (struct box_result) {box, false, false}; - -no_memory: - form_free_control(gadget); - return (struct box_result) {0, false, true}; -} - - -/** - * Add an option to a form select control. - * - * \param control select containing the option - * \param n xml element node for