summaryrefslogtreecommitdiff
path: root/render
diff options
context:
space:
mode:
Diffstat (limited to 'render')
-rw-r--r--render/box.c517
-rw-r--r--render/box.h3
-rw-r--r--render/html.c94
-rw-r--r--render/html.h3
-rw-r--r--render/layout.c414
5 files changed, 699 insertions, 332 deletions
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;
@@ -728,6 +694,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.
*
* \param type the content_type to search for
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 <stdlib.h>
#include <string.h>
#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);