From 8b78a7803d393c9a1fb8b76deab79cdb5ad480d8 Mon Sep 17 00:00:00 2001 From: James Bursa Date: Fri, 21 May 2004 14:26:59 +0000 Subject: [project @ 2004-05-21 14:26:59 by bursa] Improved handling of objects and frames; some work on malloc() failure in box conversion. svn path=/import/netsurf/; revision=880 --- content/content.h | 4 +- render/box.c | 517 +++++++++++++++++++++++++++++++++++++----------------- render/box.h | 3 +- render/html.c | 94 +++++----- render/html.h | 3 +- render/layout.c | 414 +++++++++++++++++++++++++++++++------------ 6 files changed, 701 insertions(+), 334 deletions(-) diff --git a/content/content.h b/content/content.h index d3555f9ad..57094ceb8 100644 --- a/content/content.h +++ b/content/content.h @@ -116,8 +116,8 @@ struct content { CONTENT_STATUS_DONE /**< All finished. */ } status; /**< Current status. */ - unsigned long width, height; /**< Dimensions, if applicable. */ - unsigned long available_width; /**< Available width (eg window width). */ + int width, height; /**< Dimensions, if applicable. */ + int available_width; /**< Available width (eg window width). */ /** Data dependent on type. */ union { diff --git a/render/box.c b/render/box.c index d1ef50d48..c6a1bd9e4 100644 --- a/render/box.c +++ b/render/box.c @@ -36,50 +36,61 @@ #include "netsurf/utils/utils.h" -/* status for box tree construction */ -struct status { +/** Status of box tree construction. */ +struct box_status { struct content *content; char *href; char *title; struct form* current_form; }; -/* result of converting a special case element */ -struct result { - struct box *box; /* box for element, if any, 0 otherwise */ - int convert_children; /* children should be converted */ +/** 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 struct box * convert_xml_to_box(xmlNode * n, struct content *content, struct css_style * parent_style, struct box * parent, struct box *inline_container, - struct status status); + struct box_status status); static struct css_style * box_get_style(struct content ** stylesheet, unsigned int stylesheet_count, struct css_style * parent_style, xmlNode * n); static void box_text_transform(char *s, unsigned int len, css_text_transform tt); -static struct result box_a(xmlNode *n, struct status *status, +static struct box_result box_a(xmlNode *n, struct box_status *status, struct css_style *style); -static struct result box_body(xmlNode *n, struct status *status, +static struct box_result box_body(xmlNode *n, struct box_status *status, struct css_style *style); -static struct result box_br(xmlNode *n, struct status *status, +static struct box_result box_br(xmlNode *n, struct box_status *status, struct css_style *style); -static struct result box_image(xmlNode *n, struct status *status, +static struct box_result box_image(xmlNode *n, struct box_status *status, struct css_style *style); -static struct result box_form(xmlNode *n, struct status *status, +static struct box_result box_form(xmlNode *n, struct box_status *status, struct css_style *style); -static struct result box_textarea(xmlNode *n, struct status *status, +static struct box_result box_textarea(xmlNode *n, struct box_status *status, struct css_style *style); -static struct result box_select(xmlNode *n, struct status *status, +static struct box_result box_select(xmlNode *n, struct box_status *status, struct css_style *style); -static struct result box_input(xmlNode *n, struct status *status, +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 status *status, +static struct box *box_input_text(xmlNode *n, struct box_status *status, struct css_style *style, bool password); -static struct result box_button(xmlNode *n, struct status *status, +static struct box_result box_button(xmlNode *n, struct box_status *status, struct css_style *style); -static struct result box_frameset(xmlNode *n, struct status *status, +static struct box_result box_frameset(xmlNode *n, struct box_status *status, struct css_style *style); static void add_option(xmlNode* n, struct form_control* current_select, char *text); static void box_normalise_block(struct box *block, pool box_pool); @@ -91,23 +102,24 @@ void box_normalise_table_row(struct box *row, unsigned int **row_span, unsigned int *table_columns, pool box_pool); static void box_normalise_inline_container(struct box *cont, pool box_pool); -static void gadget_free(struct form_control* g); static void box_free_box(struct box *box); -static struct result box_object(xmlNode *n, struct status *status, +static struct box_result box_object(xmlNode *n, struct box_status *status, struct css_style *style); -static struct result box_embed(xmlNode *n, struct status *status, +static struct box_result box_embed(xmlNode *n, struct box_status *status, struct css_style *style); -static struct result box_applet(xmlNode *n, struct status *status, +static struct box_result box_applet(xmlNode *n, struct box_status *status, struct css_style *style); -static struct result box_iframe(xmlNode *n, struct status *status, +static struct box_result box_iframe(xmlNode *n, struct box_status *status, struct css_style *style); static bool plugin_decode(struct content* content, char* url, struct box* box, struct object_params* po); +static struct box_multi_length *box_parse_multi_lengths(const char *s, + unsigned int *count); /* element_table must be sorted by name */ struct element_entry { char name[10]; /* element type */ - struct result (*convert)(xmlNode *n, struct status *status, + struct box_result (*convert)(xmlNode *n, struct box_status *status, struct css_style *style); }; static const struct element_entry element_table[] = { @@ -217,7 +229,7 @@ void box_insert_sibling(struct box *box, struct box *new_box) void xml_to_box(xmlNode *n, struct content *c) { - struct status status = {c, 0, 0, 0}; + struct box_status status = {c, 0, 0, 0}; LOG(("node %p", n)); assert(c->type == CONTENT_HTML); @@ -280,7 +292,7 @@ static box_type box_map[] = { struct box * convert_xml_to_box(xmlNode * n, struct content *content, struct css_style * parent_style, struct box * parent, struct box *inline_container, - struct status status) + struct box_status status) { struct box * box = 0; struct box * inline_container_c; @@ -289,7 +301,7 @@ struct box * convert_xml_to_box(xmlNode * n, struct content *content, char * s; xmlChar * title0; char * title = 0; - int convert_children = 1; + bool convert_children = true; char *href_in = status.href; assert(n != 0 && parent_style != 0 && parent != 0); @@ -325,9 +337,12 @@ struct box * convert_xml_to_box(xmlNode * n, struct content *content, (int (*)(const void *, const void *)) strcmp); if (element != 0) { /* a special convert function exists for this element */ - struct result res = element->convert(n, &status, style); + struct box_result res = element->convert(n, &status, style); box = res.box; convert_children = res.convert_children; + if (res.memory_error) { + /** \todo handle memory exhaustion */ + } if (box == 0) { /* no box for this element */ assert(convert_children == 0); @@ -367,7 +382,7 @@ struct box * convert_xml_to_box(xmlNode * n, struct content *content, assert(inline_container->last != 0); inline_container->last->space = 1; } - xfree(text); + free(text); goto end; } @@ -448,7 +463,7 @@ struct box * convert_xml_to_box(xmlNode * n, struct content *content, inline_container = 0; } } while (*current); - xfree(text); + free(text); goto end; } else if (box->type == BOX_INLINE || @@ -733,7 +748,7 @@ void box_text_transform(char *s, unsigned int len, * be created for that element, and convert_children must be 0. */ -struct result box_a(xmlNode *n, struct status *status, +struct box_result box_a(xmlNode *n, struct box_status *status, struct css_style *style) { struct box *box; @@ -742,27 +757,27 @@ struct result box_a(xmlNode *n, struct status *status, status->href = s; box = box_create(style, status->href, status->title, status->content->data.html.box_pool); - return (struct result) {box, 1}; + return (struct box_result) {box, true, false}; } -struct result box_body(xmlNode *n, struct status *status, +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->content->data.html.box_pool); - return (struct result) {box, 1}; + return (struct box_result) {box, true, false}; } -struct result box_br(xmlNode *n, struct status *status, +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->content->data.html.box_pool); box->type = BOX_BR; - return (struct result) {box, 0}; + return (struct box_result) {box, false, false}; } static const content_type image_types[] = { @@ -772,7 +787,7 @@ static const content_type image_types[] = { #endif CONTENT_UNKNOWN }; -struct result box_image(xmlNode *n, struct status *status, +struct box_result box_image(xmlNode *n, struct box_status *status, struct css_style *style) { struct box *box; @@ -792,7 +807,7 @@ struct result box_image(xmlNode *n, struct status *status, /* img without src is an error */ if (!(s = (char *) xmlGetProp(n, (const xmlChar *) "src"))) - return (struct result) {box, 0}; + return (struct box_result) {box, false, false}; /* imagemap associated with this image */ if ((map = xmlGetProp(n, (const xmlChar *) "usemap"))) { @@ -811,19 +826,20 @@ struct result box_image(xmlNode *n, struct status *status, url = url_join(s1, status->content->data.html.base_url); if (!url) { xmlFree(s); - return (struct result) {box, 0}; + return (struct box_result) {box, false, false}; } LOG(("image '%s'", url)); xmlFree(s); /* start fetch */ - html_fetch_object(status->content, url, box, image_types); + html_fetch_object(status->content, url, box, image_types, + status->content->available_width, 1000); - return (struct result) {box, 0}; + return (struct box_result) {box, false, false}; } -struct result box_form(xmlNode *n, struct status *status, +struct box_result box_form(xmlNode *n, struct box_status *status, struct css_style *style) { char *s, *s2; @@ -836,7 +852,7 @@ struct result box_form(xmlNode *n, struct status *status, s = (char *) xmlGetProp(n, (const xmlChar *) "action"); if (!s) { /* the action attribute is required */ - return (struct result) {box, 1}; + return (struct box_result) {box, true, false}; } status->current_form = form = xcalloc(1, sizeof(*form)); @@ -857,10 +873,10 @@ struct result box_form(xmlNode *n, struct status *status, form->controls = form->last_control = 0; - return (struct result) {box, 1}; + return (struct box_result) {box, true, false}; } -struct result box_textarea(xmlNode *n, struct status *status, +struct box_result box_textarea(xmlNode *n, struct box_status *status, struct css_style *style) { xmlChar *content, *current; @@ -870,14 +886,26 @@ struct result box_textarea(xmlNode *n, struct status *status, box = box_create(style, NULL, 0, status->content->data.html.box_pool); box->type = BOX_INLINE_BLOCK; - box->gadget = xcalloc(1, sizeof(struct form_control)); + box->gadget = form_new_control(GADGET_TEXTAREA); + if (!box->gadget) { + free(box); + return (struct box_result) {0, false, true}; + } box->gadget->box = box; - box->gadget->type = GADGET_TEXTAREA; if (status->current_form) form_add_control(status->current_form, box->gadget); else box->gadget->form = 0; + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "name"))) { + box->gadget->name = strdup(s); + xmlFree(s); + if (!box->gadget->name) { + box_free(box); + return (struct box_result) {0, false, true}; + } + } + /* split the content at newlines and make an inline container with an * inline box for each line */ current = content = xmlNodeGetContent(n); @@ -906,25 +934,23 @@ struct result box_textarea(xmlNode *n, struct status *status, } while (*current); xmlFree(content); - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "name"))) - { - box->gadget->name = s; - } - - return (struct result) {box, 0}; + return (struct box_result) {box, false, false}; } -struct result box_select(xmlNode *n, struct status *status, +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 = xcalloc(1, sizeof(struct form_control)); + struct form_control *gadget; char* s; xmlNode *c, *c2; - gadget->type = GADGET_SELECT; + gadget = form_new_control(GADGET_SELECT); + if (gadget) + return (struct box_result) {0, false, true}; + if (status->current_form) form_add_control(status->current_form, gadget); else @@ -961,12 +987,17 @@ struct result box_select(xmlNode *n, struct status *status, if (gadget->data.select.num_items == 0) { /* no options: ignore entire select */ - xfree(gadget); - return (struct result) {0, 0}; + form_free_control(gadget); + return (struct box_result) {0, false, false}; } if ((s = (char *) xmlGetProp(n, (const xmlChar *) "name"))) { - gadget->name = s; + gadget->name = strdup(s); + xmlFree(s); + if (!gadget->name) { + form_free_control(gadget); + return (struct box_result) {0, false, true}; + } } box = box_create(style, NULL, 0, status->content->data.html.box_pool); @@ -1002,7 +1033,7 @@ struct result box_select(xmlNode *n, struct status *status, inline_box->length = strlen(inline_box->text); inline_box->font = font_open(status->content->data.html.fonts, style); - return (struct result) {box, 0}; + return (struct box_result) {box, false, false}; } void add_option(xmlNode* n, struct form_control* current_select, char *text) @@ -1042,7 +1073,7 @@ void add_option(xmlNode* n, struct form_control* current_select, char *text) } } -struct result box_input(xmlNode *n, struct status *status, +struct box_result box_input(xmlNode *n, struct box_status *status, struct css_style *style) { struct box* box = 0; @@ -1060,29 +1091,46 @@ struct result box_input(xmlNode *n, struct status *status, box = box_create(style, NULL, 0, status->content->data.html.box_pool); box->type = BOX_INLINE_BLOCK; - box->gadget = gadget = xcalloc(1, sizeof(struct form_control)); + box->gadget = gadget = form_new_control(GADGET_FILE); + if (!gadget) { + box_free_box(box); + xmlFree(type); + return (struct box_result) {0, false, true}; + } gadget->box = box; - gadget->type = GADGET_FILE; box->font = font_open(status->content->data.html.fonts, style); } else if (type && strcasecmp(type, "hidden") == 0) { /* no box for hidden inputs */ - gadget = xcalloc(1, sizeof(struct form_control)); - gadget->type = GADGET_HIDDEN; + gadget = form_new_control(GADGET_HIDDEN); + if (!gadget) { + xmlFree(type); + return (struct box_result) {0, false, true}; + } - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "value"))) - gadget->value = s; + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "value"))) { + gadget->value = strdup(s); + xmlFree(s); + if (!gadget->value) { + form_free_control(gadget); + xmlFree(type); + return (struct box_result) {0, false, true}; + } + } } else if (type && (strcasecmp(type, "checkbox") == 0 || strcasecmp(type, "radio") == 0)) { box = box_create(style, NULL, 0, status->content->data.html.box_pool); - box->gadget = gadget = xcalloc(1, sizeof(struct form_control)); + box->gadget = gadget = form_new_control(GADGET_RADIO); + if (!gadget) { + box_free_box(box); + xmlFree(type); + return (struct box_result) {0, false, true}; + } gadget->box = box; if (type[0] == 'c' || type[0] == 'C') gadget->type = GADGET_CHECKBOX; - else - gadget->type = GADGET_RADIO; if ((s = (char *) xmlGetProp(n, (const xmlChar *) "checked"))) { if (gadget->type == GADGET_CHECKBOX) @@ -1092,12 +1140,19 @@ struct result box_input(xmlNode *n, struct status *status, xmlFree(s); } - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "value"))) - gadget->value = s; + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "value"))) { + gadget->value = strdup(s); + xmlFree(s); + if (!gadget->value) { + box_free_box(box); + xmlFree(type); + return (struct box_result) {0, false, true}; + } + } } else if (type && (strcasecmp(type, "submit") == 0 || strcasecmp(type, "reset") == 0)) { - struct result result = box_button(n, status, style); + struct box_result result = box_button(n, status, style); struct box *inline_container, *inline_box; box = result.box; inline_container = box_create(0, 0, 0, @@ -1119,7 +1174,7 @@ struct result box_input(xmlNode *n, struct status *status, box_add_child(box, inline_container); } else if (type && strcasecmp(type, "button") == 0) { - struct result result = box_button(n, status, style); + struct box_result result = box_button(n, status, style); struct box *inline_container, *inline_box; box = result.box; inline_container = box_create(0, 0, 0, @@ -1143,14 +1198,21 @@ struct result box_input(xmlNode *n, struct status *status, } else if (type && strcasecmp(type, "image") == 0) { box = box_create(style, NULL, 0, status->content->data.html.box_pool); - box->gadget = gadget = xcalloc(1, sizeof(struct form_control)); + box->gadget = gadget = form_new_control(GADGET_IMAGE); + if (!gadget) { + box_free_box(box); + xmlFree(type); + return (struct box_result) {0, false, true}; + } gadget->box = box; gadget->type = GADGET_IMAGE; if ((s = (char *) xmlGetProp(n, (const xmlChar*) "src"))) { url = url_join(s, status->content->data.html.base_url); if (url) html_fetch_object(status->content, url, box, - image_types); + image_types, + status->content->available_width, + 1000); xmlFree(s); } @@ -1168,14 +1230,22 @@ struct result box_input(xmlNode *n, struct status *status, if (status->current_form) form_add_control(status->current_form, gadget); else - gadget->form = 0; - gadget->name = (char *) xmlGetProp(n, (const xmlChar *) "name"); + gadget->form = 0; + s = (char *) xmlGetProp(n, (const xmlChar *) "name"); + if (s) { + gadget->name = strdup(s); + xmlFree(s); + if (!gadget->name) { + box_free_box(box); + return (struct box_result) {0, false, true}; + } + } } - return (struct result) {box, 0}; + return (struct box_result) {box, false, false}; } -struct box *box_input_text(xmlNode *n, struct status *status, +struct box *box_input_text(xmlNode *n, struct box_status *status, struct css_style *style, bool password) { char *s; @@ -1185,7 +1255,7 @@ struct box *box_input_text(xmlNode *n, struct status *status, struct box *inline_container, *inline_box; box->type = BOX_INLINE_BLOCK; - box->gadget = xcalloc(1, sizeof(struct form_control)); + box->gadget = form_new_control(GADGET_TEXTBOX); box->gadget->box = box; box->gadget->maxlength = 100; @@ -1228,38 +1298,56 @@ struct box *box_input_text(xmlNode *n, struct status *status, return box; } -struct result box_button(xmlNode *n, struct status *status, +struct box_result box_button(xmlNode *n, struct box_status *status, struct css_style *style) { + xmlChar *s; char *type = (char *) xmlGetProp(n, (const xmlChar *) "type"); struct box *box = box_create(style, 0, 0, status->content->data.html.box_pool); box->type = BOX_INLINE_BLOCK; if (!type || strcasecmp(type, "submit") == 0) { - box->gadget = xcalloc(1, sizeof(struct form_control)); - box->gadget->type = GADGET_SUBMIT; + box->gadget = form_new_control(GADGET_SUBMIT); } else if (strcasecmp(type, "reset") == 0) { - box->gadget = xcalloc(1, sizeof(struct form_control)); - box->gadget->type = GADGET_RESET; + box->gadget = form_new_control(GADGET_RESET); } else { /* type="button" or unknown: just render the contents */ xmlFree(type); - return (struct result) {box, 1}; + return (struct box_result) {box, true, false}; } if (type) xmlFree(type); + if (!box->gadget) { + box_free_box(box); + return (struct box_result) {0, false, true}; + } + if (status->current_form) form_add_control(status->current_form, box->gadget); else box->gadget->form = 0; box->gadget->box = box; - box->gadget->name = (char *) xmlGetProp(n, (const xmlChar *) "name"); - box->gadget->value = (char *) xmlGetProp(n, (const xmlChar *) "value"); + if ((s = xmlGetProp(n, (const xmlChar *) "name"))) { + box->gadget->name = strdup((char *) s); + xmlFree(s); + if (!box->gadget->name) { + box_free_box(box); + return (struct box_result) {0, false, true}; + } + } + if ((s = xmlGetProp(n, (const xmlChar *) "value"))) { + box->gadget->value = strdup((char *) s); + xmlFree(s); + if (!box->gadget->value) { + box_free_box(box); + return (struct box_result) {0, false, true}; + } + } - return (struct result) {box, 1}; + return (struct box_result) {box, true, false}; } @@ -1476,7 +1564,7 @@ void box_normalise_table(struct box *table, pool box_pool) } table->columns = table_columns; - xfree(row_span); + free(row_span); if (table->children == 0) { LOG(("table->children == 0, removing")); @@ -1744,32 +1832,8 @@ void box_normalise_inline_container(struct box *cont, pool box_pool) } -void gadget_free(struct form_control* g) -{ - struct form_option *o, *o1; - - if (g->name != 0) - xmlFree(g->name); - free(g->value); - free(g->initial_value); - - if (g->type == GADGET_SELECT) { - o = g->data.select.items; - while (o != NULL) - { - if (o->text != 0) - xmlFree(o->text); - if (o->value != 0) - xmlFree(o->value); - o1 = o->next; - xfree(o); - o = o1; - } - } -} - /** - * free a box tree recursively + * Free a box tree recursively. */ void box_free(struct box *box) @@ -1786,13 +1850,16 @@ void box_free(struct box *box) box_free_box(box); } + +/** + * Free a single box structure. + */ + void box_free_box(struct box *box) { if (!box->clone) { - if (box->gadget) { - gadget_free(box->gadget); - free(box->gadget); - } + if (box->gadget) + form_free_control(box->gadget); free(box->href); free(box->title); free(box->col); @@ -1800,8 +1867,7 @@ void box_free_box(struct box *box) free(box->style); } - if (box->usemap) - free(box->usemap); + free(box->usemap); free(box->text); /* TODO: free object_params */ } @@ -1810,7 +1876,7 @@ void box_free_box(struct box *box) /** * add an object to the box tree */ -struct result box_object(xmlNode *n, struct status *status, +struct box_result box_object(xmlNode *n, struct box_status *status, struct css_style *style) { struct box *box; @@ -1838,7 +1904,7 @@ struct result box_object(xmlNode *n, struct status *status, if (!url) { free(po); xmlFree(s); - return (struct result) {box, 1}; + return (struct box_result) {box, true, true}; } po->data = strdup(s); LOG(("object '%s'", po->data)); @@ -1941,16 +2007,16 @@ struct result box_object(xmlNode *n, struct status *status, /* start fetch */ if (plugin_decode(status->content, url, box, po)) - return (struct result) {box, 0}; + return (struct box_result) {box, false, false}; - return (struct result) {box, 1}; + return (struct box_result) {box, true, false}; } /** * add an embed to the box tree */ -struct result box_embed(xmlNode *n, struct status *status, +struct box_result box_embed(xmlNode *n, struct box_status *status, struct css_style *style) { struct box *box; @@ -1978,7 +2044,7 @@ struct result box_embed(xmlNode *n, struct status *status, if (!url) { free(po); xmlFree(s); - return (struct result) {box, 0}; + return (struct box_result) {box, false, true}; } LOG(("embed '%s'", url)); po->data = strdup(s); @@ -2014,14 +2080,14 @@ struct result box_embed(xmlNode *n, struct status *status, /* start fetch */ plugin_decode(status->content, url, box, po); - return (struct result) {box,0}; + return (struct box_result) {box, false, false}; } /** * add an applet to the box tree */ -struct result box_applet(xmlNode *n, struct status *status, +struct box_result box_applet(xmlNode *n, struct box_status *status, struct css_style *style) { struct box *box; @@ -2049,7 +2115,7 @@ struct result box_applet(xmlNode *n, struct status *status, if (!url) { free(po); xmlFree(s); - return (struct result) {box, 1}; + return (struct box_result) {box, true, false}; } LOG(("applet '%s'", url)); po->classid = strdup(s); @@ -2117,9 +2183,9 @@ struct result box_applet(xmlNode *n, struct status *status, /* start fetch */ if(plugin_decode(status->content, url, box, po)) - return (struct result) {box,0}; + return (struct box_result) {box, false, false}; - return (struct result) {box,1}; + return (struct box_result) {box, true, false}; } /** @@ -2127,7 +2193,7 @@ struct result box_applet(xmlNode *n, struct status *status, * add an iframe to the box tree * TODO - implement GUI nested wimp stuff 'cos this looks naff atm. (16_5) */ -struct result box_iframe(xmlNode *n, struct status *status, +struct box_result box_iframe(xmlNode *n, struct box_status *status, struct css_style *style) { struct box *box; @@ -2153,7 +2219,7 @@ struct result box_iframe(xmlNode *n, struct status *status, if (!url) { free(po); xmlFree(s); - return (struct result) {box, 0}; + return (struct box_result) {box, false, true}; } LOG(("embed '%s'", url)); po->data = strdup(s); @@ -2165,7 +2231,7 @@ struct result box_iframe(xmlNode *n, struct status *status, /* start fetch */ plugin_decode(status->content, url, box, po); - return (struct result) {box,0}; + return (struct box_result) {box, false, false}; } /** @@ -2269,7 +2335,7 @@ bool plugin_decode(struct content* content, char* url, struct box* box, * when we fetch it (if the type was not specified or is different to that * given in the attributes). */ - html_fetch_object(content, url, box, 0); + html_fetch_object(content, url, box, 0, 1000, 1000); return true; } @@ -2301,49 +2367,105 @@ void box_coords(struct box *box, int *x, int *y) } -struct result box_frameset(xmlNode *n, struct status *status, +struct box_result box_frameset(xmlNode *n, struct box_status *status, struct css_style *style) { - unsigned int i; unsigned int row, col; unsigned int rows = 1, cols = 1; + int object_width, object_height; char *s, *s1, *url; struct box *box; struct box *row_box; struct box *cell_box; + struct box *object_box; struct css_style *row_style; - struct css_style *cell_style; - struct result r; + struct box_result r; + struct box_multi_length *row_height = 0, *col_width = 0; xmlNode *c; box = box_create(style, 0, status->title, status->content->data.html.box_pool); box->type = BOX_TABLE; - /* count rows and columns */ - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "rows"))) { - for (i = 0; s[i]; i++) - if (s[i] == ',') - rows++; - free(s); - } + /* parse rows and columns */ + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "rows"))) { + row_height = box_parse_multi_lengths(s, &rows); + xmlFree(s); + if (!row_height) { + box_free_box(box); + return (struct box_result) {0, false, true}; + } + } - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "cols"))) { - for (i = 0; s[i]; i++) - if (s[i] == ',') - cols++; - free(s); - } + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "cols"))) { + col_width = box_parse_multi_lengths(s, &cols); + xmlFree(s); + if (!col_width) { + free(row_height); + box_free_box(box); + return (struct box_result) {0, false, true}; + } + } LOG(("rows %u, cols %u", rows, cols)); + box->min_width = 1; + box->max_width = 10000; + box->col = malloc(sizeof box->col[0] * cols); + if (!box->col) { + free(row_height); + free(col_width); + box_free_box(box); + return (struct box_result) {0, false, true}; + } + + if (col_width) { + for (col = 0; col != cols; col++) { + if (col_width[col].type == LENGTH_PX) { + box->col[col].type = COLUMN_WIDTH_FIXED; + box->col[col].width = col_width[col].value; + } else if (col_width[col].type == LENGTH_PERCENT) { + box->col[col].type = COLUMN_WIDTH_PERCENT; + box->col[col].width = col_width[col].value; + } else { + box->col[col].type = COLUMN_WIDTH_RELATIVE; + box->col[col].width = col_width[col].value; + } + box->col[col].min = 1; + box->col[col].max = 10000; + } + } else { + box->col[0].type = COLUMN_WIDTH_RELATIVE; + box->col[0].width = 1; + box->col[0].min = 1; + box->col[0].max = 10000; + } + /* create the frameset table */ c = n->children; for (row = 0; c && row != rows; row++) { row_style = malloc(sizeof (struct css_style)); - if (!row_style) - return (struct result) {box, 0}; + if (!row_style) { + free(row_height); + free(col_width); + return (struct box_result) {box, false, true}; + } memcpy(row_style, style, sizeof (struct css_style)); + object_height = 1000; /** \todo get available height */ + /* if (row_height) { + row_style->height.height = CSS_HEIGHT_LENGTH; + row_style->height.length.unit = CSS_UNIT_PX; + if (row_height[row].type == LENGTH_PERCENT) + row_style->height.length.value = 1000 * + row_height[row].value / 100; + else if (row_height[row].type == LENGTH_RELATIVE) + row_style->height.length.value = 100 * + row_height[row].value; + else + row_style->height.length.value = + row_height[row].value; + object_height = row_style->height.length.value; + }*/ row_box = box_create(row_style, 0, 0, status->content->data.html.box_pool); row_box->type = BOX_TABLE_ROW; @@ -2358,24 +2480,40 @@ struct result box_frameset(xmlNode *n, struct status *status, if (!c) break; - cell_style = malloc(sizeof (struct css_style)); - if (!cell_style) - return (struct result) {box, 0}; - memcpy(cell_style, style, sizeof (struct css_style)); - cell_box = box_create(cell_style, 0, 0, - status->content->data.html.box_pool); + /* estimate frame width */ + object_width = status->content->available_width; + if (col_width && col_width[col].type == LENGTH_PX) + object_width = col_width[col].value; + + cell_box = box_create(style, 0, 0, + status->content->data.html.box_pool); cell_box->type = BOX_TABLE_CELL; + cell_box->style_clone = 1; box_add_child(row_box, cell_box); if (strcmp((const char *) c->name, "frameset") == 0) { LOG(("frameset")); r = box_frameset(c, status, style); + if (r.memory_error) { + box_free(box); + free(row_height); + free(col_width); + return (struct box_result) {0, false, + true}; + } + r.box->style_clone = 1; box_add_child(cell_box, r.box); c = c->next; continue; } + object_box = box_create(style, 0, 0, + status->content->data.html.box_pool); + object_box->type = BOX_BLOCK; + object_box->style_clone = 1; + box_add_child(cell_box, object_box); + if (!(s = (char *) xmlGetProp(c, (const xmlChar *) "src"))) { c = c->next; @@ -2392,12 +2530,63 @@ struct result box_frameset(xmlNode *n, struct status *status, LOG(("frame, url '%s'", url)); - html_fetch_object(status->content, url, cell_box, 0); + html_fetch_object(status->content, url, object_box, 0, + object_width, object_height); xmlFree(s); c = c->next; } } - return (struct result) {box, 0}; + free(row_height); + free(col_width); + + style->width.width = CSS_WIDTH_PERCENT; + style->width.value.percent = 100; + + return (struct box_result) {box, false, false}; +} + + +/** + * Parse a multi-length-list, as defined by HTML 4.01. + * + * \param s string to parse + * \param count updated to number of entries + * \return array of struct box_multi_length, or 0 on memory exhaustion + */ + +struct box_multi_length *box_parse_multi_lengths(const char *s, + unsigned int *count) +{ + char *end; + unsigned int i, n = 1; + struct box_multi_length *length; + + for (i = 0; s[i]; i++) + if (s[i] == ',') + n++; + + length = malloc(sizeof *length * n); + if (!length) + return 0; + + for (i = 0; i != n; i++) { + length[i].value = strtof(s, &end); + if (length[i].value <= 0) + length[i].value = 1; + s = end; + switch (*s) { + case '%': length[i].type = LENGTH_PERCENT; break; + case '*': length[i].type = LENGTH_RELATIVE; break; + default: length[i].type = LENGTH_PX; break; + } + while (*s && *s != ',') + s++; + if (*s == ',') + s++; + } + + *count = n; + return length; } diff --git a/render/box.h b/render/box.h index 0b1831c6f..6e94247c4 100644 --- a/render/box.h +++ b/render/box.h @@ -92,7 +92,8 @@ typedef enum { struct column { enum { COLUMN_WIDTH_UNKNOWN = 0, COLUMN_WIDTH_FIXED, - COLUMN_WIDTH_AUTO, COLUMN_WIDTH_PERCENT } type; + COLUMN_WIDTH_AUTO, COLUMN_WIDTH_PERCENT, + COLUMN_WIDTH_RELATIVE } type; int min, max, width; }; diff --git a/render/html.c b/render/html.c index e4681f762..4503e4d30 100644 --- a/render/html.c +++ b/render/html.c @@ -38,6 +38,7 @@ static void html_head(struct content *c, xmlNode *head); static void html_find_stylesheets(struct content *c, xmlNode *head); static void html_object_callback(content_msg msg, struct content *object, void *p1, void *p2, union content_msg_data data); +static void html_object_done(struct box *box, struct content *object); static bool html_object_type_permitted(const content_type type, const content_type *permitted_types); @@ -524,7 +525,8 @@ void html_convert_css_callback(content_msg msg, struct content *css, */ void html_fetch_object(struct content *c, char *url, struct box *box, - const content_type *permitted_types) + const content_type *permitted_types, + int available_width, int available_height) { unsigned int i = c->data.html.object_count; union content_msg_data data; @@ -539,7 +541,7 @@ void html_fetch_object(struct content *c, char *url, struct box *box, /* start fetch */ c->data.html.object[i].content = fetchcache(url, c->url, html_object_callback, - c, (void*)i, c->width, c->height, + c, (void*)i, available_width, available_height, true #ifdef WITH_POST , 0, 0 @@ -547,9 +549,8 @@ void html_fetch_object(struct content *c, char *url, struct box *box, #ifdef WITH_COOKIES , false #endif - ); /* we don't know the object's - dimensions yet; use - parent's as an estimate */ + ); + if (c->data.html.object[i].content) { c->active++; if (c->data.html.object[i].content->status == CONTENT_STATUS_DONE) @@ -571,7 +572,6 @@ void html_object_callback(content_msg msg, struct content *object, unsigned int i = (unsigned int) p2; int x, y; struct box *box = c->data.html.object[i].box; - struct box *b; switch (msg) { case CONTENT_MSG_LOADING: @@ -590,53 +590,14 @@ void html_object_callback(content_msg msg, struct content *object, break; case CONTENT_MSG_READY: + if (object->type == CONTENT_HTML) { + html_object_done(box, object); + content_reformat(c, c->available_width, 0); + } break; case CONTENT_MSG_DONE: - LOG(("got object '%s'", object->url)); - box->object = object; - /* retain aspect ratio of box content */ - if ((box->style->width.width == CSS_WIDTH_LENGTH /*|| - box->style->width.width == CSS_WIDTH_PERCENT*/) && - box->style->height.height == CSS_HEIGHT_AUTO) { - box->style->height.height = CSS_HEIGHT_LENGTH; - box->style->height.length.unit = CSS_UNIT_PX; - if (box->style->width.width == CSS_WIDTH_LENGTH) { - box->style->height.length.value = object->height * (box->style->width.value.length.value / object->width); - } - /*else { - box->style->height.length.value = object->height * (box->style->width.value.percent / 100); - }*/ - box->height = box->style->height.length.value; - } - if (box->style->height.height == CSS_HEIGHT_LENGTH && - box->style->width.width == CSS_WIDTH_AUTO) { - box->style->width.width = CSS_WIDTH_LENGTH; - box->style->width.value.length.unit = CSS_UNIT_PX; - box->style->width.value.length.value = object->width * (box->style->height.length.value / object->height); - box->min_width = box->max_width = box->width = box->style->width.value.length.value; - } - /* set dimensions to object dimensions if auto */ - if (box->style->width.width == CSS_WIDTH_AUTO) { - box->style->width.width = CSS_WIDTH_LENGTH; - box->style->width.value.length.unit = CSS_UNIT_PX; - box->style->width.value.length.value = object->width; - box->min_width = box->max_width = box->width = object->width; - } - if (box->style->height.height == CSS_HEIGHT_AUTO) { - box->style->height.height = CSS_HEIGHT_LENGTH; - box->style->height.length.unit = CSS_UNIT_PX; - box->style->height.length.value = object->height; - box->height = object->height; - } - /* invalidate parent min, max widths */ - for (b = box->parent; b; b = b->parent) - b->max_width = UNKNOWN_MAX_WIDTH; - /* delete any clones of this box */ - while (box->next && box->next->clone) { - /* box_free_box(box->next); */ - box->next = box->next->next; - } + html_object_done(box, object); c->active--; break; @@ -714,7 +675,12 @@ void html_object_callback(content_msg msg, struct content *object, assert(0); } - if (c->status == CONTENT_STATUS_READY && c->active == 0) { + if (c->status == CONTENT_STATUS_READY && c->active == 0 && + (msg == CONTENT_MSG_LOADING || + msg == CONTENT_MSG_DONE || + msg == CONTENT_MSG_ERROR || + msg == CONTENT_MSG_REDIRECT || + msg == CONTENT_MSG_AUTH)) { /* all objects have arrived */ content_reformat(c, c->available_width, 0); c->status = CONTENT_STATUS_DONE; @@ -727,6 +693,32 @@ void html_object_callback(content_msg msg, struct content *object, } +/** + * Update a box whose content has completed rendering. + */ + +void html_object_done(struct box *box, struct content *object) +{ + struct box *b; + + box->object = object; + + if (box->width != UNKNOWN_WIDTH && + object->available_width != box->width) + content_reformat(object, box->width, box->height); + + /* invalidate parent min, max widths */ + for (b = box->parent; b; b = b->parent) + b->max_width = UNKNOWN_MAX_WIDTH; + + /* delete any clones of this box */ + while (box->next && box->next->clone) { + /* box_free_box(box->next); */ + box->next = box->next->next; + } +} + + /** * Check if a type is in a list. * diff --git a/render/html.h b/render/html.h index ab49b266b..8446b651e 100644 --- a/render/html.h +++ b/render/html.h @@ -88,7 +88,8 @@ void html_revive(struct content *c, unsigned int width, unsigned int height); void html_reformat(struct content *c, unsigned int width, unsigned int height); void html_destroy(struct content *c); void html_fetch_object(struct content *c, char *url, struct box *box, - const content_type *permitted_types); + const content_type *permitted_types, + int available_width, int available_height); /* in riscos/htmlinstance.c */ void html_add_instance(struct content *c, struct browser_window *bw, diff --git a/render/layout.c b/render/layout.c index 0e825f5cd..1979b2a0f 100644 --- a/render/layout.c +++ b/render/layout.c @@ -22,6 +22,7 @@ #include #include #include "netsurf/css/css.h" +#include "netsurf/content/content.h" #ifdef riscos #include "netsurf/desktop/gui.h" #endif @@ -59,7 +60,12 @@ static void place_float_below(struct box *c, int width, int cx, int y, struct box *cont); static void layout_table(struct box *box, int available_width); static void calculate_widths(struct box *box); +static void calculate_block_widths(struct box *box, int *min, int *max, + int *max_sum); static void calculate_inline_container_widths(struct box *box); +static void calculate_inline_replaced_widths(struct box *box, int *min, + int *max, int *line_max); +static void calculate_inline_widths(struct box *box, int *min, int *line_max); static void calculate_table_widths(struct box *table); @@ -267,6 +273,8 @@ void layout_block_context(struct box *block) /** * Compute dimensions of box, margins, paddings, and borders for a block-level * element. + * + * See CSS 2.1 10.3.3, 10.3.4, 10.6.2, and 10.6.3. */ void layout_block_find_dimensions(int available_width, struct box *box) @@ -291,11 +299,6 @@ void layout_block_find_dimensions(int available_width, struct box *box) break; } - layout_find_dimensions(available_width, style, margin, padding, border); - - box->width = layout_solve_width(available_width, width, margin, - padding, border); - /* height */ switch (style->height.height) { case CSS_HEIGHT_LENGTH: @@ -307,6 +310,40 @@ void layout_block_find_dimensions(int available_width, struct box *box) break; } + if (box->object) { + /* block-level replaced element, see 10.3.4 and 10.6.2 */ + if (width == AUTO && box->height == AUTO) { + width = box->object->width; + box->height = box->object->height; + } else if (width == AUTO) { + if (box->object->height) + width = box->object->width * + (float) box->height / + box->object->height; + else + width = box->object->width; + } else if (box->height == AUTO) { + if (box->object->width) + box->height = box->object->height * + (float) width / + box->object->width; + else + box->height = box->object->height; + } + } + + layout_find_dimensions(available_width, style, margin, padding, border); + + box->width = layout_solve_width(available_width, width, margin, + padding, border); + + if (box->object && box->object->type == CONTENT_HTML && + box->width != box->object->available_width) { + content_reformat(box->object, box->width, box->height); + if (style->height.height == CSS_HEIGHT_AUTO) + box->height = box->object->height; + } + if (margin[TOP] == AUTO) margin[TOP] = 0; if (margin[BOTTOM] == AUTO) @@ -377,16 +414,7 @@ void layout_float_find_dimensions(int available_width, break; case CSS_WIDTH_AUTO: default: - /* CSS 2.1 section 10.3.5 */ - available_width -= box->margin[LEFT] + box->border[LEFT] + - box->padding[LEFT] + box->padding[RIGHT] + - box->border[RIGHT] + box->margin[RIGHT]; - if (box->min_width < available_width) - box->width = available_width; - else - box->width = box->min_width; - if (box->max_width < box->width) - box->width = box->max_width; + box->width = AUTO; break; } @@ -401,6 +429,30 @@ void layout_float_find_dimensions(int available_width, break; } + if (box->object) { + /* floating replaced element, see 10.3.6 and 10.6.2 */ + if (box->width == AUTO && box->height == AUTO) { + box->width = box->object->width; + box->height = box->object->height; + } else if (box->width == AUTO) + box->width = box->object->width * (float) box->height / + box->object->height; + else if (box->height == AUTO) + box->height = box->object->height * (float) box->width / + box->object->width; + } else if (box->width == AUTO) { + /* CSS 2.1 section 10.3.5 */ + available_width -= box->margin[LEFT] + box->border[LEFT] + + box->padding[LEFT] + box->padding[RIGHT] + + box->border[RIGHT] + box->margin[RIGHT]; + if (box->min_width < available_width) + box->width = available_width; + else + box->width = box->min_width; + if (box->max_width < box->width) + box->width = box->max_width; + } + if (box->margin[TOP] == AUTO) box->margin[TOP] = 0; if (box->margin[BOTTOM] == AUTO) @@ -653,34 +705,87 @@ struct box * layout_line(struct box *first, int width, int *y, if (b->type != BOX_INLINE) continue; - if ((b->object || b->gadget) && b->style && - b->style->height.height == CSS_HEIGHT_LENGTH) - h = len(&b->style->height.length, b->style); - else - h = line_height(b->style ? b->style : + if (!b->object && !b->gadget) { + /* inline non-replaced, 10.3.1 and 10.6.1 */ + b->height = line_height(b->style ? b->style : b->parent->parent->style); - b->height = h; - - if (height < h) - height = h; - - if ((b->object || b->gadget) && b->style && - b->style->width.width == CSS_WIDTH_LENGTH) - b->width = len(&b->style->width.value.length, b->style); - else if ((b->object || b->gadget) && b->style && - b->style->width.width == CSS_WIDTH_PERCENT) - b->width = width * b->style->width.value.percent / 100; - else if (b->text) { - if (b->width == UNKNOWN_WIDTH) - b->width = font_width(b->font, b->text, - b->length); - } else - b->width = 0; + if (height < b->height) + height = b->height; + + if (b->text) { + if (b->width == UNKNOWN_WIDTH) + b->width = font_width(b->font, b->text, + b->length); + x += b->width + b->space ? + b->font->space_width : 0; + } else + b->width = 0; - if (b->text) - x += b->width + b->space ? b->font->space_width : 0; - else - x += b->width; + continue; + } + + /* inline replaced, 10.3.2 and 10.6.2 */ + assert(b->style); + + /* calculate box width */ + switch (b->style->width.width) { + case CSS_WIDTH_LENGTH: + b->width = len(&b->style->width.value.length, + b->style); + break; + case CSS_WIDTH_PERCENT: + b->width = width * + b->style->width.value.percent / + 100; + break; + case CSS_WIDTH_AUTO: + default: + b->width = AUTO; + break; + } + + /* height */ + switch (b->style->height.height) { + case CSS_HEIGHT_LENGTH: + b->height = len(&b->style->height.length, + b->style); + break; + case CSS_HEIGHT_AUTO: + default: + b->height = AUTO; + break; + } + + if (b->width == AUTO && b->height == AUTO) { + b->width = b->object->width; + b->height = b->object->height; + } else if (b->width == AUTO) { + if (b->object->height) + b->width = b->object->width * + (float) b->height / + b->object->height; + else + b->width = b->object->width; + } else if (b->height == AUTO) { + if (b->object->width) + b->height = b->object->height * + (float) b->width / + b->object->width; + else + b->height = b->object->height; + } + + if (b->object && b->object->type == CONTENT_HTML && + b->width != b->object->available_width) { + content_reformat(b->object, b->width, b->height); + if (b->style->height.height == CSS_HEIGHT_AUTO) + b->height = b->object->height; + } + + if (height < b->height) + height = b->height; + + x += b->width; } /* find new sides using this height */ @@ -691,11 +796,11 @@ struct box * layout_line(struct box *first, int width, int *y, x0 -= cx; x1 -= cx; - if (indent) - x0 += layout_text_indent(first->parent->parent->style, width); + if (indent) + x0 += layout_text_indent(first->parent->parent->style, width); - if (x1 < x0) - x1 = x0; + if (x1 < x0) + x1 = x0; /* pass 2: place boxes in line: loop body executed at least once */ for (x = x_previous = 0, b = first; x <= x1 - x0 && b; b = b->next) { @@ -984,7 +1089,7 @@ void place_float_below(struct box *c, int width, int cx, int y, /** - * layout a table + * Layout a table. */ void layout_table(struct box *table, int available_width) @@ -999,6 +1104,8 @@ void layout_table(struct box *table, int available_width) int table_height = 0; int *xs; /* array of column x positions */ int auto_width; + int spare_width; + int relative_sum = 0; struct box *c; struct box *row; struct box *row_group; @@ -1059,6 +1166,8 @@ void layout_table(struct box *table, int available_width) /* table narrower than required width for columns: * treat percentage widths as maximums */ for (i = 0; i != columns; i++) { + if (col[i].type == COLUMN_WIDTH_RELATIVE) + continue; if (col[i].type == COLUMN_WIDTH_PERCENT) { col[i].max = auto_width * col[i].width / 100; if (col[i].max < col[i].min) @@ -1070,6 +1179,8 @@ void layout_table(struct box *table, int available_width) } else { /* take percentages exactly */ for (i = 0; i != columns; i++) { + if (col[i].type == COLUMN_WIDTH_RELATIVE) + continue; if (col[i].type == COLUMN_WIDTH_PERCENT) { int width = auto_width * col[i].width / 100; if (width < col[i].min) @@ -1082,6 +1193,25 @@ void layout_table(struct box *table, int available_width) } } + /* allocate relative widths */ + spare_width = auto_width; + for (i = 0; i != columns; i++) { + if (col[i].type == COLUMN_WIDTH_RELATIVE) + relative_sum += col[i].width; + else + spare_width -= col[i].width; + } + if (spare_width < 0) + spare_width = 0; + for (i = 0; i != columns; i++) { + if (col[i].type == COLUMN_WIDTH_RELATIVE) { + col[i].min = col[i].max = (float) spare_width * + (float) col[i].width / relative_sum; + min_width += col[i].min; + max_width += col[i].max; + } + } + if (auto_width <= min_width) { /* not enough space: minimise column widths */ for (i = 0; i < columns; i++) { @@ -1227,7 +1357,7 @@ void layout_table(struct box *table, int available_width) void calculate_widths(struct box *box) { struct box *child; - int min = 0, max = 0, width, extra_fixed = 0; + int min = 0, max = 0, extra_fixed = 0; float extra_frac = 0; unsigned int side; struct css_style *style = box->style; @@ -1244,25 +1374,15 @@ void calculate_widths(struct box *box) switch (child->type) { case BOX_BLOCK: case BOX_TABLE: - if (child->type == BOX_TABLE) - calculate_table_widths(child); - else - calculate_widths(child); - if (child->style->width.width == CSS_WIDTH_LENGTH) { - width = len(&child->style->width.value.length, - child->style); - if (min < width) min = width; - if (max < width) max = width; - } else { - if (min < child->min_width) min = child->min_width; - if (max < child->max_width) max = child->max_width; - } + calculate_block_widths(child, &min, &max, 0); break; case BOX_INLINE_CONTAINER: calculate_inline_container_widths(child); - if (min < child->min_width) min = child->min_width; - if (max < child->max_width) max = child->max_width; + if (min < child->min_width) + min = child->min_width; + if (max < child->max_width) + max = child->max_width; break; default: @@ -1299,72 +1419,78 @@ void calculate_widths(struct box *box) } +/** + * Find min, max widths for a BOX_BLOCK, BOX_INLINE_BLOCK, BOX_FLOAT_*, + * or BOX_TABLE. + * + * \param box BLOCK, INLINE_BLOCK, FLOAT, or TABLE box + * \param min current min, updated to new min + * \param max current max, updated to new max + * \param max_sum sum of maximum widths, updated, or 0 if not required + */ + +void calculate_block_widths(struct box *box, int *min, int *max, + int *max_sum) +{ + int width; + + if (box->type == BOX_TABLE) + calculate_table_widths(box); + else + calculate_widths(box); + + if (box->style->width.width == CSS_WIDTH_LENGTH) { + width = len(&box->style->width.value.length, box->style); + if (*min < width) *min = width; + if (*max < width) *max = width; + if (max_sum) *max_sum += width; + } else if (box->style->width.width == CSS_WIDTH_AUTO && box->object) { + /* replaced element */ + if (box->style->height.height == CSS_HEIGHT_AUTO) + width = box->object->width; + else + width = box->object->width * + (float) len(&box->style->height.length, + box->style) / box->object->height; + if (*min < width) *min = width; + if (*max < width) *max = width; + if (max_sum) *max_sum += width; + } else { + if (*min < box->min_width) *min = box->min_width; + if (*max < box->max_width) *max = box->max_width; + if (max_sum) *max_sum += box->max_width; + } +} + + +/** + * Find min, max width for an inline container. + */ void calculate_inline_container_widths(struct box *box) { struct box *child; - int min = 0, max = 0, line_max = 0, width; - unsigned int i, j; + int min = 0, max = 0, line_max = 0; for (child = box->children; child != 0; child = child->next) { switch (child->type) { case BOX_INLINE: - if (child->object || child->gadget) { - if (child->style->width.width == CSS_WIDTH_LENGTH) { - child->width = len(&child->style->width.value.length, - child->style); - line_max += child->width; - if (min < child->width) - min = child->width; - } - - } else if (child->text) { - /* max = all one line */ - child->width = font_width(child->font, - child->text, child->length); - line_max += child->width; - if (child->next && child->space) - line_max += child->font->space_width; - - /* min = widest word */ - i = 0; - do { - for (j = i; j != child->length && child->text[j] != ' '; j++) - ; - width = font_width(child->font, child->text + i, (j - i)); - if (min < width) min = width; - i = j + 1; - } while (j != child->length); - } + if (child->object || child->gadget) + calculate_inline_replaced_widths(child, + &min, &max, &line_max); + else if (child->text) + calculate_inline_widths(child, + &min, &line_max); break; case BOX_INLINE_BLOCK: - calculate_widths(child); - if (child->style != 0 && - child->style->width.width == CSS_WIDTH_LENGTH) { - width = len(&child->style->width.value.length, - child->style); - if (min < width) min = width; - line_max += width; - } else { - if (min < child->min_width) min = child->min_width; - line_max += child->max_width; - } + calculate_block_widths(child, &min, &max, + &line_max); break; case BOX_FLOAT_LEFT: case BOX_FLOAT_RIGHT: - calculate_widths(child); - if (child->style != 0 && - child->style->width.width == CSS_WIDTH_LENGTH) { - width = len(&child->style->width.value.length, - child->style); - if (min < width) min = width; - if (max < width) max = width; - } else { - if (min < child->min_width) min = child->min_width; - if (max < child->max_width) max = child->max_width; - } + calculate_block_widths(child, &min, &max, 0); break; case BOX_BR: @@ -1392,6 +1518,63 @@ void calculate_inline_container_widths(struct box *box) } +/** + * Find min, max width for an inline replaced box. + */ + +void calculate_inline_replaced_widths(struct box *box, int *min, + int *max, int *line_max) +{ + int width; + + if (box->style->width.width == CSS_WIDTH_LENGTH) { + box->width = len(&box->style->width.value.length, box->style); + *line_max += box->width; + if (*min < box->width) + *min = box->width; + } else if (box->style->width.width == CSS_WIDTH_AUTO) { + if (box->style->height.height == CSS_HEIGHT_AUTO) + width = box->object->width; + else + width = box->object->width * + (float) len(&box->style->height.length, + box->style) / box->object->height; + if (*min < width) *min = width; + if (*max < width) *max = width; + } +} + + +/** + * Find min, max width for an inline text box. + */ + +void calculate_inline_widths(struct box *box, int *min, int *line_max) +{ + unsigned int i, j; + int width; + + /* max = all one line */ + box->width = font_width(box->font, box->text, box->length); + *line_max += box->width; + if (box->next && box->space) + *line_max += box->font->space_width; + + /* min = widest word */ + i = 0; + do { + for (j = i; j != box->length && box->text[j] != ' '; j++) + ; + width = font_width(box->font, box->text + i, (j - i)); + if (*min < width) *min = width; + i = j + 1; + } while (j != box->length); +} + + +/** + * Find min, max widths for a table and determine column width types. + */ void calculate_table_widths(struct box *table) { @@ -1406,8 +1589,9 @@ void calculate_table_widths(struct box *table) if (table->max_width != UNKNOWN_MAX_WIDTH) return; - free(table->col); - table->col = col = xcalloc(table->columns, sizeof(*col)); + if (!table->col) + table->col = xcalloc(table->columns, sizeof(*col)); + col = table->col; assert(table->children != 0 && table->children->children != 0); -- cgit v1.2.3