From ddeadd1c02880367ad786b113d352a519f45ec73 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Thu, 23 Jul 2009 23:05:34 +0000 Subject: Merge LibCSS port to trunk. svn path=/trunk/netsurf/; revision=8752 --- render/box.c | 39 +- render/box.h | 22 +- render/box_construct.c | 997 +++++++++--------------------- render/box_normalise.c | 637 +++++++++++-------- render/directory.c | 5 +- render/directory.h | 3 +- render/font.c | 48 +- render/font.h | 2 +- render/html.c | 191 +++--- render/html.h | 18 +- render/html_redraw.c | 349 ++++++----- render/hubbub_binding.c | 21 +- render/layout.c | 1558 ++++++++++++++++++++++++++++------------------- render/list.c | 49 +- render/list.h | 10 +- render/loosen.c | 501 --------------- render/loosen.h | 35 -- render/parser_binding.h | 8 +- render/table.c | 892 +++++++++++++++++++++------ render/table.h | 2 +- render/textplain.c | 9 +- render/textplain.h | 3 +- 22 files changed, 2804 insertions(+), 2595 deletions(-) delete mode 100644 render/loosen.c delete mode 100644 render/loosen.h (limited to 'render') diff --git a/render/box.c b/render/box.c index 7dde0b759..b1ebfcbad 100644 --- a/render/box.c +++ b/render/box.c @@ -28,6 +28,7 @@ #include #include "content/content.h" #include "css/css.h" +#include "css/dump.h" #include "desktop/options.h" #include "render/box.h" #include "render/form.h" @@ -58,7 +59,7 @@ static struct box_duplicate_llist *box_duplicate_last = NULL; * \return allocated and initialised box, or 0 on memory exhaustion */ -struct box * box_create(struct css_style *style, +struct box * box_create(css_computed_style *style, char *href, const char *target, char *title, char *id, void *context) { @@ -78,10 +79,11 @@ struct box * box_create(struct css_style *style, box->descendant_x0 = box->descendant_y0 = 0; box->descendant_x1 = box->descendant_y1 = 0; for (i = 0; i != 4; i++) - box->margin[i] = box->padding[i] = box->border[i] = 0; + box->margin[i] = box->padding[i] = box->border[i].width = 0; box->scroll_x = box->scroll_y = 0; box->min_width = 0; box->max_width = UNKNOWN_MAX_WIDTH; + box->byte_offset = 0; box->text = NULL; box->length = 0; box->space = 0; @@ -444,34 +446,34 @@ siblings: bool box_contains_point(struct box *box, int x, int y, bool *physically) { - if (box->x <= x + box->border[LEFT] && + if (box->x <= x + box->border[LEFT].width && x < box->x + box->padding[LEFT] + box->width + - box->border[RIGHT] + box->padding[RIGHT] && - box->y <= y + box->border[TOP] && + box->border[RIGHT].width + box->padding[RIGHT] && + box->y <= y + box->border[TOP].width && y < box->y + box->padding[TOP] + box->height + - box->border[BOTTOM] + box->padding[BOTTOM]) { + box->border[BOTTOM].width + box->padding[BOTTOM]) { *physically = true; return true; } if (box->list_marker && box->list_marker->x <= x + - box->list_marker->border[LEFT] && + box->list_marker->border[LEFT].width && x < box->list_marker->x + box->list_marker->padding[LEFT] + box->list_marker->width + - box->list_marker->border[RIGHT] + + box->list_marker->border[RIGHT].width + box->list_marker->padding[RIGHT] && box->list_marker->y <= y + - box->list_marker->border[TOP] && + box->list_marker->border[TOP].width && y < box->list_marker->y + box->list_marker->padding[TOP] + box->list_marker->height + - box->list_marker->border[BOTTOM] + + box->list_marker->border[BOTTOM].width + box->list_marker->padding[BOTTOM]) { *physically = true; return true; } - if ((box->style && box->style->overflow == CSS_OVERFLOW_VISIBLE) || - !box->style) { + if ((box->style && css_computed_overflow(box->style) == + CSS_OVERFLOW_VISIBLE) || !box->style) { if (box->x + box->descendant_x0 <= x && x < box->x + box->descendant_x1 && box->y + box->descendant_y0 <= y && @@ -502,8 +504,8 @@ struct box *box_object_at_point(struct content *c, int x, int y) assert(c->type == CONTENT_HTML); while ((box = box_at_point(box, x, y, &box_x, &box_y, &content))) { - if (box->style && - box->style->visibility == CSS_VISIBILITY_HIDDEN) + if (box->style && css_computed_visibility(box->style) == + CSS_VISIBILITY_HIDDEN) continue; if (box->object) @@ -532,8 +534,8 @@ struct box *box_href_at_point(struct content *c, int x, int y) assert(c->type == CONTENT_HTML); while ((box = box_at_point(box, x, y, &box_x, &box_y, &content))) { - if (box->style && - box->style->visibility == CSS_VISIBILITY_HIDDEN) + if (box->style && css_computed_visibility(box->style) == + CSS_VISIBILITY_HIDDEN) continue; if (box->href) @@ -580,7 +582,8 @@ bool box_visible(struct box *box) struct box *fallback; /* visibility: hidden */ - if (box->style && box->style->visibility == CSS_VISIBILITY_HIDDEN) + if (box->style && css_computed_visibility(box->style) == + CSS_VISIBILITY_HIDDEN) return false; /* check if a fallback */ @@ -651,7 +654,7 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth) if (box->gadget) fprintf(stream, "(gadget) "); if (box->style) - css_dump_style(stream, box->style); + nscss_dump_computed_style(stream, box->style); if (box->href) fprintf(stream, " -> '%s'", box->href); if (box->target) diff --git a/render/box.h b/render/box.h index 9a90c21a4..3134ea1a2 100644 --- a/render/box.h +++ b/render/box.h @@ -91,10 +91,10 @@ #include #include +#include "css/css.h" struct box; struct column; -struct css_style; struct object_params; struct object_param; @@ -106,7 +106,7 @@ typedef enum { BOX_TABLE_ROW_GROUP, BOX_FLOAT_LEFT, BOX_FLOAT_RIGHT, BOX_INLINE_BLOCK, BOX_BR, BOX_TEXT, - BOX_INLINE_END + BOX_INLINE_END, BOX_NONE } box_type; struct rect { @@ -114,6 +114,18 @@ struct rect { int x1, y1; }; +/* Sides of a box */ +enum box_side { TOP, RIGHT, BOTTOM, LEFT }; + +/** + * Container for box border details + */ +struct box_border { + enum css_border_style style; /**< border-style */ + enum css_border_color color; /**< border-color type */ + css_color c; /**< border-color value */ + int width; /**< border-width (pixels) */ +}; /** Node in box tree. All dimensions are in pixels. */ struct box { @@ -121,7 +133,7 @@ struct box { box_type type; /** Style for this box. 0 for INLINE_CONTAINER and FLOAT_*. */ - struct css_style * style; + css_computed_style *style; /** Coordinate of left padding edge relative to parent box, or relative * to ancestor that contains this box in float_children for FLOAT_. */ @@ -153,7 +165,7 @@ struct box { int margin[4]; /**< Margin: TOP, RIGHT, BOTTOM, LEFT. */ int padding[4]; /**< Padding: TOP, RIGHT, BOTTOM, LEFT. */ - int border[4]; /**< Border width: TOP, RIGHT, BOTTOM, LEFT. */ + struct box_border border[4]; /**< Border: TOP, RIGHT, BOTTOM, LEFT. */ int scroll_x; /**< Horizontal scroll of descendants. */ int scroll_y; /**< Vertical scroll of descendants. */ @@ -282,7 +294,7 @@ extern const char *TARGET_BLANK; #define UNKNOWN_MAX_WIDTH INT_MAX -struct box * box_create(struct css_style *style, +struct box * box_create(css_computed_style *style, char *href, const char *target, char *title, char *id, void *context); void box_add_child(struct box *parent, struct box *child); diff --git a/render/box_construct.c b/render/box_construct.c index b8aa5e600..564a443c3 100644 --- a/render/box_construct.c +++ b/render/box_construct.c @@ -37,6 +37,8 @@ #include "utils/config.h" #include "content/content.h" #include "css/css.h" +#include "css/utils.h" +#include "css/select.h" #include "desktop/browser.h" #include "desktop/options.h" #include "render/box.h" @@ -82,59 +84,30 @@ static const content_type image_types[] = { #endif CONTENT_UNKNOWN }; -#define MAX_SPAN (100) - - /* the strings are not important, since we just compare the pointers */ const char *TARGET_SELF = "_self"; const char *TARGET_PARENT = "_parent"; const char *TARGET_TOP = "_top"; const char *TARGET_BLANK = "_blank"; -/* keeps track of markup presentation */ -struct markup_track { - enum { - ALIGN_NONE, - ALIGN_LEFT, - ALIGN_CENTER, - ALIGN_RIGHT - } align; - bool cell_border; - colour border_color; - - bool cell_padding; - long padding_width; - - bool table; -}; - static bool convert_xml_to_box(xmlNode *n, struct content *content, - struct css_style *parent_style, + const css_computed_style *parent_style, struct box *parent, struct box **inline_container, - char *href, const char *target, char *title, - struct markup_track markup_track, - struct css_importance *author); + char *href, const char *target, char *title); bool box_construct_element(xmlNode *n, struct content *content, - struct css_style *parent_style, + const css_computed_style *parent_style, struct box *parent, struct box **inline_container, - char *href, const char *target, char *title, - struct markup_track markup_track, - struct css_importance *author); + char *href, const char *target, char *title); bool box_construct_text(xmlNode *n, struct content *content, - struct css_style *parent_style, + const css_computed_style *parent_style, struct box *parent, struct box **inline_container, char *href, const char *target, char *title); -static struct css_style * box_get_style(struct content *c, - struct css_style *parent_style, - xmlNode *n, struct markup_track *markup_track, - struct css_importance *author); -static void box_solve_display(struct css_style *style, bool root); +static css_computed_style * box_get_style(struct content *c, + const css_computed_style *parent_style, xmlNode *n); static void box_text_transform(char *s, unsigned int len, - css_text_transform tt); + enum css_text_transform tt); #define BOX_SPECIAL_PARAMS xmlNode *n, struct content *content, \ - struct box *box, bool *convert_children, \ - struct markup_track markup_track, \ - struct css_importance *author + struct box *box, bool *convert_children static bool box_a(BOX_SPECIAL_PARAMS); static bool box_body(BOX_SPECIAL_PARAMS); static bool box_br(BOX_SPECIAL_PARAMS); @@ -157,8 +130,10 @@ static bool box_get_attribute(xmlNode *n, const char *attribute, void *context, char **value); static struct frame_dimension *box_parse_multi_lengths(const char *s, unsigned int *count); -static void parse_inline_colour(char *text, colour *variable); - +static bool fetch_object_interned_url(struct content *c, lwc_string *url, + struct box *box, const content_type *permitted_types, + int available_width, int available_height, + bool background); /* element_table must be sorted by name */ struct element_entry { @@ -195,12 +170,7 @@ static const struct element_entry element_table[] = { bool xml_to_box(xmlNode *n, struct content *c) { struct box root; - struct box *inline_container = 0; - struct css_importance author; - struct markup_track markup_track; - markup_track.cell_border = false; - markup_track.cell_padding = false; - markup_track.align = ALIGN_NONE; + struct box *inline_container = NULL; assert(c->type == CONTENT_HTML); @@ -214,20 +184,12 @@ bool xml_to_box(xmlNode *n, struct content *c) root.float_children = NULL; root.next_float = NULL; - c->data.html.style = talloc_memdup(c, &css_base_style, - sizeof css_base_style); - if (!c->data.html.style) - return false; - c->data.html.style->font_size.value.length.value = - option_font_size * 0.1; - /* and get the default font family from the options */ - c->data.html.style->font_family = option_font_default; - c->data.html.object_count = 0; c->data.html.object = 0; - if (!convert_xml_to_box(n, c, c->data.html.style, &root, - &inline_container, 0, 0, 0, markup_track, &author)) + /* The root box's style */ + if (!convert_xml_to_box(n, c, NULL, &root, + &inline_container, 0, 0, 0)) return false; if (!box_normalise_block(&root, c)) @@ -241,7 +203,7 @@ bool xml_to_box(xmlNode *n, struct content *c) /* mapping from CSS display to box type - * this table must be in sync with css/css_enums */ + * this table must be in sync with libcss' css_display enum */ static const box_type box_map[] = { 0, /*CSS_DISPLAY_INHERIT,*/ BOX_INLINE, /*CSS_DISPLAY_INLINE,*/ @@ -255,10 +217,11 @@ static const box_type box_map[] = { BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_HEADER_GROUP,*/ BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_FOOTER_GROUP,*/ BOX_TABLE_ROW, /*CSS_DISPLAY_TABLE_ROW,*/ - BOX_INLINE, /*CSS_DISPLAY_TABLE_COLUMN_GROUP,*/ - BOX_INLINE, /*CSS_DISPLAY_TABLE_COLUMN,*/ + BOX_NONE, /*CSS_DISPLAY_TABLE_COLUMN_GROUP,*/ + BOX_NONE, /*CSS_DISPLAY_TABLE_COLUMN,*/ BOX_TABLE_CELL, /*CSS_DISPLAY_TABLE_CELL,*/ - BOX_INLINE /*CSS_DISPLAY_TABLE_CAPTION,*/ + BOX_INLINE, /*CSS_DISPLAY_TABLE_CAPTION,*/ + BOX_NONE /*CSS_DISPLAY_NONE*/ }; @@ -267,31 +230,25 @@ static const box_type box_map[] = { * * \param n fragment of xml tree * \param content content of type CONTENT_HTML that is being processed - * \param parent_style style at this point in xml tree + * \param parent_style style at this point in xml tree, or NULL for root box * \param parent parent in box tree * \param inline_container current inline container box, or 0, updated to * new current inline container on exit * \param href current link URL, or 0 if not in a link * \param target current link target, or 0 if none * \param title current title, or 0 if none - * \param markup_track track presentation markup that affects descendents - * \param author denotes whether current style has author level - * importance for certain properties * \return true on success, false on memory exhaustion */ bool convert_xml_to_box(xmlNode *n, struct content *content, - struct css_style *parent_style, + const css_computed_style *parent_style, struct box *parent, struct box **inline_container, - char *href, const char *target, char *title, - struct markup_track markup_track, - struct css_importance *author) + char *href, const char *target, char *title) { switch (n->type) { case XML_ELEMENT_NODE: return box_construct_element(n, content, parent_style, parent, - inline_container, - href, target, title, markup_track, author); + inline_container, href, target, title); case XML_TEXT_NODE: return box_construct_text(n, content, parent_style, parent, inline_container, href, target, title); @@ -307,25 +264,20 @@ bool convert_xml_to_box(xmlNode *n, struct content *content, * * \param n XML node of type XML_ELEMENT_NODE * \param content content of type CONTENT_HTML that is being processed - * \param parent_style style at this point in xml tree + * \param parent_style style at this point in xml tree, or NULL for root node * \param parent parent in box tree * \param inline_container current inline container box, or 0, updated to * new current inline container on exit * \param href current link URL, or 0 if not in a link * \param target current link target, or 0 if none * \param title current title, or 0 if none - * \param markup_track track presentation markup that affects descendents - * \param author denotes whether current style has author level - * importance for certain properties * \return true on success, false on memory exhaustion */ bool box_construct_element(xmlNode *n, struct content *content, - struct css_style *parent_style, + const css_computed_style *parent_style, struct box *parent, struct box **inline_container, - char *href, const char *target, char *title, - struct markup_track markup_track, - struct css_importance *author) + char *href, const char *target, char *title) { bool convert_children = true; char *id = 0; @@ -333,14 +285,14 @@ bool box_construct_element(xmlNode *n, struct content *content, struct box *box = 0; struct box *inline_container_c; struct box *inline_end; - struct css_style *style = 0; + css_computed_style *style = 0; struct element_entry *element; xmlChar *title0; xmlNode *c; + lwc_string *bgimage_uri; assert(n); assert(n->type == XML_ELEMENT_NODE); - assert(parent_style); assert(parent); assert(inline_container); @@ -352,18 +304,23 @@ bool box_construct_element(xmlNode *n, struct content *content, */ parent->strip_leading_newline = 0; - style = box_get_style(content, parent_style, n, &markup_track, author); + style = box_get_style(content, parent_style, n); if (!style) return false; /* extract title attribute, if present */ if ((title0 = xmlGetProp(n, (const xmlChar *) "title"))) { char *title1 = squash_whitespace((char *) title0); + xmlFree(title0); + if (!title1) return false; + title = talloc_strdup(content, title1); + free(title1); + if (!title) return false; } @@ -376,8 +333,25 @@ bool box_construct_element(xmlNode *n, struct content *content, box = box_create(style, href, target, title, id, content); if (!box) return false; - /* set box type from style */ - box->type = box_map[style->display]; + /* set box type from computed display */ + if ((css_computed_position(style) == CSS_POSITION_ABSOLUTE || + css_computed_position(style) == CSS_POSITION_FIXED) && + (css_computed_display_static(style) == + CSS_DISPLAY_INLINE || + css_computed_display_static(style) == + CSS_DISPLAY_INLINE_BLOCK || + css_computed_display_static(style) == + CSS_DISPLAY_INLINE_TABLE)) { + /* Special case for absolute positioning: make absolute inlines + * into inline block so that the boxes are constructed in an + * inline container as if they were not absolutely positioned. + * Layout expects and handles this. */ + box->type = box_map[CSS_DISPLAY_INLINE_BLOCK]; + } else { + /* Normal mapping */ + box->type = box_map[css_computed_display(style, + n->parent == NULL)]; + } /* special elements */ element = bsearch((const char *) n->name, element_table, @@ -385,16 +359,17 @@ bool box_construct_element(xmlNode *n, struct content *content, (int (*)(const void *, const void *)) strcmp); if (element) { /* a special convert function exists for this element */ - if (!element->convert(n, content, box, &convert_children, - markup_track, author)) + if (!element->convert(n, content, box, &convert_children)) return false; + href = box->href; target = box->target; } - if (style->display == CSS_DISPLAY_NONE) { + if (box->type == BOX_NONE || css_computed_display(box->style, + n->parent == NULL) == CSS_DISPLAY_NONE) { /* Free style and invalidate box's style pointer */ - talloc_free(style); + css_computed_style_destroy(style); box->style = NULL; /* If this box has an associated gadget, invalidate the @@ -416,60 +391,71 @@ bool box_construct_element(xmlNode *n, struct content *content, (box->type == BOX_INLINE || box->type == BOX_BR || box->type == BOX_INLINE_BLOCK || - style->float_ == CSS_FLOAT_LEFT || - style->float_ == CSS_FLOAT_RIGHT)) { + css_computed_float(style) == CSS_FLOAT_LEFT || + css_computed_float(style) == CSS_FLOAT_RIGHT)) { /* this is the first inline in a block: make a container */ *inline_container = box_create(0, 0, 0, 0, 0, content); if (!*inline_container) return false; + (*inline_container)->type = BOX_INLINE_CONTAINER; + box_add_child(parent, *inline_container); } if (box->type == BOX_INLINE || box->type == BOX_BR) { /* inline box: add to tree and recurse */ box_add_child(*inline_container, box); + if (convert_children && n->children) { for (c = n->children; c; c = c->next) if (!convert_xml_to_box(c, content, style, parent, inline_container, - href, target, title, - markup_track, author)) + href, target, title)) return false; + inline_end = box_create(style, href, target, title, id, content); if (!inline_end) return false; + inline_end->type = BOX_INLINE_END; + if (*inline_container) box_add_child(*inline_container, inline_end); else box_add_child(box->parent, inline_end); + box->inline_end = inline_end; inline_end->inline_end = box; } } else if (box->type == BOX_INLINE_BLOCK) { /* inline block box: add to tree and recurse */ box_add_child(*inline_container, box); + inline_container_c = 0; + for (c = n->children; convert_children && c; c = c->next) if (!convert_xml_to_box(c, content, style, box, &inline_container_c, - href, target, title, markup_track, - author)) + href, target, title)) return false; } else { /* list item: compute marker, then treat as non-inline box */ - if (style->display == CSS_DISPLAY_LIST_ITEM) { + if (css_computed_display(style, n->parent == NULL) == + CSS_DISPLAY_LIST_ITEM) { + lwc_string *image_uri; struct box *marker; + marker = box_create(style, 0, 0, title, 0, content); if (!marker) return false; + marker->type = BOX_BLOCK; + /** \todo marker content (list-style-type) */ - switch (style->list_style_type) { + switch (css_computed_list_style_type(style)) { case CSS_LIST_STYLE_TYPE_DISC: - default: /* 2022 BULLET */ marker->text = (char *) "\342\200\242"; marker->length = 3; @@ -489,6 +475,7 @@ bool box_construct_element(xmlNode *n, struct content *content, case CSS_LIST_STYLE_TYPE_LOWER_ROMAN: case CSS_LIST_STYLE_TYPE_UPPER_ALPHA: case CSS_LIST_STYLE_TYPE_UPPER_ROMAN: + default: if (parent->last) { struct box *last = parent->last; @@ -515,9 +502,11 @@ bool box_construct_element(xmlNode *n, struct content *content, list_marker->rows + 1; } } + marker->text = talloc_array(content, char, 20); if (!marker->text) return false; + snprintf(marker->text, 20, "%u.", marker->rows); marker->length = strlen(marker->text); break; @@ -526,43 +515,50 @@ bool box_construct_element(xmlNode *n, struct content *content, marker->length = 0; break; } - if (style->list_style_image.type == - CSS_LIST_STYLE_IMAGE_URI) { - if (!html_fetch_object(content, - style->list_style_image.uri, + + if (css_computed_list_style_image(style, &image_uri) == + CSS_LIST_STYLE_IMAGE_URI && + image_uri != NULL) { + if (!fetch_object_interned_url(content, + image_uri, marker, 0, content->available_width, 1000, false)) return false; } + box->list_marker = marker; marker->parent = box; } /* float: insert a float box between the parent and * current node. Note: new parent will be the float */ - if (style->float_ == CSS_FLOAT_LEFT || - style->float_ == CSS_FLOAT_RIGHT) { + if (css_computed_float(style) == CSS_FLOAT_LEFT || + css_computed_float(style) == CSS_FLOAT_RIGHT) { parent = box_create(0, href, target, title, 0, content); if (!parent) return false; - if (style->float_ == CSS_FLOAT_LEFT) + + if (css_computed_float(style) == CSS_FLOAT_LEFT) parent->type = BOX_FLOAT_LEFT; else parent->type = BOX_FLOAT_RIGHT; + box_add_child(*inline_container, parent); } /* non-inline box: add to tree and recurse */ box_add_child(parent, box); + inline_container_c = 0; + for (c = n->children; convert_children && c; c = c->next) if (!convert_xml_to_box(c, content, style, box, &inline_container_c, - href, target, title, markup_track, - author)) + href, target, title)) return false; - if (style->float_ == CSS_FLOAT_NONE) + + if (css_computed_float(style) == CSS_FLOAT_NONE) /* new inline container unless this is a float */ *inline_container = 0; } @@ -571,23 +567,23 @@ bool box_construct_element(xmlNode *n, struct content *content, if ((s = (char *) xmlGetProp(n, (const xmlChar *) "colspan"))) { if (isdigit(s[0])) { box->columns = strtol(s, NULL, 10); - if ((MAX_SPAN < box->columns) || (box->columns < 1)) - box->columns = 1; } xmlFree(s); } + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "rowspan"))) { if (isdigit(s[0])) { box->rows = strtol(s, NULL, 10); - if ((MAX_SPAN < box->rows) || (box->rows < 1)) - box->rows = 1; } xmlFree(s); } /* fetch any background image for this box */ - if (style->background_image.type == CSS_BACKGROUND_IMAGE_URI) { - if (!html_fetch_object(content, style->background_image.uri, + if (css_computed_background_image(style, &bgimage_uri) == + CSS_BACKGROUND_IMAGE_IMAGE && + bgimage_uri != NULL) { + if (!fetch_object_interned_url(content, + bgimage_uri, box, image_types, content->available_width, 1000, true)) return false; @@ -613,7 +609,7 @@ bool box_construct_element(xmlNode *n, struct content *content, */ bool box_construct_text(xmlNode *n, struct content *content, - struct css_style *parent_style, + const css_computed_style *parent_style, struct box *parent, struct box **inline_container, char *href, const char *target, char *title) { @@ -625,8 +621,9 @@ bool box_construct_text(xmlNode *n, struct content *content, assert(parent); assert(inline_container); - if (parent_style->white_space == CSS_WHITE_SPACE_NORMAL || - parent_style->white_space == CSS_WHITE_SPACE_NOWRAP) { + if (css_computed_white_space(parent_style) == CSS_WHITE_SPACE_NORMAL || + css_computed_white_space(parent_style) == + CSS_WHITE_SPACE_NOWRAP) { char *text = squash_whitespace((char *) n->content); if (!text) return false; @@ -643,10 +640,14 @@ bool box_construct_text(xmlNode *n, struct content *content, parent = parent->parent; box_dump(stderr, parent, 0); } + assert((*inline_container)->last != 0); + (*inline_container)->last->space = 1; } + free(text); + return true; } @@ -657,34 +658,48 @@ bool box_construct_text(xmlNode *n, struct content *content, free(text); return false; } + (*inline_container)->type = BOX_INLINE_CONTAINER; + box_add_child(parent, *inline_container); } - box = box_create(parent_style, href, target, title, 0, content); + /** \todo Dropping const here is not clever */ + box = box_create((css_computed_style *) parent_style, + href, target, title, 0, content); if (!box) { free(text); return false; } + box->type = BOX_TEXT; + box->text = talloc_strdup(content, text); free(text); if (!box->text) return false; + box->length = strlen(box->text); + /* strip ending space char off */ if (box->length > 1 && box->text[box->length - 1] == ' ') { box->space = 1; box->length--; } - if (parent_style->text_transform != CSS_TEXT_TRANSFORM_NONE) + + if (css_computed_text_transform(parent_style) != + CSS_TEXT_TRANSFORM_NONE) box_text_transform(box->text, box->length, - parent_style->text_transform); - if (parent_style->white_space == CSS_WHITE_SPACE_NOWRAP) { + css_computed_text_transform(parent_style)); + + if (css_computed_white_space(parent_style) == + CSS_WHITE_SPACE_NOWRAP) { unsigned int i; + for (i = 0; i != box->length && box->text[i] != ' '; ++i) ; /* no body */ + if (i != box->length) { /* there is a space in text block and we * want all spaces to be converted to NBSP @@ -699,9 +714,12 @@ bool box_construct_text(xmlNode *n, struct content *content, } box_add_child(*inline_container, box); + if (box->text[0] == ' ') { box->length--; + memmove(box->text, &box->text[1], box->length); + if (box->prev != NULL) box->prev->space = 1; } @@ -710,17 +728,22 @@ bool box_construct_text(xmlNode *n, struct content *content, /* white-space: pre */ char *text = cnv_space2nbsp((char *) n->content); char *current; + enum css_white_space white_space = + css_computed_white_space(parent_style); + /* note: pre-wrap/pre-line are unimplemented */ - assert(parent_style->white_space == CSS_WHITE_SPACE_PRE || - parent_style->white_space == - CSS_WHITE_SPACE_PRE_LINE || - parent_style->white_space == - CSS_WHITE_SPACE_PRE_WRAP); + assert(white_space == CSS_WHITE_SPACE_PRE || + white_space == CSS_WHITE_SPACE_PRE_LINE || + white_space == CSS_WHITE_SPACE_PRE_WRAP); + if (!text) return false; - if (parent_style->text_transform != CSS_TEXT_TRANSFORM_NONE) + + if (css_computed_text_transform(parent_style) != + CSS_TEXT_TRANSFORM_NONE) box_text_transform(text, strlen(text), - parent_style->text_transform); + css_computed_text_transform(parent_style)); + current = text; /* swallow a single leading new line */ @@ -739,7 +762,9 @@ bool box_construct_text(xmlNode *n, struct content *content, do { size_t len = strcspn(current, "\r\n"); char old = current[len]; + current[len] = 0; + if (!*inline_container) { *inline_container = box_create(0, 0, 0, 0, 0, content); @@ -747,26 +772,37 @@ bool box_construct_text(xmlNode *n, struct content *content, free(text); return false; } + (*inline_container)->type = BOX_INLINE_CONTAINER; + box_add_child(parent, *inline_container); } - box = box_create(parent_style, href, target, title, 0, - content); + + /** \todo Dropping const isn't clever */ + box = box_create((css_computed_style *) parent_style, + href, target, title, 0, content); if (!box) { free(text); return false; } + box->type = BOX_TEXT; + box->text = talloc_strdup(content, current); if (!box->text) { free(text); return false; } + box->length = strlen(box->text); + box_add_child(*inline_container, box); + current[len] = old; + current += len; + if (current[0] == '\r' && current[1] == '\n') { current += 2; *inline_container = 0; @@ -775,6 +811,7 @@ bool box_construct_text(xmlNode *n, struct content *content, *inline_container = 0; } } while (*current); + free(text); } @@ -782,576 +819,75 @@ bool box_construct_text(xmlNode *n, struct content *content, } +static void *myrealloc(void *ptr, size_t len, void *pw) +{ + return talloc_realloc_size(pw, ptr, len); +} + /** * Get the style for an element. * * \param c content of type CONTENT_HTML that is being processed - * \param parent_style style at this point in xml tree + * \param parent_style style at this point in xml tree, or NULL for root * \param n node in xml tree - * \param markup_track track presentation markup that affects descendents - * \param author denotes whether current style has author level - * importance for certain properties - * \return the new style, or 0 on memory exhaustion - * - * The style is collected from three sources: - * 1. any styles for this element in the document stylesheet(s) - * 2. the 'style' attribute - * 3. non-CSS HTML attributes (subject to importance of CSS style properties) + * \return the new style, or NULL on memory exhaustion */ - -struct css_style * box_get_style(struct content *c, - struct css_style *parent_style, - xmlNode *n, struct markup_track *markup_track, - struct css_importance *author) +css_computed_style *box_get_style(struct content *c, + const css_computed_style *parent_style, + xmlNode *n) { char *s; - struct css_style *style; - struct css_style *style_new; - char *url; - url_func_result res; - colour border_color = 0x888888; /* mid-grey default for tables */ - - /* if not in a table, switch off cellpadding and cell borders - * and record that we're not in a table */ - if (strcmp((const char *) n->name, "thead") != 0 && - strcmp((const char *) n->name, "tbody") != 0 && - strcmp((const char *) n->name, "tfoot") != 0 && - strcmp((const char *) n->name, "tr") != 0 && - strcmp((const char *) n->name, "td") != 0 && - strcmp((const char *) n->name, "th") != 0 && - strcmp((const char *) n->name, "col") != 0 && - strcmp((const char *) n->name, "colgroup") != 0) { - markup_track->cell_border = false; - markup_track->cell_padding = false; - markup_track->table = false; - } - - style = talloc_memdup(c, parent_style, sizeof *style); - if (!style) - return 0; - - style_new = talloc_memdup(c, &css_blank_style, sizeof *style_new); - if (!style_new) - return 0; - css_get_style(c->data.html.working_stylesheet, n, style_new, author); - css_cascade(style, style_new, NULL); + css_stylesheet *inline_style = NULL; + css_computed_style *partial; + css_computed_style *style; - /* style_new isn't needed past this point */ - talloc_free(style_new); - - /* Handle style attribute. (style attribute values have high enough - * specificity to override existing style data.) */ + /* Firstly, construct inline stylesheet, if any */ if ((s = (char *) xmlGetProp(n, (const xmlChar *) "style"))) { - struct css_style *astyle; - astyle = css_duplicate_style(&css_empty_style); - if (!astyle) { - xmlFree(s); - return 0; - } - css_parse_property_list(c, astyle, s); - css_cascade(style, astyle, author); - css_free_style(astyle); - xmlFree(s); - } - - /* Apply presentational HTML attributes to style - * (Only apply if style property does not have "author" level - * importance or higher.) - */ - - /* This property only applies to the body element, if you believe - * the spec. Many browsers seem to allow it on other elements too, - * so let's be generic ;) */ - if (!author->background_image && (s = (char *) xmlGetProp(n, - (const xmlChar *) "background"))) { - res = url_join(s, c->data.html.base_url, &url); - xmlFree(s); - if (res == URL_FUNC_NOMEM) { - return 0; - } else if (res == URL_FUNC_OK) { - /* if url is equivalent to the parent's url, - * we've got infinite inclusion: ignore */ - if (strcmp(url, c->data.html.base_url) == 0) - free(url); - else { - style->background_image.type = - CSS_BACKGROUND_IMAGE_URI; - style->background_image.uri = talloc_strdup( - c, url); - free(url); - if (!style->background_image.uri) - return 0; - } - } - } - - if (!author->background_color && (s = (char *) xmlGetProp(n, - (const xmlChar *) "bgcolor"))) { - parse_inline_colour(s, &style->background_color); - xmlFree(s); - } - - if (!author->color && (s = (char *) xmlGetProp(n, - (const xmlChar *) "color"))) { - parse_inline_colour(s, &style->color); - xmlFree(s); - } - - if (!author->height && (s = (char *) xmlGetProp(n, - (const xmlChar *) "height")) && - ((strcmp((const char *) n->name, "iframe") == 0) || - (strcmp((const char *) n->name, "td") == 0) || - (strcmp((const char *) n->name, "th") == 0) || - (strcmp((const char *) n->name, "tr") == 0) || - (strcmp((const char *) n->name, "img") == 0) || - (strcmp((const char *) n->name, "object") == 0) || - (strcmp((const char *) n->name, "applet") == 0))) { - float value = isdigit(s[0]) ? atof(s) : -1; - if (value <= 0 || strlen(s) == 0) { - /* ignore negative values and height="" */ - } else if (strrchr(s, '%')) { - style->height.height = CSS_HEIGHT_PERCENT; - style->height.value.percent = value; - } else { - style->height.height = CSS_HEIGHT_LENGTH; - style->height.value.length.unit = CSS_UNIT_PX; - style->height.value.length.value = value; - } - xmlFree(s); - } - - if (!author->width && strcmp((const char *) n->name, "input") == 0) { - int size = -1; - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "size"))) { - size = isdigit(s[0]) ? atoi(s): -1; - if (0 < size) { - char *type = (char *) xmlGetProp(n, - (const xmlChar *) "type"); - style->width.width = CSS_WIDTH_LENGTH; - if (!type || strcasecmp(type, "text") == 0 || - strcasecmp(type, "password") == 0) - /* in characters for text, password */ - style->width.value.length.unit = - CSS_UNIT_EX; - else if (strcasecmp(type, "file") != 0) - /* in pixels otherwise; ignore width - * on file, because we do them - * differently to most browsers */ - style->width.value.length.unit = - CSS_UNIT_PX; - style->width.value.length.value = size; - if (type) - xmlFree(type); - } - xmlFree(s); - } - /* If valid maxlength value is provided, the size attribute is - * unset and maxlength is small, use it to reduce input width - * to sensible size */ - if ((s = (char *) xmlGetProp(n, (const xmlChar *) - "maxlength"))) { - int maxlength = isdigit(s[0]) ? atoi(s): -1; - if (0 < maxlength && size == -1 && maxlength < 10) { - char *type; - /* Bump up really small widths */ - maxlength = maxlength < 5 ? maxlength + 1 : - maxlength; - type = (char *) xmlGetProp(n, - (const xmlChar *) "type"); - style->width.width = CSS_WIDTH_LENGTH; - if (!type || strcasecmp(type, "text") == 0 || - strcasecmp(type, "password") == 0) - /* in characters for text, password */ - style->width.value.length.unit = - CSS_UNIT_EX; - style->width.value.length.value = maxlength; - if (type) - xmlFree(type); - } - xmlFree(s); - } - } + inline_style = nscss_create_inline_style( + (uint8_t *) s, strlen(s), + c->data.html.encoding, c->url, false, + c->data.html.dict, myrealloc, c); - if (!author->color && strcmp((const char *) n->name, "body") == 0) { - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "text"))) { - parse_inline_colour(s, &style->color); - xmlFree(s); - } - } - - if (!author->width && (s = (char *) xmlGetProp(n, - (const xmlChar *) "width")) && - ((strcmp((const char *) n->name, "hr") == 0) || - (strcmp((const char *) n->name, "iframe") == 0) || - (strcmp((const char *) n->name, "img") == 0) || - (strcmp((const char *) n->name, "object") == 0) || - (strcmp((const char *) n->name, "table") == 0) || - (strcmp((const char *) n->name, "td") == 0) || - (strcmp((const char *) n->name, "th") == 0) || - (strcmp((const char *) n->name, "applet") == 0))) { - float value = isdigit(s[0]) ? atof(s) : -1; - if (value < 0 || strlen(s) == 0) { - /* ignore negative values and width="" */ - } else if (strrchr(s, '%')) { - style->width.width = CSS_WIDTH_PERCENT; - style->width.value.percent = value; - } else { - style->width.width = CSS_WIDTH_LENGTH; - style->width.value.length.unit = CSS_UNIT_PX; - style->width.value.length.value = value; - } xmlFree(s); - } - - if (strcmp((const char *) n->name, "textarea") == 0) { - if (!author->height && (s = (char *) xmlGetProp(n, - (const xmlChar *) "rows"))) { - int value = isdigit(s[0]) ? atoi(s): -1; - if (0 < value) { - style->height.height = CSS_HEIGHT_LENGTH; - style->height.value.length.unit = CSS_UNIT_EM; - style->height.value.length.value = value; - } - xmlFree(s); - } - if (!author->width && (s = (char *) xmlGetProp(n, - (const xmlChar *) "cols"))) { - int value = isdigit(s[0]) ? atoi(s): -1; - if (0 < value) { - style->width.width = CSS_WIDTH_LENGTH; - style->width.value.length.unit = CSS_UNIT_EX; - style->width.value.length.value = value; - } - xmlFree(s); - } - } - - if (strcmp((const char *) n->name, "table") == 0) { - if (!author->border_spacing && (s = (char *) xmlGetProp(n, - (const xmlChar *) "cellspacing"))) { - /* percentage cellspacing not implemented */ - if (!strrchr(s, '%')) { - int value = isdigit(s[0]) ? atoi(s): -1; - if (0 <= value) { - style->border_spacing.border_spacing = - CSS_BORDER_SPACING_LENGTH; - style->border_spacing.horz.unit = - style->border_spacing.vert.unit = - CSS_UNIT_PX; - style->border_spacing.horz.value = - style->border_spacing.vert.value = - value; - } - } - xmlFree(s); - } - - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "bordercolor"))) { - parse_inline_colour(s, &border_color); - xmlFree(s); - } - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "border"))) { - int border_width = atoi(s); - /* precentage border width not implemented */ - if (!strrchr(s, '%') && 0 < border_width) { - unsigned int i; - for (i = 0; i != 4; i++) { - if (!author->border_color[i]) - style->border[i].color = - border_color; - if (!author->border_width[i]) { - style->border[i].width.width = - CSS_BORDER_WIDTH_LENGTH; - style->border[i].width.value. - value = border_width; - style->border[i].width.value. - unit = CSS_UNIT_PX; - } - if (!author->border_style[i]) - style->border[i].style = - CSS_BORDER_STYLE_OUTSET; - } - } - xmlFree(s); - } - } - if (strcmp((const char *) n->name, "td") == 0 || - strcmp((const char *) n->name, "th") == 0) { - /* set any cellborders stipulated by associated table */ - if (markup_track->cell_border) { - unsigned int i; - for (i = 0; i != 4; i++) { - if (!author->border_color[i]) - style->border[i].color = markup_track-> - border_color; - if (!author->border_width[i]) { - style->border[i].width.width = - CSS_BORDER_WIDTH_LENGTH; - style->border[i].width.value.value = 1; - style->border[i].width.value.unit = - CSS_UNIT_PX; - } - if (!author->border_style[i]) - style->border[i].style = - CSS_BORDER_STYLE_INSET; - } - } - /* set any cellpadding stipulated by associated table */ - if (markup_track->cell_padding) { - unsigned int i; - for (i = 0; i != 4; i++) { - if (!author->padding[i]) { - style->padding[i].padding = - CSS_PADDING_LENGTH; - style->padding[i].value.length.value = - markup_track->padding_width; - style->padding[i].value.length.unit = - CSS_UNIT_PX; - } - } - } + if (inline_style == NULL) + return NULL; } - if ((strcmp((const char *) n->name, "img") == 0) || - (strcmp((const char *) n->name, "image") == 0) || - (strcmp((const char *) n->name, "applet") == 0)) { - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "hspace"))) { - /* percentage hspace not implemented */ - if (!strrchr(s, '%')) { - int value = isdigit(s[0]) ? atoi(s): -1; - if (0 <= value && !author->margin[LEFT]) { - style->margin[LEFT].margin = - CSS_MARGIN_LENGTH; - style->margin[LEFT].value.length.value = - value; - style->margin[LEFT].value.length.unit = - CSS_UNIT_PX; - } - if (0 <= value && !author->margin[RIGHT]) { - style->margin[RIGHT].margin = - CSS_MARGIN_LENGTH; - style->margin[RIGHT].value.length. - value = value; - style->margin[RIGHT].value.length.unit = - CSS_UNIT_PX; - } - } - xmlFree(s); - } - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "vspace"))) { - /* percentage vspace not implemented */ - if (!strrchr(s, '%')) { - int value = isdigit(s[0]) ? atoi(s): -1; - if (0 <= value && !author->margin[TOP]) { - style->margin[TOP].margin = - CSS_MARGIN_LENGTH; - style->margin[TOP].value.length.value = - value; - style->margin[TOP].value.length.unit = - CSS_UNIT_PX; - } - if (0 <= value && !author->margin[BOTTOM]) { - style->margin[BOTTOM].margin = - CSS_MARGIN_LENGTH; - style->margin[BOTTOM].value.length. - value = value; - style->margin[BOTTOM].value.length. - unit = CSS_UNIT_PX; - } - } - xmlFree(s); - } - } + /* Select partial style for element */ + partial = nscss_get_style(c, n, CSS_PSEUDO_ELEMENT_NONE, + CSS_MEDIA_SCREEN, inline_style, myrealloc, c); - /* Handle markup-originating alignment of block level elements. - * Adjust left and right margins. text-align property is handled in - * the default CSS file. - */ - if (markup_track->align != ALIGN_NONE && - (style->display == CSS_DISPLAY_BLOCK || - style->display == CSS_DISPLAY_TABLE) && - (strcmp((const char *) n->name, "blockquote") != 0)) { - if (!author->margin[LEFT]) { - if (markup_track->align == ALIGN_LEFT) { - /* left */ - style->margin[LEFT].margin = CSS_MARGIN_LENGTH; - style->margin[LEFT].value.length.value = 0; - style->margin[LEFT].value.length.unit = - CSS_UNIT_PX; - } else - /* center or right */ - style->margin[LEFT].margin = CSS_MARGIN_AUTO; - } - - if (!author->margin[RIGHT]) { - if (markup_track->align == ALIGN_RIGHT) { - /* right */ - style->margin[RIGHT].margin = CSS_MARGIN_LENGTH; - style->margin[RIGHT].value.length.value= 0; - style->margin[RIGHT].value.length.unit = - CSS_UNIT_PX; - } else - /* left or center */ - style->margin[RIGHT].margin = CSS_MARGIN_AUTO; - } - if (author->margin[LEFT] || author->margin[RIGHT]) { - /* author stylesheet sets a margin so stop markup - * alignment model propagation */ - markup_track->align = ALIGN_NONE; - } - } - /* Centered tables are a special case. The align attribute only - * affects the current element (table) and overrides any existing - * HTML alignment rule. Tables aligned to left or right are floated - * by the default CSS file. */ - if (!author->margin[LEFT] && !author->margin[RIGHT] && - strcmp((const char *) n->name, "table") == 0) { - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "align"))) { - if (strcasecmp(s, "center") == 0) { - style->margin[LEFT].margin = CSS_MARGIN_AUTO; - style->margin[RIGHT].margin = CSS_MARGIN_AUTO; - } - xmlFree(s); - } - } + /* No longer need inline style */ + if (inline_style != NULL) + css_stylesheet_destroy(inline_style); - box_solve_display(style, !n->parent); + /* Failed selecting partial style -- bail out */ + if (partial == NULL) + return NULL; - /* Update markup_track with attributes which affect children of - * current box. */ + /* If there's a parent style, compose with partial to obtain + * complete computed style for element */ + if (parent_style != NULL) { + css_error error; - /* Handle html block level element alignment model. - * Note that only margins of block level children are considered, - * text-align for the current block can be handled in the default - * CSS file. - */ - if (strcmp((const char *) n->name, "center") == 0) - markup_track->align = ALIGN_CENTER; - else if (strcmp((const char *) n->name, "div") == 0 || - strcmp((const char *) n->name, "col") == 0 || - strcmp((const char *) n->name, "colgroup") == 0 || - strcmp((const char *) n->name, "tbody") == 0 || - strcmp((const char *) n->name, "td") == 0 || - strcmp((const char *) n->name, "tfoot") == 0 || - strcmp((const char *) n->name, "th") == 0 || - strcmp((const char *) n->name, "thead") == 0 || - strcmp((const char *) n->name, "tr") == 0) { - - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "align"))) { - if (strcasecmp(s, "center") == 0) - markup_track->align = ALIGN_CENTER; - else if (strcasecmp(s, "right") == 0) - markup_track->align = ALIGN_RIGHT; - else if (strcasecmp(s, "left") == 0) - markup_track->align = ALIGN_LEFT; - xmlFree(s); - /* Need to remember if we're in a table, so that any - * alignment rules set on the table's elements won't - * get overridden by the default alignment of a cell - * with no align attribute. At this point, we're in a - * table if the element isn't a div */ - if (strcmp((const char *) n->name, "div") != 0) - markup_track->table = true; + error = css_computed_style_compose(parent_style, partial, + nscss_compute_font_size, NULL, partial); + if (error != CSS_OK) { + css_computed_style_destroy(partial); + return NULL; } - } - /* Table cells without an align value have a default implied - * alignment. */ - if (strcmp((const char *) n->name, "td") == 0 && !markup_track->table) { - if (!(s = (char *) xmlGetProp(n, (const xmlChar *) "align"))) - markup_track->align = ALIGN_LEFT; - else - xmlFree(s); - } - if (strcmp((const char *) n->name, "th") == 0 && !markup_track->table) { - if (!(s = (char *) xmlGetProp(n, (const xmlChar *) "align"))) - markup_track->align = ALIGN_CENTER; - else - xmlFree(s); - } - /* Some of TABLE's attributes apply to the table cells contained - * within the table. Those details are stored so they may be applied - * to the cells when we get to them. */ - if (strcmp((const char *) n->name, "table") == 0) { - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "cellpadding"))) { - char *endp; - long value = strtol(s, &endp, 10); - /* precentage padding width not implemented */ - if (*endp == 0 && 0 <= value && value < 1000) { - markup_track->padding_width = value; - markup_track->cell_padding = true; - } - xmlFree(s); - } - if ((s = (char *) xmlGetProp(n, - (const xmlChar *) "border"))) { - int border_width = atoi(s); - markup_track->border_color = border_color; - /* percentage border width not implemented */ - if (!strrchr(s, '%') && 0 < border_width) { - markup_track->cell_border = true; - } - xmlFree(s); - } + style = partial; + } else { + /* No parent style, so partial must be fully computed */ + style = partial; } return style; } -/** - * Calculate 'display' based on 'display', 'position', and 'float', as given - * by CSS 2.1 9.7. - * - * \param style style to update - * \param root this is the root element - */ - -void box_solve_display(struct css_style *style, bool root) -{ - if (style->display == CSS_DISPLAY_NONE) /* 1. */ - return; - else if (style->position == CSS_POSITION_ABSOLUTE || - style->position == CSS_POSITION_FIXED) /* 2. */ - style->float_ = CSS_FLOAT_NONE; - else if (style->float_ != CSS_FLOAT_NONE) /* 3. */ - ; - else if (root) /* 4. */ - ; - else /* 5. */ - return; - - /* Special case for absolute positioning: make absolute inlines into - * inline block so that the boxes are constructed in an inline container - * as if they were not absolutely positioned. Layout expects and - * handles this. */ - if ((style->position == CSS_POSITION_ABSOLUTE || - style->position == CSS_POSITION_FIXED) && - (style->display == CSS_DISPLAY_INLINE || - style->display == CSS_DISPLAY_INLINE_BLOCK || - style->display == CSS_DISPLAY_INLINE_TABLE)) { - style->display = CSS_DISPLAY_INLINE_BLOCK; - return; - } - - /* map specified value to computed value using table given in 9.7 */ - if (style->display == CSS_DISPLAY_INLINE_TABLE) - style->display = CSS_DISPLAY_TABLE; - else if (style->display == CSS_DISPLAY_LIST_ITEM || - style->display == CSS_DISPLAY_TABLE) - ; /* same as specified */ - else - style->display = CSS_DISPLAY_BLOCK; -} - - /** * Apply the CSS text-transform property to given text for its ASCII chars. * @@ -1360,8 +896,7 @@ void box_solve_display(struct css_style *style, bool root) * \param tt transform type */ -void box_text_transform(char *s, unsigned int len, - css_text_transform tt) +void box_text_transform(char *s, unsigned int len, enum css_text_transform tt) { unsigned int i; if (len == 0) @@ -1416,7 +951,15 @@ void box_text_transform(char *s, unsigned int len, bool box_body(BOX_SPECIAL_PARAMS) { - content->data.html.background_colour = box->style->background_color; + enum css_background_color type; + css_color color; + + type = css_computed_background_color(box->style, &color); + if (type == CSS_BACKGROUND_COLOR_TRANSPARENT) + content->data.html.background_colour = NS_TRANSPARENT; + else + content->data.html.background_colour = nscss_color_to_ns(color); + return true; } @@ -1507,7 +1050,8 @@ bool box_image(BOX_SPECIAL_PARAMS) char *s, *url; xmlChar *alt, *src; - if (box->style && box->style->display == CSS_DISPLAY_NONE) + if (box->style && css_computed_display(box->style, + n->parent == NULL) == CSS_DISPLAY_NONE) return true; /* handle alt text */ @@ -1558,7 +1102,8 @@ bool box_object(BOX_SPECIAL_PARAMS) xmlNode *c; struct box *inline_container = 0; - if (box->style && box->style->display == CSS_DISPLAY_NONE) + if (box->style && css_computed_display(box->style, + n->parent == NULL) == CSS_DISPLAY_NONE) return true; if (!box_get_attribute(n, "usemap", content, &box->usemap)) @@ -1685,8 +1230,7 @@ bool box_object(BOX_SPECIAL_PARAMS) /* convert children and place into fallback */ for (c = n->children; c; c = c->next) { if (!convert_xml_to_box(c, content, box->style, box, - &inline_container, 0, 0, 0, markup_track, - author)) + &inline_container, 0, 0, 0)) return false; } box->fallback = box->children; @@ -1849,7 +1393,7 @@ bool box_frameset(BOX_SPECIAL_PARAMS) if (convert_children) *convert_children = false; /* And ignore this spurious frameset */ - box->style->display = CSS_DISPLAY_NONE; + box->type = BOX_NONE; return true; } @@ -1860,7 +1404,7 @@ bool box_frameset(BOX_SPECIAL_PARAMS) ok = box_create_frameset(content->data.html.frameset, n, content); if (ok) - box->style->display = CSS_DISPLAY_NONE; + box->type = BOX_NONE; if (convert_children) *convert_children = false; @@ -1920,7 +1464,11 @@ bool box_create_frameset(struct content_html_frames *f, xmlNode *n, /* common extension: bordercolor="#RRGGBB|" to control *all children */ if ((s = (char *) xmlGetProp(n, (const xmlChar *) "bordercolor"))) { - parse_inline_colour(s, &default_border_colour); + css_color color; + + if (nscss_parse_colour((const char *) s, &color)) + default_border_colour = nscss_color_to_ns(color); + xmlFree(s); } @@ -2030,7 +1578,13 @@ bool box_create_frameset(struct content_html_frames *f, xmlNode *n, } if ((s = (char *) xmlGetProp(c, (const xmlChar *) "bordercolor"))) { - parse_inline_colour(s, &frame->border_colour); + css_color color; + + if (nscss_parse_colour((const char *) s, + &color)) + frame->border_colour = + nscss_color_to_ns(color); + xmlFree(s); } @@ -2100,7 +1654,11 @@ bool box_iframe(BOX_SPECIAL_PARAMS) xmlFree(s); } if ((s = (char *) xmlGetProp(n, (const xmlChar *) "bordercolor"))) { - parse_inline_colour(s, &iframe->border_colour); + css_color color; + + if (nscss_parse_colour(s, &color)) + iframe->border_colour = nscss_color_to_ns(color); + xmlFree(s); } if ((s = (char *) xmlGetProp(n, @@ -2155,31 +1713,36 @@ bool box_input(BOX_SPECIAL_PARAMS) gadget->box = box; if (type && strcasecmp(type, "password") == 0) { - if (!box_input_text(n, content, box, 0, markup_track, author, - true)) + if (!box_input_text(n, content, box, 0, true)) goto no_memory; } else if (type && strcasecmp(type, "file") == 0) { box->type = BOX_INLINE_BLOCK; } else if (type && strcasecmp(type, "hidden") == 0) { /* no box for hidden inputs */ - box->style->display = CSS_DISPLAY_NONE; + box->type = BOX_NONE; } else if (type && (strcasecmp(type, "checkbox") == 0 || strcasecmp(type, "radio") == 0)) { } else if (type && (strcasecmp(type, "submit") == 0 || strcasecmp(type, "reset") == 0 || strcasecmp(type, "button") == 0)) { struct box *inline_container, *inline_box; - if (!box_button(n, content, box, 0, markup_track, author)) + + if (!box_button(n, content, box, 0)) goto no_memory; + inline_container = box_create(0, 0, 0, 0, 0, content); if (!inline_container) goto no_memory; + inline_container->type = BOX_INLINE_CONTAINER; + inline_box = box_create(box->style, 0, 0, box->title, 0, content); if (!inline_box) goto no_memory; + inline_box->type = BOX_TEXT; + if (box->gadget->value != NULL) inline_box->text = talloc_strdup(content, box->gadget->value); @@ -2191,15 +1754,20 @@ bool box_input(BOX_SPECIAL_PARAMS) messages_get("Form_Reset")); else inline_box->text = talloc_strdup(content, "Button"); + if (!inline_box->text) goto no_memory; + inline_box->length = strlen(inline_box->text); + box_add_child(inline_container, inline_box); + box_add_child(box, inline_container); } else if (type && strcasecmp(type, "image") == 0) { gadget->type = GADGET_IMAGE; - if (box->style && box->style->display != CSS_DISPLAY_NONE) { + if (box->style && css_computed_display(box->style, + n->parent == NULL) != CSS_DISPLAY_NONE) { if ((s = (char *) xmlGetProp(n, (const xmlChar*) "src"))) { res = url_join(s, @@ -2227,8 +1795,7 @@ bool box_input(BOX_SPECIAL_PARAMS) } } else { /* the default type is "text" */ - if (!box_input_text(n, content, box, 0, markup_track, author, - false)) + if (!box_input_text(n, content, box, 0, false)) goto no_memory; } @@ -2582,7 +2149,8 @@ bool box_embed(BOX_SPECIAL_PARAMS) xmlChar *src; xmlAttr *a; - if (box->style && box->style->display == CSS_DISPLAY_NONE) + if (box->style && css_computed_display(box->style, + n->parent == NULL) == CSS_DISPLAY_NONE) return true; params = talloc(content, struct object_params); @@ -2790,18 +2358,39 @@ struct frame_dimension *box_parse_multi_lengths(const char *s, return length; } - /** - * Parse an inline colour string + * Fetch an object from an interned URL + * + * \param c Current content + * \param url URL to fetch + * \param box Box containing object + * \param permitted_types Array of permitted types terminated by + * CONTENT_UNKNOWN, or NULL for all types + * \param available_width Estimate of width of object + * \param available_height Estimate of height of object + * \param background This object forms the box background + * \return true on success, false on memory exhaustion */ -static void parse_inline_colour(char *s, colour *variable) { - colour new_colour = CSS_COLOR_NONE; - if (s[0] == '#') { - if (strlen(s) == 7) - new_colour = hex_colour(s + 1, 6); - } else { - new_colour = named_colour(s); - } - if (new_colour != CSS_COLOR_NONE) - *variable = new_colour; +bool fetch_object_interned_url(struct content *c, lwc_string *url, + struct box *box, const content_type *permitted_types, + int available_width, int available_height, + bool background) +{ + char *url_buf; + bool ret = true; + + url_buf = malloc(lwc_string_length(url) + 1); + if (url_buf == NULL) + return false; + + memcpy(url_buf, lwc_string_data(url), lwc_string_length(url)); + url_buf[lwc_string_length(url)] = '\0'; + + ret = html_fetch_object(c, url_buf, box, permitted_types, + available_width, available_height, background); + + free(url_buf); + + return ret; } + diff --git a/render/box_normalise.c b/render/box_normalise.c index 865433e26..fc563e743 100644 --- a/render/box_normalise.c +++ b/render/box_normalise.c @@ -26,6 +26,7 @@ #include #include #include "css/css.h" +#include "css/select.h" #include "render/box.h" #include "render/table.h" #include "desktop/gui.h" @@ -35,29 +36,34 @@ #include "utils/talloc.h" +/** + * Row spanning information for a cell + */ struct span_info { + /** Number of rows this cell spans */ unsigned int row_span; + /** The cell in this column spans all rows until the end of the table */ bool auto_row; - bool auto_column; }; +/** + * Column record for a table + */ struct columns { + /** Current column index */ unsigned int current_column; - bool extra; - /* Number of columns in main part of table 1..max columns */ + /** Number of columns in main part of table 1..max columns */ unsigned int num_columns; - /* Information about columns in main table, - array 0 to num_columns - 1 */ + /** Information about columns in main table, array [0, num_columns) */ struct span_info *spans; - /* Number of columns that have cells after a colspan 0 */ - unsigned int extra_columns; - /* Number of rows in table */ + /** Number of rows in table */ unsigned int num_rows; }; static bool box_normalise_table(struct box *table, struct content *c); -static void box_normalise_table_spans(struct box *table); +static bool box_normalise_table_spans(struct box *table, + struct span_info *spans, struct content *c); static bool box_normalise_table_row_group(struct box *row_group, struct columns *col_info, struct content *c); @@ -69,6 +75,18 @@ static bool calculate_table_row(struct columns *col_info, unsigned int *start_column); static bool box_normalise_inline_container(struct box *cont, struct content *c); +/** + * Allocator + * + * \param ptr Pointer to reallocate, or NULL for new allocation + * \param size Number of bytes requires + * \param pw Allocation context + * \return Pointer to allocated block, or NULL on failure + */ +static void *myrealloc(void *ptr, size_t len, void *pw) +{ + return talloc_realloc_size(pw, ptr, len); +} /** * Ensure the box tree is correctly nested by adding and removing nodes. @@ -96,30 +114,34 @@ bool box_normalise_block(struct box *block, struct content *c) struct box *child; struct box *next_child; struct box *table; - struct css_style *style; + css_computed_style *style; + + assert(block != NULL); - assert(block != 0); LOG(("block %p, block->type %u", block, block->type)); + assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK || block->type == BOX_TABLE_CELL); + gui_multitask(); - for (child = block->children; child != 0; child = next_child) { + for (child = block->children; child != NULL; child = next_child) { LOG(("child %p, child->type = %d", child, child->type)); + next_child = child->next; /* child may be destroyed */ + switch (child->type) { case BOX_BLOCK: /* ok */ - if (!box_normalise_block(child, c)) + if (box_normalise_block(child, c) == false) return false; break; case BOX_INLINE_CONTAINER: - if (!box_normalise_inline_container(child, - c)) + if (box_normalise_inline_container(child, c) == false) return false; break; case BOX_TABLE: - if (!box_normalise_table(child, c)) + if (box_normalise_table(child, c) == false) return false; break; case BOX_INLINE: @@ -137,37 +159,48 @@ bool box_normalise_block(struct box *block, struct content *c) case BOX_TABLE_ROW: case BOX_TABLE_CELL: /* insert implied table */ - style = talloc_memdup(c, block->style, sizeof *style); - if (!style) + assert(block->style != NULL); + + style = nscss_get_blank_style(c, block->style, + myrealloc, c); + if (style == NULL) return false; - css_cascade(style, &css_blank_style, NULL); + table = box_create(style, block->href, block->target, - 0, 0, c); - if (!table) { - talloc_free(style); + NULL, NULL, c); + if (table == NULL) { + css_computed_style_destroy(style); return false; } table->type = BOX_TABLE; - if (child->prev == 0) + + if (child->prev == NULL) block->children = table; else child->prev->next = table; + table->prev = child->prev; - while (child != 0 && ( + + while (child != NULL && ( child->type == BOX_TABLE_ROW_GROUP || child->type == BOX_TABLE_ROW || child->type == BOX_TABLE_CELL)) { box_add_child(table, child); + next_child = child->next; - child->next = 0; + child->next = NULL; child = next_child; } - table->last->next = 0; + + table->last->next = NULL; table->next = next_child = child; - if (table->next) + if (table->next != NULL) table->next->prev = table; + else + block->last = table; table->parent = block; - if (!box_normalise_table(table, c)) + + if (box_normalise_table(table, c) == false) return false; break; default: @@ -184,30 +217,32 @@ bool box_normalise_table(struct box *table, struct content * c) struct box *child; struct box *next_child; struct box *row_group; - struct css_style *style; + css_computed_style *style; struct columns col_info; - assert(table != 0); + assert(table != NULL); assert(table->type == BOX_TABLE); + LOG(("table %p", table)); + col_info.num_columns = 1; col_info.current_column = 0; col_info.spans = malloc(2 * sizeof *col_info.spans); - if (!col_info.spans) + if (col_info.spans == NULL) return false; + col_info.spans[0].row_span = col_info.spans[1].row_span = 0; - col_info.spans[0].auto_row = col_info.spans[0].auto_column = - col_info.spans[1].auto_row = col_info.spans[1].auto_column = false; - col_info.num_rows = col_info.extra_columns = 0; - col_info.extra = false; + col_info.spans[0].auto_row = false; + col_info.spans[1].auto_row = false; + col_info.num_rows = 0; - for (child = table->children; child != 0; child = next_child) { + for (child = table->children; child != NULL; child = next_child) { next_child = child->next; switch (child->type) { case BOX_TABLE_ROW_GROUP: /* ok */ - if (!box_normalise_table_row_group(child, - &col_info, c)) { + if (box_normalise_table_row_group(child, + &col_info, c) == false) { free(col_info.spans); return false; } @@ -219,46 +254,56 @@ bool box_normalise_table(struct box *table, struct content * c) case BOX_TABLE_CELL: /* insert implied table row group */ assert(table->style != NULL); - style = talloc_memdup(c, table->style, sizeof *style); - if (!style) { + + style = nscss_get_blank_style(c, table->style, + myrealloc, c); + if (style == NULL) { free(col_info.spans); return false; } - css_cascade(style, &css_blank_style, NULL); + row_group = box_create(style, table->href, - table->target, 0, 0, c); - if (!row_group) { + table->target, NULL, NULL, c); + if (row_group == NULL) { + css_computed_style_destroy(style); free(col_info.spans); - talloc_free(style); return false; } + row_group->type = BOX_TABLE_ROW_GROUP; - if (child->prev == 0) + + if (child->prev == NULL) table->children = row_group; else child->prev->next = row_group; + row_group->prev = child->prev; - while (child != 0 && ( + + while (child != NULL && ( child->type == BOX_BLOCK || child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || child->type == BOX_TABLE_ROW || child->type == BOX_TABLE_CELL)) { box_add_child(row_group, child); + next_child = child->next; - child->next = 0; + child->next = NULL; child = next_child; } + assert(row_group->last != NULL); - row_group->last->next = 0; + + row_group->last->next = NULL; row_group->next = next_child = child; - if (row_group->next) + if (row_group->next != NULL) row_group->next->prev = row_group; else table->last = row_group; row_group->parent = table; - if (!box_normalise_table_row_group(row_group, - &col_info, c)) { + + if (box_normalise_table_row_group(row_group, + &col_info, c) == false) { free(col_info.spans); return false; } @@ -282,38 +327,43 @@ bool box_normalise_table(struct box *table, struct content * c) table->columns = col_info.num_columns; table->rows = col_info.num_rows; - free(col_info.spans); - if (table->children == 0) { + if (table->children == NULL) { struct box *row; LOG(("table->children == 0, creating implied row")); assert(table->style != NULL); - style = talloc_memdup(c, table->style, sizeof *style); - if (!style) { + + style = nscss_get_blank_style(c, table->style, myrealloc, c); + if (style == NULL) { + free(col_info.spans); return false; } - css_cascade(style, &css_blank_style, NULL); + row_group = box_create(style, table->href, - table->target, 0, 0, c); - if (!row_group) { - talloc_free(style); + table->target, NULL, NULL, c); + if (row_group == NULL) { + css_computed_style_destroy(style); + free(col_info.spans); return false; } row_group->type = BOX_TABLE_ROW_GROUP; - style = talloc_memdup(c, row_group->style, sizeof *style); - if (!style) { + style = nscss_get_blank_style(c, row_group->style, + myrealloc, c); + if (style == NULL) { box_free(row_group); + free(col_info.spans); return false; } - css_cascade(style, &css_blank_style, NULL); + row = box_create(style, row_group->href, - row_group->target, 0, 0, c); - if (!row) { - talloc_free(style); + row_group->target, NULL, NULL, c); + if (row == NULL) { + css_computed_style_destroy(style); box_free(row_group); + free(col_info.spans); return false; } row->type = BOX_TABLE_ROW; @@ -327,11 +377,15 @@ bool box_normalise_table(struct box *table, struct content * c) table->rows = 1; } - box_normalise_table_spans(table); - if (!table_calculate_column_types(table)) + if (box_normalise_table_spans(table, col_info.spans, c) == false) { + free(col_info.spans); + return false; + } + + free(col_info.spans); + + if (table_calculate_column_types(table) == false) return false; - if (table->style->border_collapse == CSS_BORDER_COLLAPSE_COLLAPSE) - table_collapse_borders(table); LOG(("table %p done", table)); @@ -339,62 +393,141 @@ bool box_normalise_table(struct box *table, struct content * c) } -void box_normalise_table_spans(struct box *table) +/** + * Normalise table cell column/row counts for colspan/rowspan = 0. + * Additionally, generate empty cells. + * + * \param table Table to process + * \param spans Array of length table->columns for use in empty cell detection + * \param c Content containing table + * \return True on success, false on memory exhaustion. + */ + +bool box_normalise_table_spans(struct box *table, struct span_info *spans, + struct content *c) { struct box *table_row_group; struct box *table_row; struct box *table_cell; - unsigned int last_column; - unsigned int max_extra = 0; - bool extra; - bool force = false; unsigned int rows_left = table->rows; + unsigned int col; + + /* Clear span data */ + memset(spans, 0, table->columns * sizeof(struct span_info)); - /* Scan table filling in table the width and height of table cells for - cells with colspan = 0 or rowspan = 0. Ignore the colspan and - rowspan of any cells that that follow an colspan = 0 */ + /* Scan table, filling in width and height of table cells with + * colspan = 0 and rowspan = 0. Also generate empty cells */ for (table_row_group = table->children; table_row_group != NULL; - table_row_group = table_row_group->next) { - for (table_row = table_row_group->children; NULL != table_row; + table_row_group = table_row_group->next) { + for (table_row = table_row_group->children; table_row != NULL; table_row = table_row->next){ - last_column = 0; - extra = false; - for (table_cell = table_row->children; NULL != table_cell; + for (table_cell = table_row->children; + table_cell != NULL; table_cell = table_cell->next) { - /* We hae reached the end of the row, and have passed - a cell with colspan = 0 so ignore col and row spans */ - if (force || extra || (table_cell->start_column + 1 <= - last_column)) { - extra = true; + /* colspan = 0 -> colspan = 1 */ + if (table_cell->columns == 0) table_cell->columns = 1; - table_cell->rows = 1; - if (table_cell->start_column <= max_extra) { - max_extra = table_cell->start_column + 1; + + /* rowspan = 0 -> rowspan = rows_left */ + if (table_cell->rows == 0) + table_cell->rows = rows_left; + + /* Record span information */ + for (col = table_cell->start_column; + col < table_cell->start_column + + table_cell->columns; col++) { + spans[col].row_span = table_cell->rows; + } + } + + /* Reduce span count of each column */ + for (col = 0; col < table->columns; col++) { + if (spans[col].row_span == 0) { + unsigned int start = col; + css_computed_style *style; + struct box *cell, *prev; + + /* If it's already zero, then we need + * to generate an empty cell for the + * gap in the row that spans as many + * columns as remain blank. + */ + assert(table_row->style != NULL); + + /* Find width of gap */ + while (col < table->columns && + spans[col].row_span == + 0) { + col++; } - table_cell->start_column += table->columns; - } else { - /* Fill out the number of columns or the number of rows - if necessary */ - if (0 == table_cell->columns) { - table_cell->columns = table->columns - - table_cell->start_column; - if ((0 == table_cell->start_column) && - (0 == table_cell->rows)) { - force = true; - } + + style = nscss_get_blank_style(c, + table_row->style, + myrealloc, c); + if (style == NULL) + return false; + + cell = box_create(style, + table_row->href, + table_row->target, + NULL, NULL, c); + if (cell == NULL) { + css_computed_style_destroy( + style); + return false; } - assert(0 != table_cell->columns); - if (0 == table_cell->rows) { - table_cell->rows = rows_left; + cell->type = BOX_TABLE_CELL; + + cell->rows = 1; + cell->columns = col - start; + cell->start_column = start; + + /* Find place to insert cell */ + for (prev = table_row->children; + prev != NULL; + prev = prev->next) { + if (prev->start_column + + prev->columns == + start) + break; + if (prev->next == NULL) + break; } - assert(0 != table_cell->rows); - last_column = table_cell->start_column + 1; + + /* Insert it */ + if (prev == NULL) { + if (table_row->children != NULL) + table_row->children-> + prev = cell; + else + table_row->last = cell; + + cell->next = + table_row->children; + table_row->children = cell; + } else { + if (prev->next != NULL) + prev->next->prev = cell; + else + table_row->last = cell; + + cell->next = prev->next; + prev->next = cell; + cell->prev = prev; + } + cell->parent = table_row; + } else { + spans[col].row_span--; } } + + assert(rows_left > 0); + rows_left--; } } - table->columns += max_extra; + + return true; } @@ -405,19 +538,21 @@ bool box_normalise_table_row_group(struct box *row_group, struct box *child; struct box *next_child; struct box *row; - struct css_style *style; + css_computed_style *style; assert(row_group != 0); assert(row_group->type == BOX_TABLE_ROW_GROUP); + LOG(("row_group %p", row_group)); - for (child = row_group->children; child != 0; child = next_child) { + for (child = row_group->children; child != NULL; child = next_child) { next_child = child->next; + switch (child->type) { case BOX_TABLE_ROW: /* ok */ - if (!box_normalise_table_row(child, col_info, - c)) + if (box_normalise_table_row(child, col_info, + c) == false) return false; break; case BOX_BLOCK: @@ -427,44 +562,52 @@ bool box_normalise_table_row_group(struct box *row_group, case BOX_TABLE_CELL: /* insert implied table row */ assert(row_group->style != NULL); - style = talloc_memdup(c, row_group->style, - sizeof *style); - if (!style) + + style = nscss_get_blank_style(c, row_group->style, + myrealloc, c); + if (style == NULL) return false; - css_cascade(style, &css_blank_style, NULL); + row = box_create(style, row_group->href, - row_group->target, 0, 0, c); - if (!row) { - talloc_free(style); + row_group->target, NULL, NULL, c); + if (row == NULL) { + css_computed_style_destroy(style); return false; } row->type = BOX_TABLE_ROW; - if (child->prev == 0) + + if (child->prev == NULL) row_group->children = row; else child->prev->next = row; + row->prev = child->prev; - while (child != 0 && ( + + while (child != NULL && ( child->type == BOX_BLOCK || child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || child->type == BOX_TABLE_ROW_GROUP || child->type == BOX_TABLE_CELL)) { box_add_child(row, child); + next_child = child->next; - child->next = 0; + child->next = NULL; child = next_child; } + assert(row->last != NULL); - row->last->next = 0; + + row->last->next = NULL; row->next = next_child = child; - if (row->next) + if (row->next != NULL) row->next->prev = row; else row_group->last = row; row->parent = row_group; - if (!box_normalise_table_row(row, col_info, - c)) + + if (box_normalise_table_row(row, col_info, + c) == false) return false; break; case BOX_INLINE: @@ -483,24 +626,30 @@ bool box_normalise_table_row_group(struct box *row_group, } } - if (row_group->children == 0) { + if (row_group->children == NULL) { LOG(("row_group->children == 0, inserting implied row")); + assert(row_group->style != NULL); - style = talloc_memdup(c, row_group->style, sizeof *style); - if (!style) { + + style = nscss_get_blank_style(c, row_group->style, + myrealloc, c); + if (style == NULL) { return false; } - css_cascade(style, &css_blank_style, NULL); + row = box_create(style, row_group->href, - row_group->target, 0, 0, c); - if (!row) { - talloc_free(style); + row_group->target, NULL, NULL, c); + if (row == NULL) { + css_computed_style_destroy(style); return false; } row->type = BOX_TABLE_ROW; row->parent = row_group; row_group->children = row_group->last = row; + + /* Keep table's row count in sync */ + col_info->num_rows++; } LOG(("row_group %p done", row_group)); @@ -516,19 +665,20 @@ bool box_normalise_table_row(struct box *row, struct box *child; struct box *next_child; struct box *cell = NULL; - struct css_style *style; + css_computed_style *style; unsigned int i; - assert(row != 0); + assert(row != NULL); assert(row->type == BOX_TABLE_ROW); LOG(("row %p", row)); - for (child = row->children; child != 0; child = next_child) { + for (child = row->children; child != NULL; child = next_child) { next_child = child->next; + switch (child->type) { case BOX_TABLE_CELL: /* ok */ - if (!box_normalise_block(child, c)) + if (box_normalise_block(child, c) == false) return false; cell = child; break; @@ -539,46 +689,51 @@ bool box_normalise_table_row(struct box *row, case BOX_TABLE_ROW: /* insert implied table cell */ assert(row->style != NULL); - style = talloc_memdup(c, row->style, sizeof *style); - if (!style) + + style = nscss_get_blank_style(c, row->style, + myrealloc, c); + if (style == NULL) return false; - css_cascade(style, &css_blank_style, NULL); - if (child->style && (child->style->position == - CSS_POSITION_ABSOLUTE || - child->style->position == - CSS_POSITION_FIXED)) { - style->position = child->style->position; - } - cell = box_create(style, row->href, row->target, 0, 0, - c); - if (!cell) { - talloc_free(style); + + cell = box_create(style, row->href, row->target, + NULL, NULL, c); + if (cell == NULL) { + css_computed_style_destroy(style); return false; } cell->type = BOX_TABLE_CELL; - if (child->prev == 0) + + if (child->prev == NULL) row->children = cell; else child->prev->next = cell; + cell->prev = child->prev; - while (child != 0 && ( + + while (child != NULL && ( child->type == BOX_BLOCK || child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || child->type == BOX_TABLE_ROW_GROUP || child->type == BOX_TABLE_ROW)) { box_add_child(cell, child); + next_child = child->next; - child->next = 0; + child->next = NULL; child = next_child; } + assert(cell->last != NULL); - cell->last->next = 0; + + cell->last->next = NULL; cell->next = next_child = child; - if (cell->next) + if (cell->next != NULL) cell->next->prev = cell; + else + row->last = cell; cell->parent = row; - if (!box_normalise_block(cell, c)) + + if (box_normalise_block(cell, c) == false) return false; break; case BOX_INLINE: @@ -596,42 +751,27 @@ bool box_normalise_table_row(struct box *row, assert(0); } - if (!calculate_table_row(col_info, cell->columns, cell->rows, - &cell->start_column)) + if (calculate_table_row(col_info, cell->columns, cell->rows, + &cell->start_column) == false) return false; } + + /* Update row spanning details for all columns */ for (i = 0; i < col_info->num_columns; i++) { - if ((col_info->spans[i].row_span != 0) && (!col_info->spans[i].auto_row)) { + if (col_info->spans[i].row_span != 0 && + col_info->spans[i].auto_row == false) { + /* This cell spans rows, and is not an auto row. + * Reduce number of rows left to span */ col_info->spans[i].row_span--; - if ((col_info->spans[i].auto_column) && (0 == col_info->spans[i].row_span)) { - col_info->spans[i].auto_column = false; - } } } + + /* Reset current column for next row */ col_info->current_column = 0; - col_info->extra = false; - - /* Removing empty rows causes ill effects for HTML such as: - * - * 12 - * - * as it breaks the colspan value. Additionally, both MSIE and FF - * render in the same manner as NetSurf does with the empty row - * culling commented out. - */ -// if (row->children == 0) { -// LOG(("row->children == 0, removing")); -// if (row->prev == 0) -// row->parent->children = row->next; -// else -// row->prev->next = row->next; -// if (row->next != 0) -// row->next->prev = row->prev; -// box_free(row); -// } else { - col_info->num_rows++; -// } + + /* Increment row counter */ + col_info->num_rows++; LOG(("row %p done", row)); @@ -640,6 +780,13 @@ bool box_normalise_table_row(struct box *row, /** + * Compute the column index at which the current cell begins. + * Additionally, update the column record to reflect row spanning. + * + * \param col_info Column record + * \param col_span Number of columns that current cell spans + * \param row_span Number of rows that current cell spans + * \param start_column Pointer to location to receive column index * \return true on success, false on memory exhaustion */ @@ -647,70 +794,55 @@ bool calculate_table_row(struct columns *col_info, unsigned int col_span, unsigned int row_span, unsigned int *start_column) { - unsigned int cell_start_col; + unsigned int cell_start_col = col_info->current_column; unsigned int cell_end_col; unsigned int i; struct span_info *spans; - if (!col_info->extra) { - /* skip columns with cells spanning from above */ - while ((col_info->spans[col_info->current_column].row_span != 0) && - (!col_info->spans[col_info->current_column].auto_column)) { - col_info->current_column++; - } - if (col_info->spans[col_info->current_column].auto_column) { - col_info->extra = true; - col_info->current_column = 0; - } - } + /* Skip columns with cells spanning from above */ + while (col_info->spans[cell_start_col].row_span != 0) + cell_start_col++; + + /* Update current column with calculated start */ + col_info->current_column = cell_start_col; + + /* If this cell has a colspan of 0, then assume 1. + * No other browser supports colspan=0, anyway. */ + if (col_span == 0) + col_span = 1; + + cell_end_col = cell_start_col + col_span; + + if (col_info->num_columns < cell_end_col) { + /* It appears that this row has more columns than + * the maximum recorded for the table so far. + * Allocate more span records. */ + spans = realloc(col_info->spans, + sizeof *spans * (cell_end_col + 1)); + if (spans == NULL) + return false; - cell_start_col = col_info->current_column; - - /* If the current table cell follows a cell with colspan=0, - ignore both colspan and rowspan just assume it is a standard - size cell */ - if (col_info->extra) { - col_info->current_column++; - col_info->extra_columns = col_info->current_column; - } else { - /* If span to end of table, assume spaning single column - at the moment */ - cell_end_col = cell_start_col + ((0 == col_span) ? 1 : col_span); - - if (col_info->num_columns < cell_end_col) { - spans = realloc(col_info->spans, - sizeof *spans * (cell_end_col + 1)); - if (!spans) - return false; - col_info->spans = spans; - col_info->num_columns = cell_end_col; - - /* Mark new final column as sentinal */ - col_info->spans[cell_end_col].row_span = 0; - col_info->spans[cell_end_col].auto_row = - col_info->spans[cell_end_col].auto_column = - false; - } + col_info->spans = spans; + col_info->num_columns = cell_end_col; - if (0 == col_span) { - col_info->spans[cell_start_col].auto_column = true; - col_info->spans[cell_start_col].row_span = row_span; - col_info->spans[cell_start_col].auto_row = (0 == row_span); - } else { - for (i = cell_start_col; i < cell_end_col; i++) { - col_info->spans[i].row_span = (0 == row_span) ? - 1 : row_span; - col_info->spans[i].auto_row = (0 == row_span); - col_info->spans[i].auto_column = false; - } - } - if (0 == col_span) { - col_info->spans[cell_end_col].auto_column = true; - } - col_info->current_column = cell_end_col; + /* Mark new final column as sentinel */ + col_info->spans[cell_end_col].row_span = 0; + col_info->spans[cell_end_col].auto_row = false; + } + + /* This cell may span multiple columns. If it also wants to span + * multiple rows, temporarily assume it spans 1 row only. This will + * be fixed up in box_normalise_table_spans() */ + for (i = cell_start_col; i < cell_end_col; i++) { + col_info->spans[i].row_span = (row_span == 0) ? 1 : row_span; + col_info->spans[i].auto_row = (row_span == 0); } + /* Update current column with calculated end. */ + col_info->current_column = cell_end_col; + *start_column = cell_start_col; + return true; } @@ -720,11 +852,11 @@ bool box_normalise_inline_container(struct box *cont, struct content * c) struct box *child; struct box *next_child; - assert(cont != 0); + assert(cont != NULL); assert(cont->type == BOX_INLINE_CONTAINER); LOG(("cont %p", cont)); - for (child = cont->children; child != 0; child = next_child) { + for (child = cont->children; child != NULL; child = next_child) { next_child = child->next; switch (child->type) { case BOX_INLINE: @@ -735,37 +867,40 @@ bool box_normalise_inline_container(struct box *cont, struct content * c) break; case BOX_INLINE_BLOCK: /* ok */ - if (!box_normalise_block(child, c)) + if (box_normalise_block(child, c) == false) return false; break; case BOX_FLOAT_LEFT: case BOX_FLOAT_RIGHT: /* ok */ - assert(child->children != 0); + assert(child->children != NULL); + switch (child->children->type) { case BOX_BLOCK: - if (!box_normalise_block( - child->children, - c)) + if (box_normalise_block(child->children, + c) == false) return false; break; case BOX_TABLE: - if (!box_normalise_table( - child->children, - c)) + if (box_normalise_table(child->children, + c) == false) return false; break; default: assert(0); } - if (child->children == 0) { + + if (child->children == NULL) { /* the child has destroyed itself: remove float */ - if (child->prev == 0) + if (child->prev == NULL) child->parent->children = child->next; else child->prev->next = child->next; - if (child->next != 0) + if (child->next != NULL) child->next->prev = child->prev; + else + child->parent->last = child->prev; + box_free(child); } break; diff --git a/render/directory.c b/render/directory.c index 2c4a70dbd..82f24efa2 100644 --- a/render/directory.c +++ b/render/directory.c @@ -40,8 +40,9 @@ static const char header[] = "\n\n\n"; static const char footer[] = "</pre>\n</body>\n</html>\n"; -bool directory_create(struct content *c, const char *params[]) { - if (!html_create(c, params)) +bool directory_create(struct content *c, struct content *parent, + const char *params[]) { + if (!html_create(c, parent, params)) /* html_create() must have broadcast MSG_ERROR already, so we * don't need to. */ return false; diff --git a/render/directory.h b/render/directory.h index 259f51b88..a54d516fb 100644 --- a/render/directory.h +++ b/render/directory.h @@ -29,7 +29,8 @@ #include "content/content_type.h" -bool directory_create(struct content *c, const char *params[]); +bool directory_create(struct content *c, struct content *parent, + const char *params[]); bool directory_convert(struct content *c, int width, int height); void directory_destroy(struct content *c); diff --git a/render/font.c b/render/font.c index 11135d959..4577ff373 100644 --- a/render/font.c +++ b/render/font.c @@ -17,13 +17,15 @@ */ #include "css/css.h" +#include "css/utils.h" +#include "desktop/options.h" #include "render/font.h" static plot_font_generic_family_t plot_font_generic_family( - css_font_family css); -static int plot_font_weight(css_font_weight css); -static plot_font_flags_t plot_font_flags(css_font_style style, - css_font_variant variant); + enum css_font_family css); +static int plot_font_weight(enum css_font_weight css); +static plot_font_flags_t plot_font_flags(enum css_font_style style, + enum css_font_variant variant); /** * Populate a font style using data from a computed CSS style @@ -31,15 +33,31 @@ static plot_font_flags_t plot_font_flags(css_font_style style, * \param css Computed style to consider * \param fstyle Font style to populate */ -void font_plot_style_from_css(const struct css_style *css, +void font_plot_style_from_css(const css_computed_style *css, plot_font_style_t *fstyle) { - fstyle->family = plot_font_generic_family(css->font_family); - fstyle->size = - css_len2pt(&css->font_size.value.length, css) * FONT_SIZE_SCALE; - fstyle->weight = plot_font_weight(css->font_weight); - fstyle->flags = plot_font_flags(css->font_style, css->font_variant); - fstyle->foreground = css->color; + lwc_string **families; + css_fixed length = 0; + css_unit unit = CSS_UNIT_PX; + css_color col; + + fstyle->family = plot_font_generic_family( + css_computed_font_family(css, &families)); + + css_computed_font_size(css, &length, &unit); + fstyle->size = FIXTOINT(FMULI(nscss_len2pt(length, unit), + FONT_SIZE_SCALE)); + + /* Clamp font size to configured minimum */ + if (fstyle->size < (option_font_min_size * FONT_SIZE_SCALE) / 10) + fstyle->size = (option_font_min_size * FONT_SIZE_SCALE) / 10; + + fstyle->weight = plot_font_weight(css_computed_font_weight(css)); + fstyle->flags = plot_font_flags(css_computed_font_style(css), + css_computed_font_variant(css)); + + css_computed_color(css, &col); + fstyle->foreground = nscss_color_to_ns(col); fstyle->background = 0; } @@ -54,7 +72,7 @@ void font_plot_style_from_css(const struct css_style *css, * \return Plot font family */ plot_font_generic_family_t plot_font_generic_family( - css_font_family css) + enum css_font_family css) { plot_font_generic_family_t plot; @@ -86,7 +104,7 @@ plot_font_generic_family_t plot_font_generic_family( * \param css CSS font weight * \return Plot weight */ -int plot_font_weight(css_font_weight css) +int plot_font_weight(enum css_font_weight css) { int weight; @@ -133,8 +151,8 @@ int plot_font_weight(css_font_weight css) * \param variant CSS font variant * \return Computed plot flags */ -plot_font_flags_t plot_font_flags(css_font_style style, - css_font_variant variant) +plot_font_flags_t plot_font_flags(enum css_font_style style, + enum css_font_variant variant) { plot_font_flags_t flags = FONTF_NONE; diff --git a/render/font.h b/render/font.h index 9a80af329..3012abce0 100644 --- a/render/font.h +++ b/render/font.h @@ -53,7 +53,7 @@ struct font_functions extern const struct font_functions nsfont; -void font_plot_style_from_css(const struct css_style *css, +void font_plot_style_from_css(const css_computed_style *css, plot_font_style_t *fstyle); #endif diff --git a/render/html.c b/render/html.c index 1905e8bc3..bad7ff2e2 100644 --- a/render/html.c +++ b/render/html.c @@ -61,7 +61,8 @@ static void html_convert_css_callback(content_msg msg, struct content *css, static bool html_meta_refresh(struct content *c, xmlNode *head); static bool html_head(struct content *c, xmlNode *head); static bool html_find_stylesheets(struct content *c, xmlNode *html); -static bool html_process_style_element(struct content *c, xmlNode *style); +static bool html_process_style_element(struct content *c, unsigned int index, + xmlNode *style); static void html_object_callback(content_msg msg, struct content *object, intptr_t p1, intptr_t p2, union content_msg_data data); static void html_object_done(struct box *box, struct content *object, @@ -92,6 +93,18 @@ static const char empty_document[] = "</body>" "</html>"; +/** + * Allocator + * + * \param ptr Pointer to reallocate, or NULL for new allocation + * \param size Number of bytes requires + * \param pw Allocation context + * \return Pointer to allocated block, or NULL on failure + */ +static void *myrealloc(void *ptr, size_t len, void *pw) +{ + return realloc(ptr, len); +} /** * Create a CONTENT_HTML. @@ -100,15 +113,19 @@ static const char empty_document[] = * created. */ -bool html_create(struct content *c, const char *params[]) +bool html_create(struct content *c, struct content *parent, + const char *params[]) { unsigned int i; struct content_html_data *html = &c->data.html; union content_msg_data msg_data; binding_error error; + lwc_context *dict; + lwc_error lerror; html->parser_binding = NULL; html->document = 0; + html->quirks = BINDING_QUIRKS_MODE_NONE; html->encoding = 0; html->base_url = c->url; html->base_target = NULL; @@ -116,8 +133,7 @@ bool html_create(struct content *c, const char *params[]) html->background_colour = NS_TRANSPARENT; html->stylesheet_count = 0; html->stylesheet_content = 0; - html->style = 0; - html->working_stylesheet = 0; + html->select_ctx = NULL; html->object_count = 0; html->object = 0; html->forms = 0; @@ -130,6 +146,14 @@ bool html_create(struct content *c, const char *params[]) html->box = 0; html->font_func = &nsfont; + lerror = lwc_create_context(myrealloc, c, &dict); + if (lerror != lwc_error_ok) { + error = BINDING_NOMEM; + goto error; + } + + html->dict = lwc_context_ref(dict); + for (i = 0; params[i]; i += 2) { if (strcasecmp(params[i], "charset") == 0) { html->encoding = talloc_strdup(c, params[i + 1]); @@ -343,7 +367,8 @@ bool html_convert(struct content *c, int width, int height) } c->data.html.document = - binding_get_document(c->data.html.parser_binding); + binding_get_document(c->data.html.parser_binding, + &c->data.html.quirks); /*xmlDebugDumpDocument(stderr, c->data.html.document);*/ if (!c->data.html.document) { @@ -467,7 +492,7 @@ bool html_convert(struct content *c, int width, int height) option_min_reflow_period : time_taken * 1.25)); LOG(("Scheduling relayout no sooner than %dcs", c->reformat_time - wallclock())); - /*box_dump(c->data.html.layout->children, 0);*/ + /*box_dump(stderr, c->data.html.layout->children, 0);*/ /* Destroy the parser binding */ binding_destroy_tree(c->data.html.parser_binding); @@ -778,16 +803,17 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) union content_msg_data msg_data; url_func_result res; struct content **stylesheet_content; + css_error error; /* stylesheet 0 is the base style sheet, - * stylesheet 1 is the adblocking stylesheet, - * stylesheet 2 is any <style> elements */ + * stylesheet 1 is the quirks mode style sheet, + * stylesheet 2 is the adblocking stylesheet */ c->data.html.stylesheet_content = talloc_array(c, struct content *, STYLESHEET_START); if (!c->data.html.stylesheet_content) goto no_memory; - c->data.html.stylesheet_content[STYLESHEET_ADBLOCK] = 0; - c->data.html.stylesheet_content[STYLESHEET_STYLE] = 0; + c->data.html.stylesheet_content[STYLESHEET_QUIRKS] = NULL; + c->data.html.stylesheet_content[STYLESHEET_ADBLOCK] = NULL; c->data.html.stylesheet_count = STYLESHEET_START; c->active = 0; @@ -805,6 +831,22 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) STYLESHEET_BASE, c->width, c->height, 0, 0, false, c); + if (c->data.html.quirks == BINDING_QUIRKS_MODE_FULL) { + c->data.html.stylesheet_content[STYLESHEET_QUIRKS] = + fetchcache(quirks_stylesheet_url, + html_convert_css_callback, (intptr_t) c, + STYLESHEET_QUIRKS, c->width, c->height, + true, 0, 0, false, false); + if (c->data.html.stylesheet_content[STYLESHEET_QUIRKS] == NULL) + goto no_memory; + c->active++; + fetchcache_go(c->data.html. + stylesheet_content[STYLESHEET_QUIRKS], + c->url, html_convert_css_callback, + (intptr_t) c, STYLESHEET_QUIRKS, c->width, + c->height, 0, 0, false, c); + } + if (option_block_ads) { c->data.html.stylesheet_content[STYLESHEET_ADBLOCK] = fetchcache(adblock_stylesheet_url, @@ -931,31 +973,14 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) i++; } else if (strcmp((const char *) node->name, "style") == 0) { - if (!html_process_style_element(c, node)) + if (!html_process_style_element(c, i, node)) return false; + i++; } } c->data.html.stylesheet_count = i; - if (c->data.html.stylesheet_content[STYLESHEET_STYLE] != 0) { - if (css_convert(c->data.html. - stylesheet_content[STYLESHEET_STYLE], c->width, - c->height)) { - if (!content_add_user(c->data.html. - stylesheet_content[STYLESHEET_STYLE], - html_convert_css_callback, - (intptr_t) c, STYLESHEET_STYLE)) { - /* no memory */ - c->data.html.stylesheet_content[STYLESHEET_STYLE] = 0; - goto no_memory; - } - } else { - /* conversion failed */ - c->data.html.stylesheet_content[STYLESHEET_STYLE] = 0; - } - } - /* complete the fetches */ while (c->active != 0) { if (c->active != last_active) { @@ -974,29 +999,23 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) return false; } - assert(c->data.html.stylesheet_content[STYLESHEET_BASE]); - css_set_origin(c->data.html.stylesheet_content[STYLESHEET_BASE], - CSS_ORIGIN_UA); - - /* any of our other stylesheet pointers could be NULL at this point if - * the CSS file(s) failed to load/fetch */ - if (c->data.html.stylesheet_content[STYLESHEET_ADBLOCK]) - css_set_origin(c->data.html.stylesheet_content[ - STYLESHEET_ADBLOCK], CSS_ORIGIN_UA); - if (c->data.html.stylesheet_content[STYLESHEET_STYLE]) - css_set_origin(c->data.html.stylesheet_content[ - STYLESHEET_STYLE], CSS_ORIGIN_AUTHOR); - for (i = STYLESHEET_START; i != c->data.html.stylesheet_count; i++) - if (c->data.html.stylesheet_content[i]) - css_set_origin(c->data.html.stylesheet_content[i], - CSS_ORIGIN_AUTHOR); - - c->data.html.working_stylesheet = css_make_working_stylesheet( - c->data.html.stylesheet_content, - c->data.html.stylesheet_count); - if (!c->data.html.working_stylesheet) + /* Create selection context */ + error = css_select_ctx_create(myrealloc, c, &c->data.html.select_ctx); + if (error != CSS_OK) goto no_memory; + /* Add sheets to it */ + for (i = STYLESHEET_BASE; i != c->data.html.stylesheet_count; i++) { + if (c->data.html.stylesheet_content[i]) { + error = css_select_ctx_append_sheet( + c->data.html.select_ctx, + c->data.html.stylesheet_content[i]-> + data.css.sheet); + if (error != CSS_OK) + goto no_memory; + } + } + return true; no_memory: @@ -1010,15 +1029,19 @@ no_memory: * Process an inline stylesheet in the document. * * \param c content structure + * \param index Index of stylesheet in stylesheet_content array * \param style xml node of style element * \return true on success, false if an error occurred */ -bool html_process_style_element(struct content *c, xmlNode *style) +bool html_process_style_element(struct content *c, unsigned int index, + xmlNode *style) { xmlNode *child; char *type, *media, *data; union content_msg_data msg_data; + struct content **stylesheet_content; + const char *params[] = { 0 }; /* type='text/css', or not present (invalid but common) */ if ((type = (char *) xmlGetProp(style, (const xmlChar *) "type"))) { @@ -1039,20 +1062,24 @@ bool html_process_style_element(struct content *c, xmlNode *style) xmlFree(media); } + /* Extend array */ + stylesheet_content = talloc_realloc(c, c->data.html.stylesheet_content, + struct content *, index + 1); + if (stylesheet_content == NULL) + goto no_memory; + + c->data.html.stylesheet_content = stylesheet_content; + /* create stylesheet */ - if (c->data.html.stylesheet_content[STYLESHEET_STYLE] == 0) { - const char *params[] = { 0 }; - c->data.html.stylesheet_content[STYLESHEET_STYLE] = - content_create(c->data.html.base_url); - if (!c->data.html.stylesheet_content[STYLESHEET_STYLE]) - goto no_memory; - if (!content_set_type(c->data.html. - stylesheet_content[STYLESHEET_STYLE], - CONTENT_CSS, "text/css", params)) - /** \todo not necessarily caused by - * memory exhaustion */ - goto no_memory; - } + c->data.html.stylesheet_content[index] = + content_create(c->data.html.base_url); + if (c->data.html.stylesheet_content[index] == NULL) + goto no_memory; + if (!content_set_type(c->data.html.stylesheet_content[index], + CONTENT_CSS, "text/css", params, c)) + /** \todo not necessarily caused by + * memory exhaustion */ + goto no_memory; /* can't just use xmlNodeGetContent(style), because that won't * give the content of comments which may be used to 'hide' @@ -1060,7 +1087,7 @@ bool html_process_style_element(struct content *c, xmlNode *style) for (child = style->children; child != 0; child = child->next) { data = (char *) xmlNodeGetContent(child); if (!content_process_data(c->data.html. - stylesheet_content[STYLESHEET_STYLE], + stylesheet_content[index], data, strlen(data))) { xmlFree(data); /** \todo not necessarily caused by @@ -1070,6 +1097,21 @@ bool html_process_style_element(struct content *c, xmlNode *style) xmlFree(data); } + /* Convert the content */ + if (nscss_convert(c->data.html.stylesheet_content[index], c->width, + c->height)) { + if (!content_add_user(c->data.html.stylesheet_content[index], + html_convert_css_callback, + (intptr_t) c, index)) { + /* no memory */ + c->data.html.stylesheet_content[index] = NULL; + goto no_memory; + } + } else { + /* conversion failed */ + c->data.html.stylesheet_content[index] = NULL; + } + return true; no_memory: @@ -1181,7 +1223,7 @@ void html_convert_css_callback(content_msg msg, struct content *css, * \return true on success, false on memory exhaustion */ -bool html_fetch_object(struct content *c, char *url, struct box *box, +bool html_fetch_object(struct content *c, const char *url, struct box *box, const content_type *permitted_types, int available_width, int available_height, bool background) @@ -1693,10 +1735,10 @@ void html_reformat(struct content *c, int width, int height) /* width and height are at least margin box of document */ c->width = layout->x + layout->padding[LEFT] + layout->width + - layout->padding[RIGHT] + layout->border[RIGHT] + + layout->padding[RIGHT] + layout->border[RIGHT].width + layout->margin[RIGHT]; c->height = layout->y + layout->padding[TOP] + layout->height + - layout->padding[BOTTOM] + layout->border[BOTTOM] + + layout->padding[BOTTOM] + layout->border[BOTTOM].width + layout->margin[BOTTOM]; /* if boxes overflow right or bottom edge, expand to contain it */ @@ -1757,6 +1799,12 @@ void html_destroy(struct content *c) c->data.html.iframe = NULL; } + /* Destroy selection context */ + if (c->data.html.select_ctx) { + css_select_ctx_destroy(c->data.html.select_ctx); + c->data.html.select_ctx = NULL; + } + /* Free stylesheets */ if (c->data.html.stylesheet_count) { for (i = 0; i != c->data.html.stylesheet_count; i++) { @@ -1768,11 +1816,6 @@ void html_destroy(struct content *c) } } - talloc_free(c->data.html.working_stylesheet); - - /*if (c->data.html.style) - css_free_style(c->data.html.style);*/ - /* Free objects */ for (i = 0; i != c->data.html.object_count; i++) { LOG(("object %i %p", i, c->data.html.object[i].content)); @@ -1784,6 +1827,8 @@ void html_destroy(struct content *c) c->data.html.object[i].content); } } + + lwc_context_unref(c->data.html.dict); } void html_destroy_frameset(struct content_html_frames *frameset) { diff --git a/render/html.h b/render/html.h index e6c322e14..b41406659 100644 --- a/render/html.h +++ b/render/html.h @@ -42,12 +42,13 @@ struct plotters; /* entries in stylesheet_content */ #define STYLESHEET_BASE 0 /* base style sheet */ -#define STYLESHEET_ADBLOCK 1 /* adblocking stylesheet */ -#define STYLESHEET_STYLE 2 /* <style> elements (not cached) */ +#define STYLESHEET_QUIRKS 1 /* quirks mode stylesheet */ +#define STYLESHEET_ADBLOCK 2 /* adblocking stylesheet */ #define STYLESHEET_START 3 /* start of document stylesheets */ extern char *default_stylesheet_url; extern char *adblock_stylesheet_url; +extern char *quirks_stylesheet_url; struct frame_dimension { float value; @@ -117,6 +118,9 @@ struct content_html_iframe { struct content_html_data { void *parser_binding; xmlDoc *document; + binding_quirks_mode quirks; /**< Quirkyness of document */ + + lwc_context *dict; /**< Internment context for this document */ char *encoding; /**< Encoding of source, 0 if unknown. */ binding_encoding_source encoding_source; @@ -133,9 +137,8 @@ struct content_html_data { unsigned int stylesheet_count; /** Stylesheets. Each may be 0. */ struct content **stylesheet_content; - struct css_style *style; /**< Base style. */ - /** Working stylesheet. */ - struct css_working_stylesheet *working_stylesheet; + /**< Style selection context */ + css_select_ctx *select_ctx; /** Number of entries in object. */ unsigned int object_count; @@ -168,12 +171,13 @@ struct content_html_data { extern bool html_redraw_debug; -bool html_create(struct content *c, const char *params[]); +bool html_create(struct content *c, struct content *parent, + const char *params[]); bool html_process_data(struct content *c, char *data, unsigned int size); bool html_convert(struct content *c, int width, int height); void html_reformat(struct content *c, int width, int height); void html_destroy(struct content *c); -bool html_fetch_object(struct content *c, char *url, struct box *box, +bool html_fetch_object(struct content *c, const char *url, struct box *box, const content_type *permitted_types, int available_width, int available_height, bool background); diff --git a/render/html_redraw.c b/render/html_redraw.c index 309b81332..735c241a3 100644 --- a/render/html_redraw.c +++ b/render/html_redraw.c @@ -32,6 +32,7 @@ #include "utils/config.h" #include "content/content.h" #include "css/css.h" +#include "css/utils.h" #include "desktop/gui.h" #include "desktop/plotters.h" #include "desktop/knockout.h" @@ -67,7 +68,7 @@ static bool html_redraw_borders(struct box *box, int x_parent, int y_parent, bool html_redraw_inline_borders(struct box *box, int x0, int y0, int x1, int y1, float scale, bool first, bool last); static bool html_redraw_border_plot(int i, int *p, colour c, - css_border_style style, int thickness); + enum css_border_style style, int thickness); static bool html_redraw_checkbox(int x, int y, int width, int height, bool selected); static bool html_redraw_radio(int x, int y, int width, int height, @@ -193,6 +194,7 @@ bool html_redraw_box(struct box *box, int x0, y0, x1, y1; int x_scrolled, y_scrolled; struct box *bg_box = NULL; + css_color bgcol = 0; if (html_redraw_printing && box->printed) return true; @@ -208,10 +210,10 @@ bool html_redraw_box(struct box *box, padding_width = padding_left + box->width + box->padding[RIGHT]; padding_height = padding_top + box->height + box->padding[BOTTOM]; - border_left = box->border[LEFT]; - border_top = box->border[TOP]; - border_right = box->border[RIGHT]; - border_bottom = box->border[BOTTOM]; + border_left = box->border[LEFT].width; + border_top = box->border[TOP].width; + border_right = box->border[RIGHT].width; + border_bottom = box->border[BOTTOM].width; } else { x = (x_parent + box->x) * scale; y = (y_parent + box->y) * scale; @@ -227,14 +229,15 @@ bool html_redraw_box(struct box *box, box->padding[RIGHT]) * scale; padding_height = (box->padding[TOP] + box->height + box->padding[BOTTOM]) * scale; - border_left = box->border[LEFT] * scale; - border_top = box->border[TOP] * scale; - border_right = box->border[RIGHT] * scale; - border_bottom = box->border[BOTTOM] * scale; + border_left = box->border[LEFT].width * scale; + border_top = box->border[TOP].width * scale; + border_right = box->border[RIGHT].width * scale; + border_bottom = box->border[BOTTOM].width * scale; } /* calculate rectangle covering this box and descendants */ - if (box->style && box->style->overflow != CSS_OVERFLOW_VISIBLE) { + if (box->style && css_computed_overflow(box->style) != + CSS_OVERFLOW_VISIBLE) { x0 = x - border_left; y0 = y - border_top; x1 = x + padding_width + border_right; @@ -296,7 +299,8 @@ bool html_redraw_box(struct box *box, } /* if visibility is hidden render children only */ - if (box->style && box->style->visibility == CSS_VISIBILITY_HIDDEN) { + if (box->style && css_computed_visibility(box->style) == + CSS_VISIBILITY_HIDDEN) { if ((plot.group_start) && (!plot.group_start("hidden box"))) return false; if (!html_redraw_box_children(box, x_parent, y_parent, @@ -346,20 +350,21 @@ bool html_redraw_box(struct box *box, */ if (!box->parent) { /* Root box */ - if (box->style && - (box->style->background_color != - NS_TRANSPARENT || + if (box->style && (css_computed_background_color(box->style, + &bgcol) != CSS_BACKGROUND_COLOR_TRANSPARENT || box->background)) { /* With its own background */ bg_box = box; } else if (!box->style || - (box->style->background_color == - NS_TRANSPARENT && + (css_computed_background_color(box->style, + &bgcol) == CSS_BACKGROUND_COLOR_TRANSPARENT && !box->background)) { /* Without its own background */ if (box->children && box->children->style && - (box->children->style-> - background_color != NS_TRANSPARENT || + (css_computed_background_color( + box->children->style, + &bgcol) != + CSS_BACKGROUND_COLOR_TRANSPARENT || box->children->background)) { /* But body has one, so use that */ bg_box = box->children; @@ -367,14 +372,14 @@ bool html_redraw_box(struct box *box, } } else if (box->parent && !box->parent->parent) { /* Body box */ - if (box->style && - (box->style->background_color != - NS_TRANSPARENT || + if (box->style && (css_computed_background_color(box->style, + &bgcol) != CSS_BACKGROUND_COLOR_TRANSPARENT || box->background)) { /* With a background */ if (box->parent->style && - (box->parent->style->background_color != - NS_TRANSPARENT || + (css_computed_background_color( + box->parent->style, &bgcol) != + CSS_BACKGROUND_COLOR_TRANSPARENT || box->parent->background)) { /* Root has own background; process normally */ bg_box = box; @@ -395,8 +400,9 @@ bool html_redraw_box(struct box *box, bg_box->type != BOX_TEXT && bg_box->type != BOX_INLINE_END && (bg_box->type != BOX_INLINE || bg_box->object) && - ((bg_box->style->background_color != NS_TRANSPARENT) || - (bg_box->background))) { + (css_computed_background_color(bg_box->style, + &bgcol) != CSS_BACKGROUND_COLOR_TRANSPARENT || + bg_box->background)) { /* find intersection of clip box and border edge */ int px0, py0, px1, py1; px0 = x - border_left < x0 ? x0 : x - border_left; @@ -451,7 +457,8 @@ bool html_redraw_box(struct box *box, /* backgrounds and borders for non-replaced inlines */ if (box->style && box->type == BOX_INLINE && box->inline_end && - (box->style->background_color != NS_TRANSPARENT || + (css_computed_background_color(box->style, &bgcol) != + CSS_BACKGROUND_COLOR_TRANSPARENT || box->background || border_top || border_right || border_bottom || border_left)) { /* inline backgrounds and borders span other boxes and may @@ -482,15 +489,15 @@ bool html_redraw_box(struct box *box, ib_y = y_parent + ib->y; ib_p_width = ib->padding[LEFT] + ib->width + ib->padding[RIGHT]; - ib_b_left = ib->border[LEFT]; - ib_b_right = ib->border[RIGHT]; + ib_b_left = ib->border[LEFT].width; + ib_b_right = ib->border[RIGHT].width; } else { ib_x = (x_parent + ib->x) * scale; ib_y = (y_parent + ib->y) * scale; ib_p_width = (ib->padding[LEFT] + ib->width + ib->padding[RIGHT]) * scale; - ib_b_left = ib->border[LEFT] * scale; - ib_b_right = ib->border[RIGHT] * scale; + ib_b_left = ib->border[LEFT].width * scale; + ib_b_right = ib->border[RIGHT].width * scale; } if (ib->inline_new_line && ib != box) { @@ -587,7 +594,8 @@ bool html_redraw_box(struct box *box, } /* clip to the padding edge for boxes with overflow hidden or scroll */ - if (box->style && box->style->overflow != CSS_OVERFLOW_VISIBLE) { + if (box->style && css_computed_overflow(box->style) != + CSS_OVERFLOW_VISIBLE) { x0 = x; y0 = y; x1 = x + padding_width; @@ -607,7 +615,7 @@ bool html_redraw_box(struct box *box, /* text decoration */ if (box->type != BOX_TEXT && box->style && - box->style->text_decoration != + css_computed_text_decoration(box->style) != CSS_TEXT_DECORATION_NONE) if (!html_redraw_text_decoration(box, x_parent, y_parent, scale, current_background_color)) @@ -665,8 +673,10 @@ bool html_redraw_box(struct box *box, /* scrollbars */ if (box->style && box->type != BOX_BR && box->type != BOX_TABLE && box->type != BOX_INLINE && - (box->style->overflow == CSS_OVERFLOW_SCROLL || - box->style->overflow == CSS_OVERFLOW_AUTO)) + (css_computed_overflow(box->style) == + CSS_OVERFLOW_SCROLL || + css_computed_overflow(box->style) == + CSS_OVERFLOW_AUTO)) if (!html_redraw_scrollbars(box, scale, x, y, padding_width, padding_height, current_background_color)) @@ -984,10 +994,10 @@ bool html_redraw_caret(struct caret *c, colour current_background_color, bool html_redraw_borders(struct box *box, int x_parent, int y_parent, int p_width, int p_height, float scale) { - int top = box->border[TOP]; - int right = box->border[RIGHT]; - int bottom = box->border[BOTTOM]; - int left = box->border[LEFT]; + int top = box->border[TOP].width; + int right = box->border[RIGHT].width; + int bottom = box->border[BOTTOM].width; + int left = box->border[LEFT].width; int x, y; unsigned int i; int p[20]; @@ -1017,12 +1027,19 @@ bool html_redraw_borders(struct box *box, int x_parent, int y_parent, p[18] = x - left; p[19] = y - top; for (i = 0; i != 4; i++) { - if (box->border[i] == 0) + colour col = 0; + + if (box->border[i].width == 0) continue; - if (!html_redraw_border_plot(i, p, - box->style->border[i].color, - box->style->border[i].style, - box->border[i] * scale)) + + if (box->border[i].color == CSS_BORDER_COLOR_TRANSPARENT) { + col = NS_TRANSPARENT; + } else { + col = nscss_color_to_ns(box->border[i].c); + } + + if (!html_redraw_border_plot(i, p, col, box->border[i].style, + box->border[i].width * scale)) return false; } @@ -1047,10 +1064,11 @@ bool html_redraw_borders(struct box *box, int x_parent, int y_parent, bool html_redraw_inline_borders(struct box *box, int x0, int y0, int x1, int y1, float scale, bool first, bool last) { - int top = box->border[TOP]; - int right = box->border[RIGHT]; - int bottom = box->border[BOTTOM]; - int left = box->border[LEFT]; + int top = box->border[TOP].width; + int right = box->border[RIGHT].width; + int bottom = box->border[BOTTOM].width; + int left = box->border[LEFT].width; + colour col; int p[20]; if (scale != 1.0) { @@ -1074,26 +1092,54 @@ bool html_redraw_inline_borders(struct box *box, int x0, int y0, int x1, int y1, assert(box->style); - if (box->border[LEFT] && first) - if (!html_redraw_border_plot(LEFT, p, - box->style->border[LEFT].color, - box->style->border[LEFT].style, left)) + if (box->border[LEFT].width && first) { + if (box->border[LEFT].color == CSS_BORDER_COLOR_TRANSPARENT) + col = NS_TRANSPARENT; + else + col = nscss_color_to_ns(box->border[LEFT].c); + + if (!html_redraw_border_plot(LEFT, p, col, + box->border[LEFT].style, + left)) return false; - if (box->border[TOP]) - if (!html_redraw_border_plot(TOP, p, - box->style->border[TOP].color, - box->style->border[TOP].style, top)) + } + + if (box->border[TOP].width) { + if (box->border[TOP].color == CSS_BORDER_COLOR_TRANSPARENT) + col = NS_TRANSPARENT; + else + col = nscss_color_to_ns(box->border[TOP].c); + + if (!html_redraw_border_plot(TOP, p, col, + box->border[TOP].style, + top)) return false; - if (box->border[BOTTOM]) - if (!html_redraw_border_plot(BOTTOM, p, - box->style->border[BOTTOM].color, - box->style->border[BOTTOM].style, bottom)) + } + + if (box->border[BOTTOM].width) { + if (box->border[BOTTOM].color == CSS_BORDER_COLOR_TRANSPARENT) + col = NS_TRANSPARENT; + else + col = nscss_color_to_ns(box->border[BOTTOM].c); + + if (!html_redraw_border_plot(BOTTOM, p, col, + box->border[BOTTOM].style, + bottom)) return false; - if (box->border[RIGHT] && last) - if (!html_redraw_border_plot(RIGHT, p, - box->style->border[RIGHT].color, - box->style->border[RIGHT].style, right)) + } + + if (box->border[RIGHT].width && last) { + if (box->border[RIGHT].color == CSS_BORDER_COLOR_TRANSPARENT) + col = NS_TRANSPARENT; + else + col = nscss_color_to_ns(box->border[RIGHT].c); + + if (!html_redraw_border_plot(RIGHT, p, col, + box->border[RIGHT].style, + right)) return false; + } + return true; } @@ -1128,7 +1174,7 @@ static plot_style_t plot_style_fillbdr_dlight = { */ bool html_redraw_border_plot(int i, int *p, colour c, - css_border_style style, int thickness) + enum css_border_style style, int thickness) { int z[8]; unsigned int light = i; @@ -1449,7 +1495,10 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, int px0 = clip_x0, py0 = clip_y0, px1 = clip_x1, py1 = clip_y1; int ox = x, oy = y; int width, height; + css_fixed hpos = 0, vpos = 0; + css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; struct box *parent; + css_color bgcol; plot_style_t pstyle_fill_bg = { .fill_type = PLOT_OP_TYPE_SOLID, .fill_colour = *background_colour, @@ -1479,7 +1528,7 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, box->padding[BOTTOM]; } /* handle background-repeat */ - switch (background->style->background_repeat) { + switch (css_computed_background_repeat(background->style)) { case CSS_BACKGROUND_REPEAT_REPEAT: repeat_x = repeat_y = true; /* optimisation: only plot the colour if @@ -1501,50 +1550,39 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, } /* handle background-position */ - switch (background->style->background_position.horz.pos) { - case CSS_BACKGROUND_POSITION_PERCENT: - x += (width - background->background->width) * - scale * - background->style->background_position. - horz.value.percent / 100; - break; - case CSS_BACKGROUND_POSITION_LENGTH: - x += (int) (css_len2px(&background->style-> - background_position.horz.value.length, - background->style) * scale); - break; - default: - break; + css_computed_background_position(background->style, + &hpos, &hunit, &vpos, &vunit); + if (hunit == CSS_UNIT_PCT) { + x += (width - background->background->width) * + scale * FIXTOFLT(hpos) / 100.; + } else { + x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit, + background->style)) * scale); } - switch (background->style->background_position.vert.pos) { - case CSS_BACKGROUND_POSITION_PERCENT: - y += (height - background->background->height) * - scale * - background->style->background_position. - vert.value.percent / 100; - break; - case CSS_BACKGROUND_POSITION_LENGTH: - y += (int) (css_len2px(&background->style-> - background_position.vert.value.length, - background->style) * scale); - break; - default: - break; + if (vunit == CSS_UNIT_PCT) { + y += (height - background->background->height) * + scale * FIXTOFLT(vpos) / 100.; + } else { + y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, + background->style)) * scale); } } /* special case for table rows as their background needs * to be clipped to all the cells */ if (box->type == BOX_TABLE_ROW) { + css_fixed h = 0, v = 0; + css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX; + for (parent = box->parent; ((parent) && (parent->type != BOX_TABLE)); parent = parent->parent); assert(parent && (parent->style)); - clip_to_children = - (parent->style->border_spacing.horz.value > 0) || - (parent->style->border_spacing.vert.value > 0); + css_computed_border_spacing(parent->style, &h, &hu, &v, &vu); + + clip_to_children = (h > 0) || (v > 0); if (clip_to_children) clip_box = box->children; @@ -1572,8 +1610,9 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, /* <td> attributes override <tr> */ if ((clip_x0 >= clip_x1) || (clip_y0 >= clip_y1) || - (clip_box->style->background_color != - NS_TRANSPARENT) || + (css_computed_background_color( + clip_box->style, &bgcol) != + CSS_BACKGROUND_COLOR_TRANSPARENT) || (clip_box->background && clip_box->background->bitmap && bitmap_get_opaque( @@ -1582,11 +1621,10 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, } /* plot the background colour */ - if (background->style->background_color != NS_TRANSPARENT) { - *background_colour = - background->style->background_color; - pstyle_fill_bg.fill_colour = - background->style->background_color; + if (css_computed_background_color(background->style, &bgcol) != + CSS_BACKGROUND_COLOR_TRANSPARENT) { + *background_colour = nscss_color_to_ns(bgcol); + pstyle_fill_bg.fill_colour = *background_colour; if (plot_colour) if (!plot.rectangle(clip_x0, clip_y0, clip_x1, clip_y1, @@ -1670,6 +1708,9 @@ bool html_redraw_inline_background(int x, int y, struct box *box, float scale, bool repeat_y = false; bool plot_colour = true; bool plot_content; + css_fixed hpos = 0, vpos = 0; + css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; + css_color bgcol; plot_style_t pstyle_fill_bg = { .fill_type = PLOT_OP_TYPE_SOLID, .fill_colour = *background_colour, @@ -1682,7 +1723,7 @@ bool html_redraw_inline_background(int x, int y, struct box *box, float scale, if (plot_content) { /* handle background-repeat */ - switch (box->style->background_repeat) { + switch (css_computed_background_repeat(box->style)) { case CSS_BACKGROUND_REPEAT_REPEAT: repeat_x = repeat_y = true; /* optimisation: only plot the colour if @@ -1704,57 +1745,35 @@ bool html_redraw_inline_background(int x, int y, struct box *box, float scale, } /* handle background-position */ - switch (box->style->background_position.horz.pos) { - case CSS_BACKGROUND_POSITION_PERCENT: - x += (px1 - px0 - - box->background->width * scale) * - box->style->background_position. - horz.value.percent / 100; - - if (!repeat_x && - ((box->style-> - background_position. - horz.value.percent < 2 && - !first) || - (box->style-> - background_position. - horz.value.percent > 98 && - !last))) { - plot_content = false; - } - break; - case CSS_BACKGROUND_POSITION_LENGTH: - x += (int) (css_len2px(&box->style-> - background_position.horz.value.length, - box->style) * scale); - break; - default: - break; + css_computed_background_position(box->style, + &hpos, &hunit, &vpos, &vunit); + if (hunit == CSS_UNIT_PCT) { + x += (px1 - px0 - box->background->width * scale) * + FIXTOFLT(hpos) / 100.; + + if (!repeat_x && ((hpos < 2 && !first) || + (hpos > 98 && !last))){ + plot_content = false; + } + } else { + x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit, + box->style)) * scale); } - switch (box->style->background_position.vert.pos) { - case CSS_BACKGROUND_POSITION_PERCENT: - y += (py1 - py0 - - box->background->height * scale) * - box->style->background_position. - vert.value.percent / 100; - break; - case CSS_BACKGROUND_POSITION_LENGTH: - y += (int) (css_len2px(&box->style-> - background_position.vert.value.length, - box->style) * scale); - break; - default: - break; + if (vunit == CSS_UNIT_PCT) { + y += (py1 - py0 - box->background->height * scale) * + FIXTOFLT(vpos) / 100.; + } else { + y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, + box->style)) * scale); } } /* plot the background colour */ - if (box->style->background_color != NS_TRANSPARENT) { - *background_colour = - box->style->background_color; - pstyle_fill_bg.fill_colour = - box->style->background_color; + if (css_computed_background_color(box->style, &bgcol) != + CSS_BACKGROUND_COLOR_TRANSPARENT) { + *background_colour = nscss_color_to_ns(bgcol); + pstyle_fill_bg.fill_colour = *background_colour; if (plot_colour) if (!plot.rectangle(clip_x0, clip_y0, @@ -1820,36 +1839,40 @@ bool html_redraw_text_decoration(struct box *box, int x_parent, int y_parent, float scale, colour background_colour) { - static const css_text_decoration decoration[] = { + static const enum css_text_decoration decoration[] = { CSS_TEXT_DECORATION_UNDERLINE, CSS_TEXT_DECORATION_OVERLINE, CSS_TEXT_DECORATION_LINE_THROUGH }; static const float line_ratio[] = { 0.9, 0.1, 0.5 }; - int colour; + colour fgcol; unsigned int i; + css_color col; + + css_computed_color(box->style, &col); + fgcol = nscss_color_to_ns(col); /* antialias colour for under/overline */ - if (html_redraw_printing) - colour = box->style->color; - else - colour = blend_colour(background_colour, box->style->color); + if (html_redraw_printing == false) + fgcol = blend_colour(background_colour, fgcol); if (box->type == BOX_INLINE) { if (!box->inline_end) return true; for (i = 0; i != NOF_ELEMENTS(decoration); i++) - if (box->style->text_decoration & decoration[i]) + if (css_computed_text_decoration(box->style) & + decoration[i]) if (!html_redraw_text_decoration_inline(box, x_parent, y_parent, scale, - colour, line_ratio[i])) + fgcol, line_ratio[i])) return false; } else { for (i = 0; i != NOF_ELEMENTS(decoration); i++) - if (box->style->text_decoration & decoration[i]) + if (css_computed_text_decoration(box->style) & + decoration[i]) if (!html_redraw_text_decoration_block(box, x_parent + box->x, y_parent + box->y, scale, - colour, line_ratio[i])) + fgcol, line_ratio[i])) return false; } @@ -2179,9 +2202,9 @@ bool html_redraw_scrollbars(struct box *box, float scale, bool box_vscrollbar_present(const struct box * const box) { - return box->descendant_y0 < -box->border[TOP] || + return box->descendant_y0 < -box->border[TOP].width || box->padding[TOP] + box->height + box->padding[BOTTOM] + - box->border[BOTTOM] < box->descendant_y1; + box->border[BOTTOM].width < box->descendant_y1; } @@ -2194,9 +2217,9 @@ bool box_vscrollbar_present(const struct box * const box) bool box_hscrollbar_present(const struct box * const box) { - return box->descendant_x0 < -box->border[LEFT] || + return box->descendant_x0 < -box->border[LEFT].width || box->padding[LEFT] + box->width + box->padding[RIGHT] + - box->border[RIGHT] < box->descendant_x1; + box->border[RIGHT].width < box->descendant_x1; } diff --git a/render/hubbub_binding.c b/render/hubbub_binding.c index 279dd9933..1d8af25ca 100644 --- a/render/hubbub_binding.c +++ b/render/hubbub_binding.c @@ -41,6 +41,8 @@ typedef struct hubbub_ctx { htmlDocPtr document; bool owns_doc; + binding_quirks_mode quirks; + const char *encoding; binding_encoding_source encoding_source; @@ -147,6 +149,7 @@ binding_error binding_create_tree(void *arena, const char *charset, void **ctx) : ENCODING_SOURCE_DETECTED; c->document = NULL; c->owns_doc = true; + c->quirks = BINDING_QUIRKS_MODE_NONE; c->forms = NULL; error = hubbub_parser_create(charset, true, myrealloc, arena, @@ -239,13 +242,15 @@ const char *binding_get_encoding(void *ctx, binding_encoding_source *source) return c->encoding != NULL ? c->encoding : "Windows-1252"; } -xmlDocPtr binding_get_document(void *ctx) +xmlDocPtr binding_get_document(void *ctx, binding_quirks_mode *quirks) { hubbub_ctx *c = (hubbub_ctx *) ctx; xmlDocPtr doc = c->document; c->owns_doc = false; + *quirks = c->quirks; + return doc; } @@ -744,6 +749,20 @@ hubbub_error add_attributes(void *ctx, void *node, hubbub_error set_quirks_mode(void *ctx, hubbub_quirks_mode mode) { + hubbub_ctx *c = (hubbub_ctx *) ctx; + + switch (mode) { + case HUBBUB_QUIRKS_MODE_NONE: + c->quirks = BINDING_QUIRKS_MODE_NONE; + break; + case HUBBUB_QUIRKS_MODE_LIMITED: + c->quirks = BINDING_QUIRKS_MODE_LIMITED; + break; + case HUBBUB_QUIRKS_MODE_FULL: + c->quirks = BINDING_QUIRKS_MODE_FULL; + break; + } + return HUBBUB_OK; } diff --git a/render/layout.c b/render/layout.c index 4d9adc468..411d6755e 100644 --- a/render/layout.c +++ b/render/layout.c @@ -40,6 +40,7 @@ #include <string.h> #include <math.h> #include "css/css.h" +#include "css/utils.h" #include "content/content.h" #include "desktop/gui.h" #include "desktop/options.h" @@ -47,6 +48,7 @@ #include "render/font.h" #include "render/form.h" #include "render/layout.h" +#include "render/table.h" #define NDEBUG #include "utils/log.h" #undef NDEBUG @@ -69,36 +71,36 @@ static bool layout_apply_minmax_height(struct box *box, struct box *container); static void layout_block_add_scrollbar(struct box *box, int which); static int layout_solve_width(int available_width, int width, int lm, int rm, int max_width, int min_width, - int margin[4], int padding[4], int border[4]); + int margin[4], int padding[4], struct box_border border[4]); static void layout_float_find_dimensions(int available_width, - struct css_style *style, struct box *box); + const css_computed_style *style, struct box *box); static void layout_find_dimensions(int available_width, int viewport_height, - struct box *box, struct css_style *style, + struct box *box, const css_computed_style *style, int *width, int *height, int *max_width, int *min_width, - int margin[4], int padding[4], int border[4]); + int margin[4], int padding[4], struct box_border border[4]); static void layout_tweak_form_dimensions(struct box *box, bool percentage, int available_width, bool setwidth, int *dimension); -static int layout_clear(struct box *fl, css_clear clear); +static int layout_clear(struct box *fl, enum css_clear clear); static void find_sides(struct box *fl, int y0, int y1, int *x0, int *x1, struct box **left, struct box **right); static void layout_minmax_inline_container(struct box *inline_container, const struct font_functions *font_func); -static int line_height(struct css_style *style); +static int line_height(const css_computed_style *style); static bool layout_line(struct box *first, int *width, int *y, int cx, int cy, struct box *cont, bool indent, bool has_text_children, struct content *content, struct box **next_box); static struct box *layout_minmax_line(struct box *first, int *min, int *max, const struct font_functions *font_func); -static int layout_text_indent(struct css_style *style, int width); +static int layout_text_indent(const css_computed_style *style, int width); static bool layout_float(struct box *b, int width, struct content *content); static void place_float_below(struct box *c, int width, int cx, int y, struct box *cont); static bool layout_table(struct box *box, int available_width, struct content *content); static void layout_move_children(struct box *box, int x, int y); -static void calculate_mbp_width(struct css_style *style, unsigned int side, - bool margin, bool border, bool padding, +static void calculate_mbp_width(const css_computed_style *style, + unsigned int side, bool margin, bool border, bool padding, int *fixed, float *frac); static void layout_lists(struct box *box, const struct font_functions *font_func); @@ -137,11 +139,11 @@ bool layout_document(struct content *content, int width, int height) layout_minmax_block(doc, font_func); layout_block_find_dimensions(width, height, 0, 0, doc); - doc->x = doc->margin[LEFT] + doc->border[LEFT]; - doc->y = doc->margin[TOP] + doc->border[TOP]; - width -= doc->margin[LEFT] + doc->border[LEFT] + doc->padding[LEFT] + - doc->padding[RIGHT] + doc->border[RIGHT] + - doc->margin[RIGHT]; + doc->x = doc->margin[LEFT] + doc->border[LEFT].width; + doc->y = doc->margin[TOP] + doc->border[TOP].width; + width -= doc->margin[LEFT] + doc->border[LEFT].width + + doc->padding[LEFT] + doc->padding[RIGHT] + + doc->border[RIGHT].width + doc->margin[RIGHT]; if (width < 0) width = 0; doc->width = width; @@ -150,18 +152,19 @@ bool layout_document(struct content *content, int width, int height) /* make <html> and <body> fill available height */ if (doc->y + doc->padding[TOP] + doc->height + doc->padding[BOTTOM] + - doc->border[BOTTOM] + doc->margin[BOTTOM] < + doc->border[BOTTOM].width + doc->margin[BOTTOM] < height) { doc->height = height - (doc->y + doc->padding[TOP] + - doc->padding[BOTTOM] + doc->border[BOTTOM] + + doc->padding[BOTTOM] + + doc->border[BOTTOM].width + doc->margin[BOTTOM]); if (doc->children) doc->children->height = doc->height - (doc->children->margin[TOP] + - doc->children->border[TOP] + + doc->children->border[TOP].width + doc->children->padding[TOP] + doc->children->padding[BOTTOM] + - doc->children->border[BOTTOM] + + doc->children->border[BOTTOM].width + doc->children->margin[BOTTOM]); } @@ -197,7 +200,8 @@ bool layout_block_context(struct box *block, int viewport_height, int y = 0; int lm, rm; struct box *margin_box; - struct css_length gadget_size; /* Checkbox / radio buttons */ + css_fixed gadget_size; + css_unit gadget_unit; /* Checkbox / radio buttons */ assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK || @@ -267,10 +271,11 @@ bool layout_block_context(struct box *block, int viewport_height, block->gadget->type == GADGET_CHECKBOX)) { /* form checkbox or radio button * if width or height is AUTO, set it to 1em */ - gadget_size.unit = CSS_UNIT_EM; - gadget_size.value = 1; + gadget_unit = CSS_UNIT_EM; + gadget_size = INTTOFIX(1); if (block->height == AUTO) - block->height = css_len2px(&gadget_size, block->style); + block->height = FIXTOINT(nscss_len2px(gadget_size, + gadget_unit, block->style)); } box = margin_box = block->children; @@ -317,8 +322,10 @@ bool layout_block_context(struct box *block, int viewport_height, */ if (box->style && - (box->style->position == CSS_POSITION_ABSOLUTE|| - box->style->position == CSS_POSITION_FIXED)) { + (css_computed_position(box->style) == + CSS_POSITION_ABSOLUTE || + css_computed_position(box->style) == + CSS_POSITION_FIXED)) { box->x = box->parent->padding[LEFT]; /* absolute positioned; this element will establish * its own block context when it gets laid out later, @@ -328,9 +335,10 @@ bool layout_block_context(struct box *block, int viewport_height, /* Clearance. */ y = 0; - if (box->style && box->style->clear != CSS_CLEAR_NONE) + if (box->style && css_computed_clear(box->style) != + CSS_CLEAR_NONE) y = layout_clear(block->float_children, - box->style->clear); + css_computed_clear(box->style)); /* Get top margin */ if (box->style) { @@ -351,7 +359,7 @@ bool layout_block_context(struct box *block, int viewport_height, if (box->type == BOX_BLOCK || box->object) { if (!box->object && box->style && - box->style->overflow != + css_computed_overflow(box->style) != CSS_OVERFLOW_VISIBLE) { /* box establishes new block formatting context * so available width may be diminished due to @@ -381,7 +389,13 @@ bool layout_block_context(struct box *block, int viewport_height, layout_block_add_scrollbar(box, BOTTOM); } } else if (box->type == BOX_TABLE) { - if (box->style->width.width == CSS_WIDTH_AUTO) { + enum css_width wtype; + css_fixed width = 0; + css_unit unit = CSS_UNIT_PX; + + wtype = css_computed_width(box->style, &width, &unit); + + if (wtype == CSS_WIDTH_AUTO) { /* max available width may be diminished due to * floats. */ int x0, x1, top; @@ -412,18 +426,18 @@ bool layout_block_context(struct box *block, int viewport_height, /* Position box: horizontal. */ box->x = box->parent->padding[LEFT] + box->margin[LEFT] + - box->border[LEFT]; + box->border[LEFT].width; cx += box->x; /* Position box: vertical. */ if (box->type != BOX_BLOCK || y || - box->border[TOP] || box->padding[TOP]) { + box->border[TOP].width || box->padding[TOP]) { margin_box->y += max_pos_margin - max_neg_margin; cy += max_pos_margin - max_neg_margin; max_pos_margin = max_neg_margin = 0; margin_box = 0; - box->y += box->border[TOP]; - cy += box->border[TOP]; + box->y += box->border[TOP].width; + cy += box->border[TOP].width; if (cy < y) { box->y += y - cy; cy = y; @@ -433,7 +447,8 @@ bool layout_block_context(struct box *block, int viewport_height, /* Unless the box has an overflow style of visible, the box * establishes a new block context. */ if (box->type == BOX_BLOCK && box->style && - box->style->overflow != CSS_OVERFLOW_VISIBLE) { + css_computed_overflow(box->style) != + CSS_OVERFLOW_VISIBLE) { cy += max_pos_margin - max_neg_margin; box->y += max_pos_margin - max_neg_margin; @@ -449,7 +464,7 @@ bool layout_block_context(struct box *block, int viewport_height, cx -= box->x; cy += box->height + box->padding[BOTTOM] + - box->border[BOTTOM]; + box->border[BOTTOM].width; max_pos_margin = max_neg_margin = 0; if (max_pos_margin < box->margin[BOTTOM]) max_pos_margin = box->margin[BOTTOM]; @@ -457,7 +472,7 @@ bool layout_block_context(struct box *block, int viewport_height, max_neg_margin = -box->margin[BOTTOM]; y = box->y + box->padding[TOP] + box->height + box->padding[BOTTOM] + - box->border[BOTTOM]; + box->border[BOTTOM].width; /* Skip children, because they are done in the new * block context */ goto advance_to_next_box; @@ -480,12 +495,19 @@ bool layout_block_context(struct box *block, int viewport_height, struct box *left, *right; y = cy; while (1) { + enum css_width wtype; + css_fixed width = 0; + css_unit unit = CSS_UNIT_PX; + + wtype = css_computed_width(box->style, + &width, &unit); + x0 = cx; x1 = cx + box->parent->width; find_sides(block->float_children, y, y + box->height, &x0, &x1, &left, &right); - if (box->style->width.width == CSS_WIDTH_AUTO) + if (wtype == CSS_WIDTH_AUTO) break; if (box->width <= x1 - x0) break; @@ -527,7 +549,8 @@ bool layout_block_context(struct box *block, int viewport_height, layout_block_add_scrollbar(box, BOTTOM); } - cy += box->height + box->padding[BOTTOM] + box->border[BOTTOM]; + cy += box->height + box->padding[BOTTOM] + + box->border[BOTTOM].width; max_pos_margin = max_neg_margin = 0; if (max_pos_margin < box->margin[BOTTOM]) max_pos_margin = box->margin[BOTTOM]; @@ -535,7 +558,8 @@ bool layout_block_context(struct box *block, int viewport_height, max_neg_margin = -box->margin[BOTTOM]; cx -= box->x; y = box->y + box->padding[TOP] + box->height + - box->padding[BOTTOM] + box->border[BOTTOM]; + box->padding[BOTTOM] + + box->border[BOTTOM].width; advance_to_next_box: if (!box->next) { /* No more siblings: @@ -556,7 +580,8 @@ bool layout_block_context(struct box *block, int viewport_height, /* Apply any min-height and max-height to * boxes in normal flow */ - if (box->style && box->style->position != + if (box->style && + css_computed_position(box->style) != CSS_POSITION_ABSOLUTE && layout_apply_minmax_height(box, NULL)) { @@ -567,7 +592,7 @@ bool layout_block_context(struct box *block, int viewport_height, } cy += box->padding[BOTTOM] + - box->border[BOTTOM]; + box->border[BOTTOM].width; if (max_pos_margin < box->margin[BOTTOM]) max_pos_margin = box->margin[BOTTOM]; else if (max_neg_margin < -box->margin[BOTTOM]) @@ -575,7 +600,7 @@ bool layout_block_context(struct box *block, int viewport_height, cx -= box->x; y = box->y + box->padding[TOP] + box->height + box->padding[BOTTOM] + - box->border[BOTTOM]; + box->border[BOTTOM].width; } while (box != block && !box->next); if (box == block) break; @@ -589,7 +614,7 @@ bool layout_block_context(struct box *block, int viewport_height, /* Increase height to contain any floats inside (CSS 2.1 10.6.7). */ for (box = block->float_children; box; box = box->next_float) { y = box->y + box->height + box->padding[BOTTOM] + - box->border[BOTTOM] + box->margin[BOTTOM]; + box->border[BOTTOM].width + box->margin[BOTTOM]; if (cy < y) cy = y; } @@ -600,7 +625,8 @@ bool layout_block_context(struct box *block, int viewport_height, layout_block_add_scrollbar(block, BOTTOM); } - if (block->style && block->style->position != CSS_POSITION_ABSOLUTE) { + if (block->style && css_computed_position(block->style) != + CSS_POSITION_ABSOLUTE) { /* Block is in normal flow */ layout_apply_minmax_height(block, NULL); } @@ -624,12 +650,9 @@ void layout_minmax_block(struct box *block, int min = 0, max = 0; int extra_fixed = 0; float extra_frac = 0; - struct css_length size; - struct css_length gadget_size; /* Checkbox / radio buttons */ - size.unit = CSS_UNIT_EM; - size.value = 10; - gadget_size.unit = CSS_UNIT_EM; - gadget_size.value = 1; + enum css_width wtype; + css_fixed width = 0; + css_unit wunit = CSS_UNIT_PX; assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK || @@ -639,22 +662,28 @@ void layout_minmax_block(struct box *block, if (block->max_width != UNKNOWN_MAX_WIDTH) return; + wtype = css_computed_width(block->style, &width, &wunit); + if (block->gadget && (block->gadget->type == GADGET_TEXTBOX || block->gadget->type == GADGET_PASSWORD || block->gadget->type == GADGET_FILE || block->gadget->type == GADGET_TEXTAREA) && - block->style && - block->style->width.width == CSS_WIDTH_AUTO) { - min = max = css_len2px(&size, block->style); + block->style && wtype == CSS_WIDTH_AUTO) { + css_fixed size = INTTOFIX(10); + css_unit unit = CSS_UNIT_EM; + + min = max = FIXTOINT(nscss_len2px(size, unit, block->style)); } if (block->gadget && (block->gadget->type == GADGET_RADIO || block->gadget->type == GADGET_CHECKBOX) && - block->style && - block->style->width.width == CSS_WIDTH_AUTO) { + block->style && wtype == CSS_WIDTH_AUTO) { + css_fixed size = INTTOFIX(1); + css_unit unit = CSS_UNIT_EM; + /* form checkbox or radio button * if width is AUTO, set it to 1em */ - min = max = css_len2px(&gadget_size, block->style); + min = max = FIXTOINT(nscss_len2px(size, unit, block->style)); } if (block->object) { @@ -686,9 +715,9 @@ void layout_minmax_block(struct box *block, assert(child->max_width != UNKNOWN_MAX_WIDTH); if (child->style && - (child->style->position == + (css_computed_position(child->style) == CSS_POSITION_ABSOLUTE || - child->style->position == + css_computed_position(child->style) == CSS_POSITION_FIXED)) { /* This child is positioned out of normal flow, * so it will have no affect on width */ @@ -708,18 +737,16 @@ void layout_minmax_block(struct box *block, } /* fixed width takes priority */ - if (block->type != BOX_TABLE_CELL && - block->style->width.width == CSS_WIDTH_LENGTH) { - min = max = css_len2px(&block->style->width.value.length, - block->style); + if (block->type != BOX_TABLE_CELL && wtype == CSS_WIDTH_SET && + wunit != CSS_UNIT_PCT) { + min = max = FIXTOINT(nscss_len2px(width, wunit, block->style)); } /* add margins, border, padding to min, max widths */ - if (block->gadget && (block->style->width.width == CSS_WIDTH_PERCENT || - (block->style->width.width == CSS_WIDTH_LENGTH && + if (block->gadget && wtype == CSS_WIDTH_SET && (block->gadget->type == GADGET_SUBMIT || block->gadget->type == GADGET_RESET || - block->gadget->type == GADGET_BUTTON)))) { + block->gadget->type == GADGET_BUTTON)) { /* some gadgets with specified width already include border and * padding, so just get margin */ calculate_mbp_width(block->style, LEFT, true, false, false, @@ -799,8 +826,8 @@ void layout_block_find_dimensions(int available_width, int viewport_height, int height; int *margin = box->margin; int *padding = box->padding; - int *border = box->border; - struct css_style *style = box->style; + struct box_border *border = box->border; + const css_computed_style *style = box->style; layout_find_dimensions(available_width, viewport_height, box, style, &width, &height, &max_width, &min_width, @@ -854,13 +881,13 @@ bool layout_apply_minmax_height(struct box *box, struct box *container) bool updated = false; /* Find containing block for percentage heights */ - if (box->style->position == CSS_POSITION_ABSOLUTE) { + if (css_computed_position(box->style) == CSS_POSITION_ABSOLUTE) { /* Box is absolutely positioned */ assert(container); containing_block = container; } else if (box->float_container && - (box->style->float_ == CSS_FLOAT_LEFT || - box->style->float_ == CSS_FLOAT_RIGHT)) { + (css_computed_float(box->style) == CSS_FLOAT_LEFT || + css_computed_float(box->style) == CSS_FLOAT_RIGHT)) { /* Box is a float */ assert(box->parent && box->parent->parent && box->parent->parent->parent); @@ -875,72 +902,73 @@ bool layout_apply_minmax_height(struct box *box, struct box *container) } if (box->style) { + enum css_height htype = CSS_HEIGHT_AUTO; + css_fixed length = 0; + css_unit unit = CSS_UNIT_PX; + + if (containing_block) { + htype = css_computed_height(containing_block->style, + &length, &unit); + } + /* max-height */ - switch (box->style->max_height.max_height) { - case CSS_MAX_HEIGHT_LENGTH: - h = css_len2px(&box->style->max_height.value.length, - box->style); - if (h < box->height) { - box->height = h; - updated = true; - } - break; - case CSS_MAX_HEIGHT_PERCENT: - if (containing_block && + if (css_computed_max_height(box->style, &length, &unit) == + CSS_MAX_HEIGHT_SET) { + if (unit == CSS_UNIT_PCT) { + if (containing_block && containing_block->height != AUTO && - (box->style->position == + (css_computed_position(box->style) == CSS_POSITION_ABSOLUTE || - (containing_block->style->height. - height == CSS_HEIGHT_LENGTH || - containing_block->style->height. - height == CSS_HEIGHT_PERCENT))) { - /* Box is absolutely positioned or its - * containing block has a valid specified - * height. (CSS 2.1 Section 10.5) */ - h = box->style->max_height.value.percent * + htype == CSS_HEIGHT_SET)) { + /* Box is absolutely positioned or its + * containing block has a valid + * specified height. (CSS 2.1 + * Section 10.5) */ + h = FIXTOFLT(length) * containing_block->height / 100; + if (h < box->height) { + box->height = h; + updated = true; + } + } + } else { + h = FIXTOINT(nscss_len2px(length, unit, + box->style)); if (h < box->height) { box->height = h; updated = true; } } - break; - default: - break; } /* min-height */ - switch (box->style->min_height.min_height) { - case CSS_MIN_HEIGHT_LENGTH: - h = css_len2px(&box->style->min_height.value.length, - box->style); - if (h > box->height) { - box->height = h; - updated = true; - } - break; - case CSS_MIN_HEIGHT_PERCENT: - if (containing_block && + if (css_computed_min_height(box->style, &length, &unit) == + CSS_MIN_HEIGHT_SET) { + if (unit == CSS_UNIT_PCT) { + if (containing_block && containing_block->height != AUTO && - (box->style->position == + (css_computed_position(box->style) == CSS_POSITION_ABSOLUTE || - (containing_block->style->height. - height == CSS_HEIGHT_LENGTH || - containing_block->style->height. - height == CSS_HEIGHT_PERCENT))) { - /* Box is absolutely positioned or its - * containing block has a valid specified - * height. (CSS 2.1 Section 10.5) */ - h = box->style->min_height.value.percent * + htype == CSS_HEIGHT_SET)) { + /* Box is absolutely positioned or its + * containing block has a valid + * specified height. (CSS 2.1 + * Section 10.5) */ + h = FIXTOFLT(length) * containing_block->height / 100; + if (h > box->height) { + box->height = h; + updated = true; + } + } + } else { + h = FIXTOINT(nscss_len2px(length, unit, + box->style)); if (h > box->height) { box->height = h; updated = true; } } - break; - default: - break; } } return updated; @@ -955,18 +983,24 @@ bool layout_apply_minmax_height(struct box *box, struct box *container) void layout_block_add_scrollbar(struct box *box, int which) { + enum css_overflow overflow; + assert(box->type == BOX_BLOCK && (which == RIGHT || which == BOTTOM)); - if (box->style && (box->style->overflow == CSS_OVERFLOW_SCROLL || - box->style->overflow == CSS_OVERFLOW_AUTO)) { + if (box->style == NULL) + return; + + overflow = css_computed_overflow(box->style); + + if (overflow == CSS_OVERFLOW_SCROLL || overflow == CSS_OVERFLOW_AUTO) { /* make space for scrollbars, unless height/width are AUTO */ if (which == BOTTOM && box->height != AUTO && - (box->style->overflow == CSS_OVERFLOW_SCROLL || + (overflow == CSS_OVERFLOW_SCROLL || box_hscrollbar_present(box))) { box->padding[BOTTOM] += SCROLLBAR_WIDTH; } if (which == RIGHT && box->width != AUTO && - (box->style->overflow == CSS_OVERFLOW_SCROLL || + (overflow == CSS_OVERFLOW_SCROLL || box_vscrollbar_present(box))) { box->width -= SCROLLBAR_WIDTH; box->padding[RIGHT] += SCROLLBAR_WIDTH; @@ -987,16 +1021,14 @@ void layout_block_add_scrollbar(struct box *box, int which) * \param min_width Box min-width ( <=0 means no min-width to apply) * \param margin[4] Current box margins. Updated with new box * left / right margins - * \param padding[4] Current box paddings. Updated with new box - * left / right paddings - * \param border[4] Current box border widths. Updated with new - * box left / right border widths + * \param padding[4] Current box paddings. + * \param border[4] Current box border widths. * \return New box width */ int layout_solve_width(int available_width, int width, int lm, int rm, int max_width, int min_width, - int margin[4], int padding[4], int border[4]) + int margin[4], int padding[4], struct box_border border[4]) { bool auto_width = false; @@ -1013,8 +1045,9 @@ int layout_solve_width(int available_width, int width, int lm, int rm, if (margin[RIGHT] == AUTO) margin[RIGHT] = rm; width = available_width - - (margin[LEFT] + border[LEFT] + padding[LEFT] + - padding[RIGHT] + border[RIGHT] + margin[RIGHT]); + (margin[LEFT] + border[LEFT].width + + padding[LEFT] + padding[RIGHT] + + border[RIGHT].width + margin[RIGHT]); width = width < 0 ? 0 : width; auto_width = true; } @@ -1032,8 +1065,8 @@ int layout_solve_width(int available_width, int width, int lm, int rm, if (!auto_width && margin[LEFT] == AUTO && margin[RIGHT] == AUTO) { /* make the margins equal, centering the element */ margin[LEFT] = margin[RIGHT] = (available_width - lm - rm - - (border[LEFT] + padding[LEFT] + width + - padding[RIGHT] + border[RIGHT])) / 2; + (border[LEFT].width + padding[LEFT] + width + + padding[RIGHT] + border[RIGHT].width)) / 2; if (margin[LEFT] < 0) { margin[RIGHT] += margin[LEFT]; @@ -1044,14 +1077,16 @@ int layout_solve_width(int available_width, int width, int lm, int rm, } else if (!auto_width && margin[LEFT] == AUTO) { margin[LEFT] = available_width - lm - - (border[LEFT] + padding[LEFT] + width + - padding[RIGHT] + border[RIGHT] + margin[RIGHT]); + (border[LEFT].width + padding[LEFT] + width + + padding[RIGHT] + border[RIGHT].width + + margin[RIGHT]); margin[LEFT] = margin[LEFT] < lm ? lm : margin[LEFT]; } else if (!auto_width) { /* margin-right auto or "over-constrained" */ margin[RIGHT] = available_width - rm - - (margin[LEFT] + border[LEFT] + padding[LEFT] + - width + padding[RIGHT] + border[RIGHT]); + (margin[LEFT] + border[LEFT].width + + padding[LEFT] + width + padding[RIGHT] + + border[RIGHT].width); } return width; @@ -1070,14 +1105,15 @@ int layout_solve_width(int available_width, int width, int lm, int rm, */ void layout_float_find_dimensions(int available_width, - struct css_style *style, struct box *box) + const css_computed_style *style, struct box *box) { int width, height, max_width, min_width; int *margin = box->margin; int *padding = box->padding; - int *border = box->border; - int scrollbar_width = (style->overflow == CSS_OVERFLOW_SCROLL || - style->overflow == CSS_OVERFLOW_AUTO) ? + struct box_border *border = box->border; + int scrollbar_width = + (css_computed_overflow(style) == CSS_OVERFLOW_SCROLL || + css_computed_overflow(style) == CSS_OVERFLOW_AUTO) ? SCROLLBAR_WIDTH : 0; layout_find_dimensions(available_width, -1, box, style, &width, &height, @@ -1107,44 +1143,49 @@ void layout_float_find_dimensions(int available_width, box->gadget->type == GADGET_PASSWORD || box->gadget->type == GADGET_FILE || box->gadget->type == GADGET_TEXTAREA)) { - struct css_length size; + css_fixed size = 0; + css_unit unit = CSS_UNIT_EM; + /* Give sensible dimensions to gadgets, with auto width/height, * that don't shrink to fit contained text. */ assert(box->style); - size.unit = CSS_UNIT_EM; if (box->gadget->type == GADGET_TEXTBOX || box->gadget->type == GADGET_PASSWORD || box->gadget->type == GADGET_FILE) { if (width == AUTO) { - size.value = 10; - width = css_len2px(&size, box->style); + size = INTTOFIX(10); + width = FIXTOINT(nscss_len2px(size, unit, + box->style)); } if (box->gadget->type == GADGET_FILE && height == AUTO) { - size.value = 1.5; - height = css_len2px(&size, box->style); + size = FLTTOFIX(1.5); + height = FIXTOINT(nscss_len2px(size, unit, + box->style)); } } if (box->gadget->type == GADGET_TEXTAREA) { if (width == AUTO) { - size.value = 10; - width = css_len2px(&size, box->style); + size = INTTOFIX(10); + width = FIXTOINT(nscss_len2px(size, unit, + box->style)); } else { width -= scrollbar_width; } if (height == AUTO) { - size.value = 4; - height = css_len2px(&size, box->style); + size = INTTOFIX(4); + height = FIXTOINT(nscss_len2px(size, unit, + box->style)); } } } else if (width == AUTO) { /* CSS 2.1 section 10.3.5 */ width = min(max(box->min_width, available_width), box->max_width); - width -= box->margin[LEFT] + box->border[LEFT] + + width -= box->margin[LEFT] + box->border[LEFT].width + box->padding[LEFT] + box->padding[RIGHT] + - box->border[RIGHT] + box->margin[RIGHT]; + box->border[RIGHT].width + box->margin[RIGHT]; if (max_width >= 0 && width > max_width) width = max_width; if (min_width > 0 && width < min_width) width = min_width; @@ -1183,33 +1224,37 @@ void layout_float_find_dimensions(int available_width, */ void layout_find_dimensions(int available_width, int viewport_height, - struct box *box, struct css_style *style, + struct box *box, const css_computed_style *style, int *width, int *height, int *max_width, int *min_width, - int margin[4], int padding[4], int border[4]) + int margin[4], int padding[4], struct box_border border[4]) { struct box *containing_block = NULL; unsigned int i; bool percentage; if (width) { - switch (style->width.width) { - case CSS_WIDTH_LENGTH: - *width = css_len2px(&style->width.value.length, style); - break; - case CSS_WIDTH_PERCENT: - *width = (style->width.value.percent * - available_width) / 100; - break; - case CSS_WIDTH_AUTO: - default: + enum css_width wtype; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + wtype = css_computed_width(style, &value, &unit); + + if (wtype == CSS_WIDTH_SET) { + if (unit == CSS_UNIT_PCT) { + *width = (FIXTOFLT(value) * available_width) + / 100; + } else { + *width = FIXTOINT(nscss_len2px(value, unit, + style)); + } + } else { *width = AUTO; - break; } /* specified gadget widths include borders and padding in some * cases */ if (box->gadget && *width != AUTO) { - percentage = style->width.width == CSS_WIDTH_PERCENT; + percentage = unit == CSS_UNIT_PCT; layout_tweak_form_dimensions(box, percentage, available_width, true, width); @@ -1217,72 +1262,91 @@ void layout_find_dimensions(int available_width, int viewport_height, } if (height) { - switch (style->height.height) { - case CSS_HEIGHT_LENGTH: - *height = css_len2px(&style->height.value.length, - style); - break; - case CSS_HEIGHT_PERCENT: - if (box->style->position == CSS_POSITION_ABSOLUTE) { - /* Box is absolutely positioned */ - assert(box->float_container); - containing_block = box->float_container; - } else if (box->float_container && - box->style->position != - CSS_POSITION_ABSOLUTE && - (box->style->float_ == - CSS_FLOAT_LEFT || - box->style->float_ == - CSS_FLOAT_RIGHT)) { - /* Box is a float */ - assert(box->parent && box->parent->parent && + enum css_height htype; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + htype = css_computed_height(style, &value, &unit); + + if (htype == CSS_HEIGHT_SET) { + if (unit == CSS_UNIT_PCT) { + enum css_height cbhtype; + + if (css_computed_position(box->style) == + CSS_POSITION_ABSOLUTE) { + /* Box is absolutely positioned */ + assert(box->float_container); + containing_block = box->float_container; + } else if (box->float_container && + css_computed_position(box->style) != + CSS_POSITION_ABSOLUTE && + (css_computed_float(box->style) == + CSS_FLOAT_LEFT || + css_computed_float(box->style) == + CSS_FLOAT_RIGHT)) { + /* Box is a float */ + assert(box->parent && + box->parent->parent && box->parent->parent->parent); - containing_block = box->parent->parent->parent; - } else if (box->parent && box->parent->type != - BOX_INLINE_CONTAINER) { - /* Box is a block level element */ - containing_block = box->parent; - } else if (box->parent && box->parent->type == - BOX_INLINE_CONTAINER) { - /* Box is an inline block */ - assert(box->parent->parent); - containing_block = box->parent->parent; - } - if (containing_block && + + containing_block = + box->parent->parent->parent; + } else if (box->parent && box->parent->type != + BOX_INLINE_CONTAINER) { + /* Box is a block level element */ + containing_block = box->parent; + } else if (box->parent && box->parent->type == + BOX_INLINE_CONTAINER) { + /* Box is an inline block */ + assert(box->parent->parent); + containing_block = box->parent->parent; + } + + if (containing_block) { + css_fixed f = 0; + css_unit u = CSS_UNIT_PX; + + cbhtype = css_computed_height( + containing_block->style, + &f, &u); + } + + if (containing_block && containing_block->height != AUTO && - (box->style->position == + (css_computed_position(box->style) == CSS_POSITION_ABSOLUTE || - (containing_block->style->height. - height == CSS_HEIGHT_LENGTH || - containing_block->style->height. - height == CSS_HEIGHT_PERCENT))) { - /* Box is absolutely positioned or its - * containing block has a valid specified - * height. (CSS 2.1 Section 10.5) */ - *height = style->height.value.percent * - containing_block->height / 100; - } else if ((!box->parent || !box->parent->parent) && - viewport_height >= 0) { - /* If root element or it's child - * (HTML or BODY) */ - *height = style->height.value.percent * - viewport_height / 100; + cbhtype == CSS_HEIGHT_SET)) { + /* Box is absolutely positioned or its + * containing block has a valid + * specified height. + * (CSS 2.1 Section 10.5) */ + *height = FIXTOFLT(value) * + containing_block->height / + 100; + } else if ((!box->parent || + !box->parent->parent) && + viewport_height >= 0) { + /* If root element or it's child + * (HTML or BODY) */ + *height = FIXTOFLT(value) * + viewport_height / 100; + } else { + /* precentage height not permissible + * treat height as auto */ + *height = AUTO; + } } else { - /* precentage height not permissible - * treat height as auto */ - *height = AUTO; + *height = FIXTOINT(nscss_len2px(value, unit, + style)); } - break; - case CSS_HEIGHT_AUTO: - default: + } else { *height = AUTO; - break; } /* specified gadget heights include borders and padding in * some cases */ if (box->gadget && *height != AUTO) { - percentage = style->height.height == CSS_HEIGHT_PERCENT; + percentage = unit == CSS_UNIT_PCT; layout_tweak_form_dimensions(box, percentage, available_width, false, height); @@ -1290,53 +1354,60 @@ void layout_find_dimensions(int available_width, int viewport_height, } if (max_width) { - switch (style->max_width.max_width) { - case CSS_MAX_WIDTH_LENGTH: - *max_width = css_len2px(&style->max_width.value.length, - style); - break; - case CSS_MAX_WIDTH_PERCENT: - *max_width = (style->max_width.value.percent * - available_width) / 100; - break; - case CSS_MAX_WIDTH_NONE: - default: + enum css_max_width type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + type = css_computed_max_width(style, &value, &unit); + + if (type == CSS_MAX_WIDTH_SET) { + if (unit == CSS_UNIT_PCT) { + *max_width = (FIXTOFLT(value) * + available_width) / 100; + } else { + *max_width = FIXTOINT(nscss_len2px(value, unit, + style)); + } + } else { /* Inadmissible */ *max_width = -1; - break; } /* specified gadget widths include borders and padding in some * cases */ if (box->gadget && *max_width != -1) { - percentage = style->max_width.max_width == - CSS_WIDTH_PERCENT; + percentage = unit == CSS_UNIT_PCT; + layout_tweak_form_dimensions(box, percentage, available_width, true, max_width); } } if (min_width) { - switch (style->min_width.min_width) { - case CSS_MIN_WIDTH_LENGTH: - *min_width = css_len2px(&style->min_width.value. - length, style); - break; - case CSS_MIN_WIDTH_PERCENT: - *min_width = (style->min_width.value.percent * + enum css_min_width type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + type = css_computed_min_width(style, &value, &unit); + + if (type == CSS_MIN_WIDTH_SET) { + if (unit == CSS_UNIT_PCT) { + *min_width = (FIXTOFLT(value) * available_width) / 100; - break; - default: + } else { + *min_width = FIXTOINT(nscss_len2px(value, unit, + style)); + } + } else { /* Inadmissible */ *min_width = 0; - break; } /* specified gadget widths include borders and padding in some * cases */ if (box->gadget && *min_width != 0) { - percentage = style->min_width.min_width == - CSS_WIDTH_PERCENT; + percentage = unit == CSS_UNIT_PCT; + layout_tweak_form_dimensions(box, percentage, available_width, true, min_width); } @@ -1344,46 +1415,137 @@ void layout_find_dimensions(int available_width, int viewport_height, for (i = 0; i != 4; i++) { if (margin) { - switch (style->margin[i].margin) { - case CSS_MARGIN_LENGTH: - margin[i] = css_len2px(&style->margin[i]. - value.length, style); + enum css_margin type = CSS_MARGIN_AUTO;; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + switch (i) { + case TOP: + type = css_computed_margin_top(style, + &value, &unit); break; - case CSS_MARGIN_PERCENT: - margin[i] = available_width * - style->margin[i].value.percent / 100; + case RIGHT: + type = css_computed_margin_right(style, + &value, &unit); break; - case CSS_MARGIN_AUTO: - default: - margin[i] = AUTO; + case BOTTOM: + type = css_computed_margin_bottom(style, + &value, &unit); + break; + case LEFT: + type = css_computed_margin_left(style, + &value, &unit); break; } + + if (type == CSS_MARGIN_SET) { + if (unit == CSS_UNIT_PCT) { + margin[i] = available_width * + FIXTOFLT(value) / 100; + } else { + margin[i] = FIXTOINT(nscss_len2px(value, + unit, style)); + } + } else { + margin[i] = AUTO; + } } if (padding) { - switch (style->padding[i].padding) { - case CSS_PADDING_PERCENT: - padding[i] = available_width * - style->padding[i].value. - percent / 100; + enum css_padding type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + switch (i) { + case TOP: + type = css_computed_padding_top(style, + &value, &unit); break; - case CSS_PADDING_LENGTH: - default: - padding[i] = css_len2px(&style->padding[i]. - value.length, style); + case RIGHT: + type = css_computed_padding_right(style, + &value, &unit); + break; + case BOTTOM: + type = css_computed_padding_bottom(style, + &value, &unit); break; + case LEFT: + type = css_computed_padding_left(style, + &value, &unit); + break; + } + + if (unit == CSS_UNIT_PCT) { + padding[i] = available_width * + FIXTOFLT(value) / 100; + } else { + padding[i] = FIXTOINT(nscss_len2px(value, unit, + style)); } } - if (border) { - if (style->border[i].style == CSS_BORDER_STYLE_HIDDEN || - style->border[i].style == - CSS_BORDER_STYLE_NONE) + /* Table cell borders are populated in table.c */ + if (border && box->type != BOX_TABLE_CELL) { + enum css_border_width wtype; + enum css_border_style bstyle = CSS_BORDER_STYLE_NONE; + enum css_border_color bcolor = + CSS_BORDER_COLOR_TRANSPARENT; + css_color color = 0; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + switch (i) { + case TOP: + wtype = css_computed_border_top_width(style, + &value, &unit); + bstyle = css_computed_border_top_style(style); + bcolor = css_computed_border_top_color(style, + &color); + break; + case RIGHT: + wtype = css_computed_border_right_width(style, + &value, &unit); + bstyle = css_computed_border_right_style(style); + bcolor = css_computed_border_right_color(style, + &color); + break; + case BOTTOM: + wtype = css_computed_border_bottom_width(style, + &value, &unit); + bstyle = css_computed_border_bottom_style( + style); + bcolor = css_computed_border_bottom_color(style, + &color); + break; + case LEFT: + wtype = css_computed_border_left_width(style, + &value, &unit); + bstyle = css_computed_border_left_style(style); + bcolor = css_computed_border_left_color(style, + &color); + break; + } + + border[i].style = bstyle; + border[i].color = bcolor; + border[i].c = color; + + if (bstyle == CSS_BORDER_STYLE_HIDDEN || + bstyle == CSS_BORDER_STYLE_NONE) /* spec unclear: following Mozilla */ - border[i] = 0; + border[i].width = 0; else - border[i] = css_len2px(&style->border[i]. - width.value, style); + border[i].width = FIXTOINT(nscss_len2px(value, + unit, style)); + + /* Special case for border-collapse: make all borders + * on table/table-row-group/table-row zero width. */ + if (css_computed_border_collapse(style) == + CSS_BORDER_COLLAPSE_COLLAPSE && + (box->type == BOX_TABLE || + box->type == BOX_TABLE_ROW_GROUP || + box->type == BOX_TABLE_ROW)) + border[i].width = 0; } } } @@ -1435,7 +1597,7 @@ void layout_tweak_form_dimensions(struct box *box, bool percentage, * \return y coordinate relative to ancestor box for floats */ -int layout_clear(struct box *fl, css_clear clear) +int layout_clear(struct box *fl, enum css_clear clear) { int y = 0; for (; fl; fl = fl->next_float) { @@ -1522,13 +1684,17 @@ bool layout_inline_container(struct box *inline_container, int width, has_text_children = false; for (c = inline_container->children; c; c = c->next) { bool is_pre = false; - if (c->style) - is_pre = (c->style->white_space == - CSS_WHITE_SPACE_PRE || - c->style->white_space == - CSS_WHITE_SPACE_PRE_LINE || - c->style->white_space == - CSS_WHITE_SPACE_PRE_WRAP); + + if (c->style) { + enum css_white_space whitespace; + + whitespace = css_computed_white_space(c->style); + + is_pre = (whitespace == CSS_WHITE_SPACE_PRE || + whitespace == CSS_WHITE_SPACE_PRE_LINE || + whitespace == CSS_WHITE_SPACE_PRE_WRAP); + } + if ((!c->object && c->text && (c->length || is_pre)) || c->type == BOX_BR) has_text_children = true; @@ -1600,31 +1766,50 @@ void layout_minmax_inline_container(struct box *inline_container, * Calculate line height from a style. */ -int line_height(struct css_style *style) +int line_height(const css_computed_style *style) { - float font_len; + enum css_line_height lhtype; + css_fixed lhvalue = 0; + css_unit lhunit = CSS_UNIT_PX; + css_fixed line_height; assert(style); - assert(style->line_height.size == CSS_LINE_HEIGHT_LENGTH || - style->line_height.size == CSS_LINE_HEIGHT_ABSOLUTE || - style->line_height.size == CSS_LINE_HEIGHT_PERCENT); - /* take account of minimum font size option */ - if ((font_len = css_len2px(&style->font_size.value.length, 0)) < - option_font_min_size * css_screen_dpi / 720.0) - font_len = option_font_min_size * css_screen_dpi / 720.0; + lhtype = css_computed_line_height(style, &lhvalue, &lhunit); + if (lhtype == CSS_LINE_HEIGHT_NORMAL) { + /* Normal => use a constant of 1.3 * font-size */ + lhvalue = FLTTOFIX(1.3); + lhtype = CSS_LINE_HEIGHT_NUMBER; + } - switch (style->line_height.size) { - case CSS_LINE_HEIGHT_LENGTH: - return css_len2px(&style->line_height.value.length, style); + if (lhtype == CSS_LINE_HEIGHT_NUMBER || + lhunit == CSS_UNIT_PCT) { + css_fixed fs = 0, px_fs; + css_unit fs_unit = CSS_UNIT_PX; - case CSS_LINE_HEIGHT_ABSOLUTE: - return style->line_height.value.absolute * font_len; + css_computed_font_size(style, &fs, &fs_unit); - case CSS_LINE_HEIGHT_PERCENT: - default: - return style->line_height.value.percent * font_len / 100.0; + /* Convert to points */ + fs = nscss_len2pt(fs, fs_unit); + fs_unit = CSS_UNIT_PT; + + /* Clamp to configured minimum */ + if (fs < FDIVI(INTTOFIX(option_font_min_size), 10)) + fs = FDIVI(INTTOFIX(option_font_min_size), 10); + + px_fs = nscss_len2px(fs, fs_unit, style); + + if (lhtype == CSS_LINE_HEIGHT_NUMBER) + line_height = FMUL(lhvalue, px_fs); + else + line_height = FDIVI(FMUL(lhvalue, px_fs), 100); + } else { + assert(lhunit != CSS_UNIT_PCT); + + line_height = nscss_len2px(lhvalue, lhunit, style); } + + return FIXTOINT(line_height); } @@ -1666,13 +1851,9 @@ bool layout_line(struct box *first, int *width, int *y, int space_before = 0, space_after = 0; unsigned int inline_count = 0; unsigned int i; - struct css_length gadget_size; /* Checkbox / radio buttons */ const struct font_functions *font_func = content->data.html.font_func; plot_font_style_t fstyle; - gadget_size.unit = CSS_UNIT_EM; - gadget_size.value = 1; - LOG(("first %p, first->text '%.*s', width %i, y %i, cx %i, cy %i", first, (int) first->length, first->text, *width, *y, cx, cy)); @@ -1706,6 +1887,11 @@ bool layout_line(struct box *first, int *width, int *y, * keep in sync with the loop in layout_minmax_line() */ LOG(("x0 %i, x1 %i, x1 - x0 %i", x0, x1, x1 - x0)); for (x = 0, b = first; x <= x1 - x0 && b != 0; b = b->next) { + enum css_width wtype; + enum css_height htype; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + assert(b->type == BOX_INLINE || b->type == BOX_INLINE_BLOCK || b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT || @@ -1719,8 +1905,10 @@ bool layout_line(struct box *first, int *width, int *y, if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT) continue; if (b->type == BOX_INLINE_BLOCK && - (b->style->position == CSS_POSITION_ABSOLUTE || - b->style->position == CSS_POSITION_FIXED)) + (css_computed_position(b->style) == + CSS_POSITION_ABSOLUTE || + css_computed_position(b->style) == + CSS_POSITION_FIXED)) continue; assert(b->style != NULL); @@ -1732,13 +1920,15 @@ bool layout_line(struct box *first, int *width, int *y, if (b->max_width != UNKNOWN_WIDTH) if (!layout_float(b, *width, content)) return false; - h = b->border[TOP] + b->padding[TOP] + b->height + - b->padding[BOTTOM] + b->border[BOTTOM]; + h = b->border[TOP].width + b->padding[TOP] + b->height + + b->padding[BOTTOM] + + b->border[BOTTOM].width; if (height < h) height = h; - x += b->margin[LEFT] + b->border[LEFT] + + x += b->margin[LEFT] + b->border[LEFT].width + b->padding[LEFT] + b->width + - b->padding[RIGHT] + b->border[RIGHT] + + b->padding[RIGHT] + + b->border[RIGHT].width + b->margin[RIGHT]; space_after = 0; continue; @@ -1751,7 +1941,7 @@ bool layout_line(struct box *first, int *width, int *y, for (i = 0; i != 4; i++) if (b->margin[i] == AUTO) b->margin[i] = 0; - x += b->margin[LEFT] + b->border[LEFT] + + x += b->margin[LEFT] + b->border[LEFT].width + b->padding[LEFT]; if (b->inline_end) { b->inline_end->margin[RIGHT] = b->margin[RIGHT]; @@ -1760,7 +1950,8 @@ bool layout_line(struct box *first, int *width, int *y, b->inline_end->border[RIGHT] = b->border[RIGHT]; } else { - x += b->padding[RIGHT] + b->border[RIGHT] + + x += b->padding[RIGHT] + + b->border[RIGHT].width + b->margin[RIGHT]; } } else if (b->type == BOX_INLINE_END) { @@ -1772,7 +1963,7 @@ bool layout_line(struct box *first, int *width, int *y, } else { space_after = 0; } - x += b->padding[RIGHT] + b->border[RIGHT] + + x += b->padding[RIGHT] + b->border[RIGHT].width + b->margin[RIGHT]; continue; } @@ -1838,32 +2029,27 @@ bool layout_line(struct box *first, int *width, int *y, assert(b->style); /* calculate box width */ - switch (b->style->width.width) { - case CSS_WIDTH_LENGTH: - b->width = css_len2px(&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: + wtype = css_computed_width(b->style, &value, &unit); + if (wtype == CSS_WIDTH_SET) { + if (unit == CSS_UNIT_PCT) { + b->width = *width * FIXTOFLT(value) / 100; + } else { + b->width = FIXTOINT(nscss_len2px(value, unit, + b->style)); + } + } else { b->width = AUTO; - break; } /* height */ - switch (b->style->height.height) { - case CSS_HEIGHT_LENGTH: - b->height = css_len2px(&b->style->height.value.length, - b->style); - break; - case CSS_HEIGHT_AUTO: - default: + htype = css_computed_height(b->style, &value, &unit); + if (htype == CSS_HEIGHT_SET) { + b->height = FIXTOINT(nscss_len2px(value, unit, + b->style)); + } else { b->height = AUTO; - break; } - + if (b->object) { if (b->width == AUTO && b->height == AUTO) { b->width = b->object->width; @@ -1886,15 +2072,20 @@ bool layout_line(struct box *first, int *width, int *y, } else { /* form control with no object */ if (b->width == AUTO) - b->width = css_len2px(&gadget_size, b->style); + b->width = FIXTOINT(nscss_len2px(INTTOFIX(1), + CSS_UNIT_EM, b->style)); if (b->height == AUTO) - b->height = css_len2px(&gadget_size, b->style); + b->height = FIXTOINT(nscss_len2px(INTTOFIX(1), + CSS_UNIT_EM, b->style)); } if (b->object && b->object->type == CONTENT_HTML && b->width != b->object->available_width) { + htype = css_computed_height(b->style, &value, &unit); + content_reformat(b->object, b->width, b->height); - if (b->style->height.height == CSS_HEIGHT_AUTO) + + if (htype == CSS_HEIGHT_AUTO) b->height = b->object->height; } @@ -1925,8 +2116,10 @@ bool layout_line(struct box *first, int *width, int *y, for (x = x_previous = 0, b = first; x <= x1 - x0 && b; b = b->next) { LOG(("pass 2: b %p, x %i", b, x)); if (b->type == BOX_INLINE_BLOCK && - (b->style->position == CSS_POSITION_ABSOLUTE || - b->style->position == CSS_POSITION_FIXED)) { + (css_computed_position(b->style) == + CSS_POSITION_ABSOLUTE || + css_computed_position(b->style) == + CSS_POSITION_FIXED)) { b->x = x + space_after; } else if (b->type == BOX_INLINE || @@ -1941,16 +2134,17 @@ bool layout_line(struct box *first, int *width, int *y, if ((b->type == BOX_INLINE && !b->inline_end) || b->type == BOX_INLINE_BLOCK) { - b->x += b->margin[LEFT] + b->border[LEFT]; + b->x += b->margin[LEFT] + b->border[LEFT].width; x = b->x + b->padding[LEFT] + b->width + b->padding[RIGHT] + - b->border[RIGHT] + + b->border[RIGHT].width + b->margin[RIGHT]; } else if (b->type == BOX_INLINE) { - b->x += b->margin[LEFT] + b->border[LEFT]; + b->x += b->margin[LEFT] + b->border[LEFT].width; x = b->x + b->padding[LEFT] + b->width; } else if (b->type == BOX_INLINE_END) { - x += b->padding[RIGHT] + b->border[RIGHT] + + x += b->padding[RIGHT] + + b->border[RIGHT].width + b->margin[RIGHT]; } else { x += b->width; @@ -1991,27 +2185,33 @@ bool layout_line(struct box *first, int *width, int *y, if (!layout_float(d, *width, content)) return false; - LOG(("%p : %d %d", d, d->margin[TOP], d->border[TOP])); - d->x = d->margin[LEFT] + d->border[LEFT]; - d->y = d->margin[TOP] + d->border[TOP]; - b->width = d->margin[LEFT] + d->border[LEFT] + + LOG(("%p : %d %d", d, d->margin[TOP], + d->border[TOP].width)); + d->x = d->margin[LEFT] + d->border[LEFT].width; + d->y = d->margin[TOP] + d->border[TOP].width; + b->width = d->margin[LEFT] + d->border[LEFT].width + d->padding[LEFT] + d->width + - d->padding[RIGHT] + d->border[RIGHT] + + d->padding[RIGHT] + + d->border[RIGHT].width + d->margin[RIGHT]; - b->height = d->margin[TOP] + d->border[TOP] + + b->height = d->margin[TOP] + d->border[TOP].width + d->padding[TOP] + d->height + - d->padding[BOTTOM] + d->border[BOTTOM] + + d->padding[BOTTOM] + + d->border[BOTTOM].width + d->margin[BOTTOM]; if (b->width > (x1 - x0) - x) place_below = true; - if (d->style && (d->style->clear == CSS_CLEAR_NONE || - (d->style->clear == CSS_CLEAR_LEFT && - left == 0) || - (d->style->clear == CSS_CLEAR_RIGHT && - right == 0) || - (d->style->clear == CSS_CLEAR_BOTH && - left == 0 && right == 0)) && + if (d->style && (css_computed_clear(d->style) == + CSS_CLEAR_NONE || + (css_computed_clear(d->style) == + CSS_CLEAR_LEFT && left == 0) || + (css_computed_clear(d->style) == + CSS_CLEAR_RIGHT && + right == 0) || + (css_computed_clear(d->style) == + CSS_CLEAR_BOTH && + left == 0 && right == 0)) && (!place_below || (left == 0 && right == 0 && x == 0)) && cy >= cont->clear_level) { @@ -2044,7 +2244,7 @@ bool layout_line(struct box *first, int *width, int *y, place_float_below(b, *width, cx, fy + height, cont); - if (d->style && d->style->clear != + if (d->style && css_computed_clear(d->style) != CSS_CLEAR_NONE) { /* to be cleared below existing * floats */ @@ -2054,7 +2254,7 @@ bool layout_line(struct box *first, int *width, int *y, b->x = cx + *width - b->width; fy = layout_clear(cont->float_children, - d->style->clear); + css_computed_clear(d->style)); if (fy > cont->clear_level) cont->clear_level = fy; if (b->y < fy) @@ -2221,7 +2421,7 @@ bool layout_line(struct box *first, int *width, int *y, } /* set positions */ - switch (first->parent->parent->style->text_align) { + switch (css_computed_text_align(first->parent->parent->style)) { case CSS_TEXT_ALIGN_RIGHT: x0 = x1 - x; break; @@ -2243,20 +2443,24 @@ bool layout_line(struct box *first, int *width, int *y, } if ((d->type == BOX_INLINE && (d->object || d->gadget)) || d->type == BOX_INLINE_BLOCK) { - d->y = *y + d->border[TOP] + d->margin[TOP]; + d->y = *y + d->border[TOP].width + d->margin[TOP]; } if (d->type == BOX_INLINE_BLOCK) { d->x += x0; } if (d->type == BOX_INLINE_BLOCK && - (d->style->position == CSS_POSITION_ABSOLUTE || - d->style->position == CSS_POSITION_FIXED)) + (css_computed_position(d->style) == + CSS_POSITION_ABSOLUTE || + css_computed_position(d->style) == + CSS_POSITION_FIXED)) continue; if ((d->type == BOX_INLINE && (d->object || d->gadget)) || d->type == BOX_INLINE_BLOCK) { - h = d->margin[TOP] + d->border[TOP] + d->padding[TOP] + - d->height + d->padding[BOTTOM] + - d->border[BOTTOM] + d->margin[BOTTOM]; + h = d->margin[TOP] + d->border[TOP].width + + d->padding[TOP] + d->height + + d->padding[BOTTOM] + + d->border[BOTTOM].width + + d->margin[BOTTOM]; if (used_height < h) used_height = h; } @@ -2279,9 +2483,9 @@ bool layout_line(struct box *first, int *width, int *y, } /* handle clearance for br */ - if (br_box && br_box->style->clear != CSS_CLEAR_NONE) { + if (br_box && css_computed_clear(br_box->style) != CSS_CLEAR_NONE) { int clear_y = layout_clear(cont->float_children, - br_box->style->clear); + css_computed_clear(br_box->style)); if (used_height < clear_y - cy) used_height = clear_y - cy; } @@ -2312,14 +2516,15 @@ struct box *layout_minmax_line(struct box *first, float frac; size_t i, j; struct box *b; - struct css_length gadget_size; /* Checkbox / radio buttons */ plot_font_style_t fstyle; - gadget_size.unit = CSS_UNIT_EM; - gadget_size.value = 1; - /* corresponds to the pass 1 loop in layout_line() */ for (b = first; b; b = b->next) { + enum css_width wtype; + enum css_height htype; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + assert(b->type == BOX_INLINE || b->type == BOX_INLINE_BLOCK || b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT || @@ -2444,34 +2649,28 @@ struct box *layout_minmax_line(struct box *first, assert(b->style); /* calculate box width */ - switch (b->style->width.width) { - case CSS_WIDTH_LENGTH: - width = css_len2px(&b->style->width.value.length, - b->style); - if (width < 0) - width = 0; - break; - case CSS_WIDTH_PERCENT: - /* - b->width = width * b->style->width.value.percent / 100; - break; - */ - case CSS_WIDTH_AUTO: - default: + wtype = css_computed_width(b->style, &value, &unit); + if (wtype == CSS_WIDTH_SET) { + if (unit == CSS_UNIT_PCT) { + /* + b->width = width * FIXTOFLT(value) / 100 + */ + } else { + width = FIXTOINT(nscss_len2px(value, unit, + b->style)); + if (width < 0) + width = 0; + } + } else { width = AUTO; - break; } /* height */ - switch (b->style->height.height) { - case CSS_HEIGHT_LENGTH: - height = css_len2px(&b->style->height.value.length, - b->style); - break; - case CSS_HEIGHT_AUTO: - default: + htype = css_computed_height(b->style, &value, &unit); + if (htype == CSS_HEIGHT_SET) { + height = FIXTOINT(nscss_len2px(value, unit, b->style)); + } else { height = AUTO; - break; } if (b->object) { @@ -2494,7 +2693,8 @@ struct box *layout_minmax_line(struct box *first, } else { /* form control with no object */ if (width == AUTO) - width = css_len2px(&gadget_size, b->style); + width = FIXTOINT(nscss_len2px(INTTOFIX(1), + CSS_UNIT_EM, b->style)); } if (min < width) @@ -2522,15 +2722,17 @@ struct box *layout_minmax_line(struct box *first, * \return length of indent */ -int layout_text_indent(struct css_style *style, int width) +int layout_text_indent(const css_computed_style *style, int width) { - switch (style->text_indent.size) { - case CSS_TEXT_INDENT_LENGTH: - return css_len2px(&style->text_indent.value.length, style); - case CSS_TEXT_INDENT_PERCENT: - return width * style->text_indent.value.percent / 100; - default: - return 0; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + + css_computed_text_indent(style, &value, &unit); + + if (unit == CSS_UNIT_PCT) { + return width * FIXTOFLT(value) / 100; + } else { + return FIXTOINT(nscss_len2px(value, unit, style)); } } @@ -2644,7 +2846,11 @@ bool layout_table(struct box *table, int available_width, struct box *row_group; struct box **row_span_cell; struct column *col; - struct css_style *style = table->style; + const css_computed_style *style = table->style; + enum css_width wtype; + enum css_height htype; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; assert(table->type == BOX_TABLE); assert(style); @@ -2676,12 +2882,13 @@ bool layout_table(struct box *table, int available_width, for (row = row_group->children; row; row = row->next) { for (c = row->children; c; c = c->next) { assert(c->style); + table_used_border_for_cell(c); layout_find_dimensions(available_width, -1, c, c->style, 0, 0, 0, 0, 0, c->padding, c->border); - if (c->style->overflow == + if (css_computed_overflow(c->style) == CSS_OVERFLOW_SCROLL || - c->style->overflow == + css_computed_overflow(c->style) == CSS_OVERFLOW_AUTO) { c->padding[RIGHT] += SCROLLBAR_WIDTH; c->padding[BOTTOM] += SCROLLBAR_WIDTH; @@ -2691,92 +2898,104 @@ bool layout_table(struct box *table, int available_width, } /* border-spacing is used in the separated borders model */ - if (style->border_collapse == CSS_BORDER_COLLAPSE_SEPARATE) { - border_spacing_h = css_len2px(&style->border_spacing.horz, - style); - border_spacing_v = css_len2px(&style->border_spacing.vert, - style); - } + if (css_computed_border_collapse(style) == + CSS_BORDER_COLLAPSE_SEPARATE) { + css_fixed h = 0, v = 0; + css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX; - /* find specified table width, or available width if auto-width */ - switch (style->width.width) { - case CSS_WIDTH_LENGTH: - table_width = css_len2px(&style->width.value.length, style); + css_computed_border_spacing(style, &h, &hu, &v, &vu); - /* specified width includes border */ - table_width -= table->border[LEFT] + table->border[RIGHT]; - table_width = table_width < 0 ? 0 : table_width; + border_spacing_h = FIXTOINT(nscss_len2px(h, hu, style)); + border_spacing_v = FIXTOINT(nscss_len2px(v, vu, style)); + } - auto_width = table_width; - break; - case CSS_WIDTH_PERCENT: - table_width = ceil(available_width * - style->width.value.percent / 100); + /* find specified table width, or available width if auto-width */ + wtype = css_computed_width(style, &value, &unit); + if (wtype == CSS_WIDTH_SET) { + if (unit == CSS_UNIT_PCT) { + table_width = ceil(available_width * + FIXTOFLT(value) / 100); + } else { + table_width = + FIXTOINT(nscss_len2px(value, unit, style)); + } /* specified width includes border */ - table_width -= table->border[LEFT] + table->border[RIGHT]; + table_width -= table->border[LEFT].width + + table->border[RIGHT].width; table_width = table_width < 0 ? 0 : table_width; auto_width = table_width; - break; - case CSS_WIDTH_AUTO: - default: + } else { table_width = AUTO; auto_width = available_width - ((table->margin[LEFT] == AUTO ? 0 : table->margin[LEFT]) + - table->border[LEFT] + + table->border[LEFT].width + table->padding[LEFT] + table->padding[RIGHT] + - table->border[RIGHT] + + table->border[RIGHT].width + (table->margin[RIGHT] == AUTO ? 0 : table->margin[RIGHT])); - break; } /* Find any table height specified within CSS/HTML */ - if (style->height.height == CSS_HEIGHT_LENGTH) { - /* This is the minimum height for the table (see 17.5.3) */ - min_height = css_len2px(&style->height.value.length, style); - } else if (style->height.height == CSS_HEIGHT_PERCENT) { - /* This is the minimum height for the table (see 17.5.3) */ - if (table->style->position == CSS_POSITION_ABSOLUTE) { - /* Table is absolutely positioned */ - assert(table->float_container); - containing_block = table->float_container; - } else if (table->float_container && - table->style->position != - CSS_POSITION_ABSOLUTE && - (table->style->float_ == - CSS_FLOAT_LEFT || - table->style->float_ == - CSS_FLOAT_RIGHT)) { - /* Table is a float */ - assert(table->parent && table->parent->parent && - table->parent->parent->parent); - containing_block = table->parent->parent->parent; - } else if (table->parent && table->parent->type != - BOX_INLINE_CONTAINER) { - /* Table is a block level element */ - containing_block = table->parent; - } else if (table->parent && table->parent->type == - BOX_INLINE_CONTAINER) { - /* Table is an inline block */ - assert(table->parent->parent); - containing_block = table->parent->parent; - } - if (containing_block && containing_block->height != AUTO && - (table->style->position == - CSS_POSITION_ABSOLUTE || - (containing_block->style->height.height == - CSS_HEIGHT_LENGTH || - containing_block->style->height.height == - CSS_HEIGHT_PERCENT))) { - /* Table is absolutely positioned or its - * containing block has a valid specified - * height. (CSS 2.1 Section 10.5) */ - min_height = style->height.value.percent * + htype = css_computed_height(style, &value, &unit); + if (htype == CSS_HEIGHT_SET) { + if (unit == CSS_UNIT_PCT) { + /* This is the minimum height for the table + * (see 17.5.3) */ + if (css_computed_position(table->style) == + CSS_POSITION_ABSOLUTE) { + /* Table is absolutely positioned */ + assert(table->float_container); + containing_block = table->float_container; + } else if (table->float_container && + css_computed_position(table->style) != + CSS_POSITION_ABSOLUTE && + (css_computed_float(table->style) == + CSS_FLOAT_LEFT || + css_computed_float(table->style) == + CSS_FLOAT_RIGHT)) { + /* Table is a float */ + assert(table->parent && table->parent->parent && + table->parent->parent->parent); + containing_block = + table->parent->parent->parent; + } else if (table->parent && table->parent->type != + BOX_INLINE_CONTAINER) { + /* Table is a block level element */ + containing_block = table->parent; + } else if (table->parent && table->parent->type == + BOX_INLINE_CONTAINER) { + /* Table is an inline block */ + assert(table->parent->parent); + containing_block = table->parent->parent; + } + + if (containing_block) { + css_fixed ignored = 0; + + htype = css_computed_height( + containing_block->style, + &ignored, &unit); + } + + if (containing_block && + containing_block->height != AUTO && + (css_computed_position(table->style) == + CSS_POSITION_ABSOLUTE || + htype == CSS_HEIGHT_SET)) { + /* Table is absolutely positioned or its + * containing block has a valid specified + * height. (CSS 2.1 Section 10.5) */ + min_height = FIXTOFLT(value) * containing_block->height / 100; + } + } else { + /* This is the minimum height for the table + * (see 17.5.3) */ + min_height = FIXTOINT(nscss_len2px(value, unit, style)); } } @@ -2950,20 +3169,21 @@ bool layout_table(struct box *table, int available_width, int row_group_height = 0; for (row = row_group->children; row; row = row->next) { int row_height = 0; - if (row->style->height.height == CSS_HEIGHT_LENGTH) { - row_height = (int) css_len2px(&row->style-> - height.value.length, - row->style); + + htype = css_computed_height(row->style, &value, &unit); + if (htype == CSS_HEIGHT_SET && unit != CSS_UNIT_PCT) { + row_height = FIXTOINT(nscss_len2px(value, unit, + row->style)); } for (c = row->children; c; c = c->next) { assert(c->style); c->width = xs[c->start_column + c->columns] - xs[c->start_column] - border_spacing_h - - c->border[LEFT] - + c->border[LEFT].width - c->padding[LEFT] - c->padding[RIGHT] - - c->border[RIGHT]; + c->border[RIGHT].width; c->float_children = 0; c->height = AUTO; @@ -2980,13 +3200,17 @@ bool layout_table(struct box *table, int available_width, * until after vertical alignment is complete */ c->descendant_y0 = c->height; c->descendant_y1 = c->padding[BOTTOM]; - if (c->style->height.height == - CSS_HEIGHT_LENGTH) { + + htype = css_computed_height(c->style, + &value, &unit); + + if (htype == CSS_HEIGHT_SET && + unit != CSS_UNIT_PCT) { /* some sites use height="1" or similar * to attempt to make cells as small as * possible, so treat it as a minimum */ - int h = (int) css_len2px(&c->style-> - height.value.length, c->style); + int h = FIXTOINT(nscss_len2px(value, + unit, c->style)); if (c->height < h) c->height = h; } @@ -2994,24 +3218,25 @@ bool layout_table(struct box *table, int available_width, */ if (c->height < row_height) c->height = row_height; - c->x = xs[c->start_column] + c->border[LEFT]; - c->y = c->border[TOP]; + c->x = xs[c->start_column] + + c->border[LEFT].width; + c->y = c->border[TOP].width; for (i = 0; i != c->columns; i++) { row_span[c->start_column + i] = c->rows; excess_y[c->start_column + i] = - c->border[TOP] + + c->border[TOP].width + c->padding[TOP] + c->height + c->padding[BOTTOM] + - c->border[BOTTOM]; + c->border[BOTTOM].width; row_span_cell[c->start_column + i] = 0; } row_span_cell[c->start_column] = c; c->padding[BOTTOM] = -border_spacing_v - - c->border[TOP] - + c->border[TOP].width - c->padding[TOP] - c->height - - c->border[BOTTOM]; + c->border[BOTTOM].width; } for (i = 0; i != columns; i++) if (row_span[i] != 0) @@ -3064,19 +3289,24 @@ bool layout_table(struct box *table, int available_width, row_group = row_group->next) { for (row = row_group->children; row; row = row->next) { for (c = row->children; c; c = c->next) { + enum css_vertical_align vertical_align; + /* unextended bottom padding is in * c->descendant_y1, and unextended * cell height is in c->descendant_y0 */ spare_height = (c->padding[BOTTOM] - c->descendant_y1) + (c->height - c->descendant_y0); - switch (c->style->vertical_align.type) { + + vertical_align = css_computed_vertical_align( + c->style, &value, &unit); + + switch (vertical_align) { case CSS_VERTICAL_ALIGN_SUB: case CSS_VERTICAL_ALIGN_SUPER: case CSS_VERTICAL_ALIGN_TEXT_TOP: case CSS_VERTICAL_ALIGN_TEXT_BOTTOM: - case CSS_VERTICAL_ALIGN_LENGTH: - case CSS_VERTICAL_ALIGN_PERCENT: + case CSS_VERTICAL_ALIGN_SET: case CSS_VERTICAL_ALIGN_BASELINE: /* todo: baseline alignment, for now * just use ALIGN_TOP */ @@ -3094,7 +3324,6 @@ bool layout_table(struct box *table, int available_width, layout_move_children(c, 0, spare_height); break; - case CSS_VERTICAL_ALIGN_NOT_SET: case CSS_VERTICAL_ALIGN_INHERIT: assert(0); break; @@ -3134,6 +3363,9 @@ void layout_minmax_table(struct box *table, float extra_frac = 0; struct column *col = table->col; struct box *row_group, *row, *cell; + enum css_width wtype; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; /* check if the widths have already been calculated */ if (table->max_width != UNKNOWN_MAX_WIDTH) @@ -3148,9 +3380,15 @@ void layout_minmax_table(struct box *table, } /* border-spacing is used in the separated borders model */ - if (table->style->border_collapse == CSS_BORDER_COLLAPSE_SEPARATE) - border_spacing_h = css_len2px(&table->style-> - border_spacing.horz, table->style); + if (css_computed_border_collapse(table->style) == + CSS_BORDER_COLLAPSE_SEPARATE) { + css_fixed h = 0, v = 0; + css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX; + + css_computed_border_spacing(table->style, &h, &hu, &v, &vu); + + border_spacing_h = FIXTOINT(nscss_len2px(h, hu, table->style)); + } /* 1st pass: consider cells with colspan 1 only */ for (row_group = table->children; row_group; row_group =row_group->next) @@ -3249,9 +3487,9 @@ void layout_minmax_table(struct box *table, } /* fixed width takes priority, unless it is too narrow */ - if (table->style->width.width == CSS_WIDTH_LENGTH) { - int width = css_len2px(&table->style->width.value.length, - table->style); + wtype = css_computed_width(table->style, &value, &unit); + if (wtype == CSS_WIDTH_SET && unit != CSS_UNIT_PCT) { + int width = FIXTOINT(nscss_len2px(value, unit, table->style)); if (table_min < width) table_min = width; if (table_max < width) @@ -3309,35 +3547,77 @@ void layout_move_children(struct box *box, int x, int y) * \param frac increased by sum of fractional margin and padding */ -void calculate_mbp_width(struct css_style *style, unsigned int side, +void calculate_mbp_width(const css_computed_style *style, unsigned int side, bool margin, bool border, bool padding, int *fixed, float *frac) { + typedef uint8_t (*len_func)(const css_computed_style *style, + css_fixed *length, css_unit *unit); + + static len_func margin_funcs[4] = { + css_computed_margin_top, + css_computed_margin_right, + css_computed_margin_bottom, + css_computed_margin_left + }; + static len_func padding_funcs[4] = { + css_computed_padding_top, + css_computed_padding_right, + css_computed_padding_bottom, + css_computed_padding_left + }; + static struct { + len_func width; + uint8_t (*style)(const css_computed_style *style); + } border_funcs[4] = { + { css_computed_border_top_width, + css_computed_border_top_style }, + { css_computed_border_right_width, + css_computed_border_right_style }, + { css_computed_border_bottom_width, + css_computed_border_bottom_style }, + { css_computed_border_left_width, + css_computed_border_left_style } + }; + + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + assert(style); /* margin */ if (margin) { - if (style->margin[side].margin == CSS_MARGIN_LENGTH) - *fixed += css_len2px(&style->margin[side].value.length, - style); - else if (style->margin[side].margin == CSS_MARGIN_PERCENT) - *frac += style->margin[side].value.percent * 0.01; + enum css_margin type; + + type = margin_funcs[side](style, &value, &unit); + if (type == CSS_MARGIN_SET) { + if (unit == CSS_UNIT_PCT) { + *frac += FIXTOFLT(value) * 0.01; + } else { + *fixed += FIXTOINT(nscss_len2px(value, unit, + style)); + } + } } /* border */ if (border) { - if (style->border[side].style != CSS_BORDER_STYLE_NONE) - *fixed += css_len2px(&style->border[side].width.value, - style); + if (border_funcs[side].style(style) != + CSS_BORDER_STYLE_NONE) { + border_funcs[side].width(style, &value, &unit); + + *fixed += FIXTOINT(nscss_len2px(value, unit, style)); + } } /* padding */ if (padding) { - if (style->padding[side].padding == CSS_PADDING_LENGTH) - *fixed += css_len2px(&style->padding[side].value.length, - style); - else if (style->padding[side].padding == CSS_PADDING_PERCENT) - *frac += style->padding[side].value.percent * 0.01; + padding_funcs[side](style, &value, &unit); + if (unit == CSS_UNIT_PCT) { + *frac += FIXTOFLT(value) * 0.01; + } else { + *fixed += FIXTOINT(nscss_len2px(value, unit, style)); + } } } @@ -3424,7 +3704,8 @@ void layout_position_relative(struct box *root, struct box *fp, int fx, int fy) continue; /* If relatively positioned, get offsets */ - if (box->style && box->style->position == CSS_POSITION_RELATIVE) + if (box->style && css_computed_position(box->style) == + CSS_POSITION_RELATIVE) layout_compute_relative_offset(box, &x, &y); else x = y = 0; @@ -3432,8 +3713,10 @@ void layout_position_relative(struct box *root, struct box *fp, int fx, int fy) /* Adjust float coordinates. * (note float x and y are relative to their block formatting * context box and not their parent) */ - if (box->style && (box->style->float_ == CSS_FLOAT_LEFT || - box->style->float_ == CSS_FLOAT_RIGHT) && + if (box->style && (css_computed_float(box->style) == + CSS_FLOAT_LEFT || + css_computed_float(box->style) == + CSS_FLOAT_RIGHT) && (fx != 0 || fy != 0)) { /* box is a float and there is a float offset to * apply */ @@ -3463,7 +3746,8 @@ void layout_position_relative(struct box *root, struct box *fp, int fx, int fy) /* Ignore things we're not interested in. */ if (!box->style || (box->style && - box->style->position != CSS_POSITION_RELATIVE)) + css_computed_position(box->style) != + CSS_POSITION_RELATIVE)) continue; box->x += x; @@ -3498,10 +3782,12 @@ void layout_compute_relative_offset(struct box *box, int *x, int *y) struct box *containing_block; assert(box && box->parent && box->style && - box->style->position == CSS_POSITION_RELATIVE); + css_computed_position(box->style) == + CSS_POSITION_RELATIVE); - if (box->float_container && (box->style->float_ == CSS_FLOAT_LEFT || - box->style->float_ == CSS_FLOAT_RIGHT)) { + if (box->float_container && (css_computed_float(box->style) == + CSS_FLOAT_LEFT || + css_computed_float(box->style) == CSS_FLOAT_RIGHT)) { containing_block = box->float_container; } else { containing_block = box->parent; @@ -3522,11 +3808,12 @@ void layout_compute_relative_offset(struct box *box, int *x, int *y) /* over constrained => examine direction property * of containing block */ if (containing_block->style) { - if (box->parent->style->direction == + if (css_computed_direction(containing_block->style) == CSS_DIRECTION_LTR) /* left wins */ right = -left; - else if (box->parent->style->direction == + else if (css_computed_direction( + containing_block->style) == CSS_DIRECTION_RTL) /* right wins */ left = -right; @@ -3576,21 +3863,25 @@ bool layout_position_absolute(struct box *box, for (c = box->children; c; c = c->next) { if ((c->type == BOX_BLOCK || c->type == BOX_TABLE || c->type == BOX_INLINE_BLOCK) && - (c->style->position == CSS_POSITION_ABSOLUTE || - c->style->position == CSS_POSITION_FIXED)) { + (css_computed_position(c->style) == + CSS_POSITION_ABSOLUTE || + css_computed_position(c->style) == + CSS_POSITION_FIXED)) { if (!layout_absolute(c, containing_block, cx, cy, content)) return false; if (!layout_position_absolute(c, c, 0, 0, content)) return false; - } else if (c->style && - c->style->position == CSS_POSITION_RELATIVE) { + } else if (c->style && css_computed_position(c->style) == + CSS_POSITION_RELATIVE) { if (!layout_position_absolute(c, c, 0, 0, content)) return false; } else { int px, py; - if (c->style && (c->style->float_ == CSS_FLOAT_LEFT || - c->style->float_ == CSS_FLOAT_RIGHT)) { + if (c->style && (css_computed_float(c->style) == + CSS_FLOAT_LEFT || + css_computed_float(c->style) == + CSS_FLOAT_RIGHT)) { /* Float x/y coords are relative to nearest * ansestor with float_children, rather than * relative to parent. Need to get x/y relative @@ -3639,7 +3930,7 @@ bool layout_absolute(struct box *box, struct box *containing_block, int width, height, max_width, min_width; int *margin = box->margin; int *padding = box->padding; - int *border = box->border; + struct box_border *border = box->border; int available_width = containing_block->width; int space; @@ -3680,8 +3971,9 @@ bool layout_absolute(struct box *box, struct box *containing_block, /* 10.3.7 */ LOG(("%i + %i + %i + %i + %i + %i + %i + %i + %i = %i", - left, margin[LEFT], border[LEFT], padding[LEFT], width, - padding[RIGHT], border[RIGHT], margin[RIGHT], right, + left, margin[LEFT], border[LEFT].width, + padding[LEFT], width, padding[RIGHT], + border[RIGHT].width, margin[RIGHT], right, containing_block->width)); if (left == AUTO && width == AUTO && right == AUTO) { if (margin[LEFT] == AUTO) @@ -3692,19 +3984,19 @@ bool layout_absolute(struct box *box, struct box *containing_block, width = min(max(box->min_width, available_width), box->max_width); - width -= box->margin[LEFT] + box->border[LEFT] + + width -= box->margin[LEFT] + box->border[LEFT].width + box->padding[LEFT] + box->padding[RIGHT] + - box->border[RIGHT] + box->margin[RIGHT]; + box->border[RIGHT].width + box->margin[RIGHT]; /* Adjust for {min|max}-width */ if (max_width >= 0 && width > max_width) width = max_width; if (min_width > 0 && width < min_width) width = min_width; right = containing_block->width - - left - - margin[LEFT] - border[LEFT] - padding[LEFT] - - width - - padding[RIGHT] - border[RIGHT] - margin[RIGHT]; + left - + margin[LEFT] - border[LEFT].width - padding[LEFT] - + width - + padding[RIGHT] - border[RIGHT].width - margin[RIGHT]; } else if (left != AUTO && width != AUTO && right != AUTO) { /* Adjust for {min|max}-width */ @@ -3713,9 +4005,9 @@ bool layout_absolute(struct box *box, struct box *containing_block, if (margin[LEFT] == AUTO && margin[RIGHT] == AUTO) { space = containing_block->width - - left - border[LEFT] - + left - border[LEFT].width - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT] - right; + border[RIGHT].width - right; if (space < 0) { margin[LEFT] = 0; margin[RIGHT] = space; @@ -3724,19 +4016,22 @@ bool layout_absolute(struct box *box, struct box *containing_block, } } else if (margin[LEFT] == AUTO) { margin[LEFT] = containing_block->width - - left - border[LEFT] - + left - border[LEFT].width - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT] - margin[RIGHT] - right; + border[RIGHT].width - margin[RIGHT] - + right; } else if (margin[RIGHT] == AUTO) { margin[RIGHT] = containing_block->width - - left - margin[LEFT] - border[LEFT] - + left - margin[LEFT] - + border[LEFT].width - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT] - right; + border[RIGHT].width - right; } else { right = containing_block->width - - left - margin[LEFT] - border[LEFT] - + left - margin[LEFT] - + border[LEFT].width - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT] - margin[RIGHT]; + border[RIGHT].width - margin[RIGHT]; } } else { if (margin[LEFT] == AUTO) @@ -3749,9 +4044,9 @@ bool layout_absolute(struct box *box, struct box *containing_block, width = min(max(box->min_width, available_width), box->max_width); - width -= box->margin[LEFT] + box->border[LEFT] + + width -= box->margin[LEFT] + box->border[LEFT].width + box->padding[LEFT] + box->padding[RIGHT] + - box->border[RIGHT] + box->margin[RIGHT]; + box->border[RIGHT].width + box->margin[RIGHT]; /* Adjust for {min|max}-width */ if (max_width >= 0 && width > max_width) @@ -3760,9 +4055,10 @@ bool layout_absolute(struct box *box, struct box *containing_block, width = min_width; left = containing_block->width - - margin[LEFT] - border[LEFT] - + margin[LEFT] - border[LEFT].width - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT] - margin[RIGHT] - right; + border[RIGHT].width - margin[RIGHT] - + right; } else if (left == AUTO && width != AUTO && right == AUTO) { /* Adjust for {min|max}-width */ @@ -3773,17 +4069,18 @@ bool layout_absolute(struct box *box, struct box *containing_block, left = static_left; right = containing_block->width - - left - margin[LEFT] - border[LEFT] - + left - margin[LEFT] - + border[LEFT].width - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT] - margin[RIGHT]; + border[RIGHT].width - margin[RIGHT]; } else if (left != AUTO && width == AUTO && right == AUTO) { available_width -= left; width = min(max(box->min_width, available_width), box->max_width); - width -= box->margin[LEFT] + box->border[LEFT] + + width -= box->margin[LEFT] + box->border[LEFT].width + box->padding[LEFT] + box->padding[RIGHT] + - box->border[RIGHT] + box->margin[RIGHT]; + box->border[RIGHT].width + box->margin[RIGHT]; /* Adjust for {min|max}-width */ if (max_width >= 0 && width > max_width) @@ -3792,9 +4089,10 @@ bool layout_absolute(struct box *box, struct box *containing_block, width = min_width; right = containing_block->width - - left - margin[LEFT] - border[LEFT] - + left - margin[LEFT] - + border[LEFT].width - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT] - margin[RIGHT]; + border[RIGHT].width - margin[RIGHT]; } else if (left == AUTO && width != AUTO && right != AUTO) { /* Adjust for {min|max}-width */ @@ -3804,14 +4102,17 @@ bool layout_absolute(struct box *box, struct box *containing_block, width = min_width; left = containing_block->width - - margin[LEFT] - border[LEFT] - + margin[LEFT] - border[LEFT].width - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT] - margin[RIGHT] - right; + border[RIGHT].width - margin[RIGHT] - + right; } else if (left != AUTO && width == AUTO && right != AUTO) { width = containing_block->width - - left - margin[LEFT] - border[LEFT] - + left - margin[LEFT] - + border[LEFT].width - padding[LEFT] - padding[RIGHT] - - border[RIGHT] - margin[RIGHT] - right; + border[RIGHT].width - margin[RIGHT] - + right; /* Adjust for {min|max}-width */ if (max_width >= 0 && width > max_width) @@ -3828,17 +4129,19 @@ bool layout_absolute(struct box *box, struct box *containing_block, width = min_width; right = containing_block->width - - left - margin[LEFT] - border[LEFT] - + left - margin[LEFT] - + border[LEFT].width - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT] - margin[RIGHT]; + border[RIGHT].width - margin[RIGHT]; } } LOG(("%i + %i + %i + %i + %i + %i + %i + %i + %i = %i", - left, margin[LEFT], border[LEFT], padding[LEFT], width, - padding[RIGHT], border[RIGHT], margin[RIGHT], right, + left, margin[LEFT], border[LEFT].width, padding[LEFT], + width, padding[RIGHT], border[RIGHT].width, + margin[RIGHT], right, containing_block->width)); - box->x = left + margin[LEFT] + border[LEFT] - cx; + box->x = left + margin[LEFT] + border[LEFT].width - cx; if (containing_block->type == BOX_BLOCK || containing_block->type == BOX_INLINE_BLOCK || containing_block->type == BOX_TABLE_CELL) { @@ -3865,8 +4168,9 @@ bool layout_absolute(struct box *box, struct box *containing_block, /* 10.6.4 */ LOG(("%i + %i + %i + %i + %i + %i + %i + %i + %i = %i", - top, margin[TOP], border[TOP], padding[TOP], height, - padding[BOTTOM], border[BOTTOM], margin[BOTTOM], bottom, + top, margin[TOP], border[TOP].width, padding[TOP], + height, padding[BOTTOM], border[BOTTOM].width, + margin[BOTTOM], bottom, containing_block->height)); if (top == AUTO && height == AUTO && bottom == AUTO) { top = static_top; @@ -3876,33 +4180,33 @@ bool layout_absolute(struct box *box, struct box *containing_block, if (margin[BOTTOM] == AUTO) margin[BOTTOM] = 0; bottom = containing_block->height - - top - margin[TOP] - border[TOP] - + top - margin[TOP] - border[TOP].width - padding[TOP] - height - padding[BOTTOM] - - border[BOTTOM] - margin[BOTTOM]; + border[BOTTOM].width - margin[BOTTOM]; } else if (top != AUTO && height != AUTO && bottom != AUTO) { if (margin[TOP] == AUTO && margin[BOTTOM] == AUTO) { space = containing_block->height - - top - border[TOP] - padding[TOP] - + top - border[TOP].width - padding[TOP] - height - padding[BOTTOM] - - border[BOTTOM] - bottom; + border[BOTTOM].width - bottom; margin[TOP] = margin[BOTTOM] = space / 2; } else if (margin[TOP] == AUTO) { margin[TOP] = containing_block->height - - top - border[TOP] - padding[TOP] - + top - border[TOP].width - padding[TOP] - height - padding[BOTTOM] - - border[BOTTOM] - margin[BOTTOM] - + border[BOTTOM].width - margin[BOTTOM] - bottom; } else if (margin[BOTTOM] == AUTO) { margin[BOTTOM] = containing_block->height - - top - margin[TOP] - border[TOP] - + top - margin[TOP] - border[TOP].width - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM] - + padding[BOTTOM] - border[BOTTOM].width - bottom; } else { bottom = containing_block->height - - top - margin[TOP] - border[TOP] - + top - margin[TOP] - border[TOP].width - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM] - + padding[BOTTOM] - border[BOTTOM].width - margin[BOTTOM]; } } else { @@ -3913,50 +4217,51 @@ bool layout_absolute(struct box *box, struct box *containing_block, if (top == AUTO && height == AUTO && bottom != AUTO) { height = box->height; top = containing_block->height - - margin[TOP] - border[TOP] - + margin[TOP] - border[TOP].width - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM] - + padding[BOTTOM] - border[BOTTOM].width - margin[BOTTOM] - bottom; } else if (top == AUTO && height != AUTO && bottom == AUTO) { top = static_top; bottom = containing_block->height - - top - margin[TOP] - border[TOP] - + top - margin[TOP] - border[TOP].width - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM] - + padding[BOTTOM] - border[BOTTOM].width - margin[BOTTOM]; } else if (top != AUTO && height == AUTO && bottom == AUTO) { height = box->height; bottom = containing_block->height - - top - margin[TOP] - border[TOP] - + top - margin[TOP] - border[TOP].width - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM] - + padding[BOTTOM] - border[BOTTOM].width - margin[BOTTOM]; } else if (top == AUTO && height != AUTO && bottom != AUTO) { top = containing_block->height - - margin[TOP] - border[TOP] - + margin[TOP] - border[TOP].width - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM] - + padding[BOTTOM] - border[BOTTOM].width - margin[BOTTOM] - bottom; } else if (top != AUTO && height == AUTO && bottom != AUTO) { height = containing_block->height - - top - margin[TOP] - border[TOP] - + top - margin[TOP] - border[TOP].width - padding[TOP] - padding[BOTTOM] - - border[BOTTOM] - margin[BOTTOM] - + border[BOTTOM].width - margin[BOTTOM] - bottom; } else if (top != AUTO && height != AUTO && bottom == AUTO) { bottom = containing_block->height - - top - margin[TOP] - border[TOP] - + top - margin[TOP] - border[TOP].width - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM] - + padding[BOTTOM] - border[BOTTOM].width - margin[BOTTOM]; } } LOG(("%i + %i + %i + %i + %i + %i + %i + %i + %i = %i", - top, margin[TOP], border[TOP], padding[TOP], height, - padding[BOTTOM], border[BOTTOM], margin[BOTTOM], bottom, + top, margin[TOP], border[TOP].width, padding[TOP], + height, padding[BOTTOM], border[BOTTOM].width, + margin[BOTTOM], bottom, containing_block->height)); - box->y = top + margin[TOP] + border[TOP] - cy; + box->y = top + margin[TOP] + border[TOP].width - cy; if (containing_block->type == BOX_BLOCK || containing_block->type == BOX_INLINE_BLOCK || containing_block->type == BOX_TABLE_CELL) { @@ -3991,49 +4296,67 @@ void layout_compute_offsets(struct box *box, struct box *containing_block, int *top, int *right, int *bottom, int *left) { + uint32_t type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + assert(containing_block->width != UNKNOWN_WIDTH && containing_block->width != AUTO && containing_block->height != AUTO); /* left */ - if (box->style->pos[LEFT].pos == CSS_POS_PERCENT) - *left = ((box->style->pos[LEFT].value.percent * - containing_block->width) / 100); - else if (box->style->pos[LEFT].pos == CSS_POS_LENGTH) - *left = css_len2px(&box->style->pos[LEFT].value.length, - box->style); - else + type = css_computed_left(box->style, &value, &unit); + if (type == CSS_LEFT_SET) { + if (unit == CSS_UNIT_PCT) { + *left = (FIXTOFLT(value) * + containing_block->width) / 100; + } else { + *left = FIXTOINT(nscss_len2px(value, unit, box->style)); + } + } else { *left = AUTO; + } /* right */ - if (box->style->pos[RIGHT].pos == CSS_POS_PERCENT) - *right = ((box->style->pos[RIGHT].value.percent * - containing_block->width) / 100); - else if (box->style->pos[RIGHT].pos == CSS_POS_LENGTH) - *right = css_len2px(&box->style->pos[RIGHT].value.length, - box->style); - else + type = css_computed_right(box->style, &value, &unit); + if (type == CSS_RIGHT_SET) { + if (unit == CSS_UNIT_PCT) { + *right = (FIXTOFLT(value) * + containing_block->width) / 100; + } else { + *right = FIXTOINT(nscss_len2px(value, unit, + box->style)); + } + } else { *right = AUTO; + } /* top */ - if (box->style->pos[TOP].pos == CSS_POS_PERCENT) - *top = ((box->style->pos[TOP].value.percent * - containing_block->height) / 100); - else if (box->style->pos[TOP].pos == CSS_POS_LENGTH) - *top = css_len2px(&box->style->pos[TOP].value.length, - box->style); - else + type = css_computed_top(box->style, &value, &unit); + if (type == CSS_TOP_SET) { + if (unit == CSS_UNIT_PCT) { + *top = (FIXTOFLT(value) * + containing_block->height) / 100; + } else { + *top = FIXTOINT(nscss_len2px(value, unit, box->style)); + } + } else { *top = AUTO; + } /* bottom */ - if (box->style->pos[BOTTOM].pos == CSS_POS_PERCENT) - *bottom = ((box->style->pos[BOTTOM].value.percent * - containing_block->height) / 100); - else if (box->style->pos[BOTTOM].pos == CSS_POS_LENGTH) - *bottom = css_len2px(&box->style->pos[BOTTOM].value.length, - box->style); - else + type = css_computed_bottom(box->style, &value, &unit); + if (type == CSS_BOTTOM_SET) { + if (unit == CSS_UNIT_PCT) { + *bottom = (FIXTOFLT(value) * + containing_block->height) / 100; + } else { + *bottom = FIXTOINT(nscss_len2px(value, unit, + box->style)); + } + } else { *bottom = AUTO; + } } @@ -4056,12 +4379,12 @@ void layout_calculate_descendant_bboxes(struct box *box) assert(0); } - box->descendant_x0 = -box->border[LEFT]; - box->descendant_y0 = -box->border[TOP]; + box->descendant_x0 = -box->border[LEFT].width; + box->descendant_y0 = -box->border[TOP].width; box->descendant_x1 = box->padding[LEFT] + box->width + - box->padding[RIGHT] + box->border[RIGHT]; + box->padding[RIGHT] + box->border[RIGHT].width; box->descendant_y1 = box->padding[TOP] + box->height + - box->padding[BOTTOM] + box->border[BOTTOM]; + box->padding[BOTTOM] + box->border[BOTTOM].width; if (box->type == BOX_INLINE || box->type == BOX_TEXT) return; @@ -4103,7 +4426,8 @@ void layout_calculate_descendant_bboxes(struct box *box) layout_calculate_descendant_bboxes(child); - if (box->style && box->style->overflow == CSS_OVERFLOW_HIDDEN) + if (box->style && css_computed_overflow(box->style) == + CSS_OVERFLOW_HIDDEN) continue; if (child->x + child->descendant_x0 < box->descendant_x0) diff --git a/render/list.c b/render/list.c index 84b5beffc..8f951a857 100644 --- a/render/list.c +++ b/render/list.c @@ -57,9 +57,9 @@ static const int list_counter_decimal[] = { 1, 4, 5, 9, / sizeof(list_counter_decimal[0])) -static struct list_counter *render_list_find_counter(char *name); +static struct list_counter *render_list_find_counter(const char *name); static char *render_list_encode_counter(struct list_counter_state *state, - css_list_style_type style); + enum css_list_style_type style); static char *render_list_encode_roman(int value); /* @@ -72,7 +72,7 @@ static void render_list_counter_output(char *name); * \param name the name of the counter to find * \return the counter, or NULL if it couldn't be found/created. */ -static struct list_counter *render_list_find_counter(char *name) { +static struct list_counter *render_list_find_counter(const char *name) { struct list_counter *counter; assert(name); @@ -131,7 +131,7 @@ void render_list_destroy_counters(void) { * \param value the value to reset the counter to * \return true on success, false on failure. */ -bool render_list_counter_reset(char *name, int value) { +bool render_list_counter_reset(const char *name, int value) { struct list_counter *counter; struct list_counter_state *state; struct list_counter_state *link; @@ -166,7 +166,7 @@ bool render_list_counter_reset(char *name, int value) { * \param value the value to increment the counter by * \return true on success, false on failure. */ -bool render_list_counter_increment(char *name, int value) { +bool render_list_counter_increment(const char *name, int value) { struct list_counter *counter; assert(name); @@ -196,7 +196,7 @@ bool render_list_counter_increment(char *name, int value) { * \param name the name of the counter to end the scope for * \return true on success, false on failure. */ -bool render_list_counter_end_scope(char *name) { +bool render_list_counter_end_scope(const char *name) { struct list_counter *counter; assert(name); @@ -216,28 +216,40 @@ bool render_list_counter_end_scope(char *name) { * \param css_counter the counter to convert * \return a textual representation of the counter, or NULL on failure */ -char *render_list_counter(struct css_counter *css_counter) { +char *render_list_counter(const css_computed_content_item *css_counter) { struct list_counter *counter; struct list_counter_state *state; char *compound = NULL; char *merge, *extend; + lwc_string *name = NULL, *sep = NULL; + uint8_t style; assert(css_counter); - counter = render_list_find_counter(css_counter->name); + + if (css_counter->type == CSS_COMPUTED_CONTENT_COUNTER) { + name = css_counter->data.counter.name; + style = css_counter->data.counter.style; + } else { + assert(css_counter->type == CSS_COMPUTED_CONTENT_COUNTERS); + + name = css_counter->data.counters.name; + sep = css_counter->data.counters.sep; + style = css_counter->data.counters.style; + } + + counter = render_list_find_counter(lwc_string_data(name)); if (!counter) { LOG(("Failed to find/create counter for conversion")); return NULL; } /* handle counter() first */ - if (!css_counter->separator) - return render_list_encode_counter(counter->state, - css_counter->style); + if (sep == NULL) + return render_list_encode_counter(counter->state, style); /* loop through all states for counters() */ for (state = counter->first; state; state = state->next) { - merge = render_list_encode_counter(state, - css_counter->style); + merge = render_list_encode_counter(state, style); if (!merge) { free(compound); return NULL; @@ -258,14 +270,14 @@ char *render_list_counter(struct css_counter *css_counter) { } if (state->next) { merge = realloc(compound, strlen(compound) + - strlen(css_counter->separator) + 1); + lwc_string_length(sep) + 1); if (!merge) { LOG(("No memory for realloc()")); free(compound); return NULL; } compound = merge; - strcat(compound, css_counter->separator); + strcat(compound, lwc_string_data(sep)); } } return compound; @@ -280,7 +292,7 @@ char *render_list_counter(struct css_counter *css_counter) { * \return a textual representation of the counter state, or NULL on failure */ static char *render_list_encode_counter(struct list_counter_state *state, - css_list_style_type style) { + enum css_list_style_type style) { char *result = NULL; int i; @@ -338,10 +350,7 @@ static char *render_list_encode_counter(struct list_counter_state *state, return NULL; result[0] = '\0'; break; - case CSS_LIST_STYLE_TYPE_INHERIT: - case CSS_LIST_STYLE_TYPE_UNKNOWN: - case CSS_LIST_STYLE_TYPE_NOT_SET: - assert(0); + default: break; } diff --git a/render/list.h b/render/list.h index 5206a3cc6..626eb5941 100644 --- a/render/list.h +++ b/render/list.h @@ -25,11 +25,13 @@ #include <stdbool.h> +#include "css/css.h" + void render_list_destroy_counters(void); -bool render_list_counter_reset(char *name, int value); -bool render_list_counter_increment(char *name, int value); -bool render_list_counter_end_scope(char *name); -char *render_list_counter(struct css_counter *css_counter); +bool render_list_counter_reset(const char *name, int value); +bool render_list_counter_increment(const char *name, int value); +bool render_list_counter_end_scope(const char *name); +char *render_list_counter(const css_computed_content_item *css_counter); void render_list_test(void); diff --git a/render/loosen.c b/render/loosen.c deleted file mode 100644 index 85136d89b..000000000 --- a/render/loosen.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * Copyright 2008 Adam Blokus <adamblokus@gmail.com> - * - * This file is part of NetSurf, http://www.netsurf-browser.org/ - * - * NetSurf is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * NetSurf is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdbool.h> -#include <assert.h> - -#include "content/content.h" - -#include "render/box.h" -#include "render/font.h" - -#include "render/layout.h" -#include "render/loosen.h" - -#include "utils/log.h" -#include "utils/talloc.h" - -#define AUTO INT_MIN -#define LOOSEN_MIN_TEXT_SIZE 10 - -static bool loosen_text(struct box *text, int width, struct content *content); - -static bool loosen_table(struct box *box, int available_width, - struct content *content); - -static bool loosen_position_static(struct box *box, int width, int cx, - struct content *content); - -static bool loosen_shrink_object(struct box *box, int width); - -static bool loosen_all_first_pass(struct box *box, int width, int cx, - struct content *content); -static bool loosen_all_second_pass(struct box *box, int width, int cx, - struct content *content); -static bool loosen_all_margins_paddings(struct box *box, int width, int cx, - struct content *content); - -static bool loosen_shrink_text(struct box *box); - -/** - * Main loosing procedure - * \param content Reformated content - talloc memory pool for new boxes - * \param layout Root of the loosened box tree - * \param width Width the content is intended to fit - * \param height Height of a single page - to be taken into consideration for \ - * preventing elements for being cropped at top/bottom edges of pages. - * \return true if successful, false otherwise (lack of memory) -*/ -bool loosen_document_layout(struct content *content, struct box *layout, - int width, int height) -{ - /* Optional try - if the current layout is not more than xx% too wide, - * maybe we scale the content to preserve the original layout? - */ - - if (!loosen_all_first_pass(layout, width, 0, content)) - return false; - layout->min_width = 0; - layout->max_width = UNKNOWN_MAX_WIDTH; - content_reformat(content, width, 0); - - /*Check if pass 1 was enough - if re-layouting doesn't give - *us the right width, go on to pass 2. And again - if pass 2 was not - *enough - go on to pass 3 - */ - - if (content->width > width) { - if (!loosen_all_second_pass(layout, width, 0, content)) - return false; - layout->min_width = 0; - layout->max_width = UNKNOWN_MAX_WIDTH; - content_reformat(content, width, 0); - } - - if (content->width > width) { - if (!loosen_all_margins_paddings(layout, width, 0, content)) - return false; - layout->min_width = 0; - layout->max_width = UNKNOWN_MAX_WIDTH; - content_reformat(content, width, 0); - } - - return true; -} - -/** Primarily - break too wide words into pieces. - * \param text - the box that contains text to be broken - * \param width Width the content is intended to fit - * \param content talloc memory pool for new boxes - * \return true if successful, false otherwise -*/ -bool loosen_text(struct box *text, int width, struct content *content) -{ - size_t offset; - int actual_x; - - int *breaks; - int break_count, i; - - unsigned int position; - const struct font_functions *font_func; - - plot_font_style_t fstyle; - - font_plot_style_from_css(text->style, &fstyle); - - if (content->type == CONTENT_HTML) - font_func = content->data.html.font_func; - else - return false; - - if (text->width <= width) { - LOG(("loosen_text called unnecessary?")); - /*Still - not an error for this function*/ - return true; - } - - breaks = malloc( sizeof(int) * text->length); - if (breaks == NULL) - return false; - - break_count = 0; - position = 0; - - while (position < text->length) { - font_func->font_position_in_string(&fstyle, - text->text + position, - text->length - position, - width, &offset, &actual_x); - - if (offset < text->length - position) { - /*Another break*/ - LOG(("Current text broken at offset %zu", - position + offset)); - breaks[break_count++] = position + offset-1; - } - - position += offset; - } - - text->text = talloc_realloc(content, text->text, char, - text->length + break_count); - - i = text->length-1; - text->length = text->length + break_count; - - for (; i>=0; i--) { - text->text[i + break_count] = text->text[i]; - if (i == breaks[break_count - 1]) { - break_count--; - text->text[i + break_count] = ' '; - } - } - - free(breaks); - - return true; -} - -/** - * Changing table layout and structure to fit the contents width. - * Firstly the borders are collapsed and the text is shrunken. - * Secondly the text is loosened( this can be helpful for all data tables which - * contain only text) - * In the most extreme case - the table has no influence on the width - * (each row is broken into one-cell rows). - * \param table - the box that contains table to be broken - * \param width Width the content is intended to fit - * \param content talloc memory pool for new boxes - * \return true if successful, false otherwise - */ -bool loosen_table(struct box *table, int width, struct content *content) -{ - struct box *row_group, *row, *cell, *br, *prev, *inline_container; - - struct box *text, *child; - const struct font_functions *font_func; - float scale; - int new_width; - - if (table->min_width <= width) - return true; - - if (content->type == CONTENT_HTML) - font_func = content->data.html.font_func; - else - return false; - - table->style->border_collapse = CSS_BORDER_COLLAPSE_COLLAPSE; - - if (!loosen_shrink_text(table)) - return false; - - if (!loosen_all_margins_paddings(table, width, 0, content)) - return false; - - scale = width; - scale /= table->min_width; - - for (row_group = table->children; row_group; - row_group = row_group->next) { - for (row = row_group->children; row; row = row->next) { - for (cell = row->children; cell; cell = cell->next) { - for (child = cell->children; child; - child = child->next) { - if (child->children) - text = child->children; - else - continue; - - /*text in nested boxes won't be broken*/ - if (text->type != BOX_TEXT) - continue; - - - /*break the words propotionally to the - current cell width*/ - new_width = (float)cell->width * scale * 0.9; - loosen_text(text, new_width, content); - } - } - } - } - - - /*check if the table is loosend enough...*/ - layout_minmax_table(table, font_func); - if (table->min_width <= width) - return true; - - - /*...in case it's not continue with bigger changes, - table cells are changed into inline containers*/ - inline_container = box_create(0, 0, 0, 0, 0, content); - inline_container->type = BOX_INLINE_CONTAINER; - inline_container->parent = table; - inline_container->style = talloc_memdup(content, table->style, - sizeof *table->style); - - prev = NULL; - - for (row_group = table->children; row_group; - row_group = row_group->next) { - for (row = row_group->children; row; row = row->next) { - - for (cell = row->children; cell; cell = cell->next) { - cell->type = BOX_INLINE_BLOCK; - cell->prev = prev; - cell->parent = inline_container; - cell->max_width = width; - cell->min_width = 0; - - if (prev!=NULL) - prev->next = cell; - else - inline_container->children = cell; - - prev = cell; - } - - br = box_create(0, 0, 0, 0, 0, content); - br->type = BOX_BR; - br->parent = inline_container; - br->prev = prev; - br->style = talloc_memdup(content, table->style, - sizeof *table->style); - br->style->clear = CSS_CLEAR_BOTH; - - if (prev != NULL) - prev->next = br; - else - inline_container->children = br; - - prev = br; - } - } - inline_container->last = prev; - - table->type = BOX_BLOCK; - table->children = table->last = inline_container; - table->col = NULL; - - return true; -} - -/** -* Recursively step through the box tree applying LOOSEN_MIN_TEXT_SIZE wherever -* text is found -* \param box the box where the shrinking should be started -* \return true if successful, false otherwise -*/ -bool loosen_shrink_text(struct box *box) -{ - struct box *child; - - box->max_width = UNKNOWN_MAX_WIDTH; - - if (box->type == BOX_TEXT) { - box->style->font_size.size = CSS_FONT_SIZE_LENGTH; - box->style->font_size.value.length.unit = CSS_UNIT_PX; - box->style->font_size.value.length.value = LOOSEN_MIN_TEXT_SIZE; - } - else if (box->children) - for(child = box->children; child; child = child->next) - if (!loosen_shrink_text(child)) - return false; - - return true; -} - - -/** - * Change absolute and relative positioned elements into block elements - * in case they are positioned to far to the rigth - * \param box - the box that should be changed - * \param width Width the content is intended to fit - * \param cx current x - not yet in use - * \param content talloc memory pool for new boxes - * \return true if successful, false otherwise - */ -bool loosen_position_static(struct box *box, int width, int cx, - struct content *content) -{ - assert(box->style); - - if (box->style->position == CSS_POSITION_ABSOLUTE) { - box->style->position = CSS_POSITION_NOT_SET; - } - - return true; -} - -/** - * Shrink an object (esp. an image) to fit the page-width - * \note Not sure wheter it won't be better for images to be cropped - * \param box - the box that should be changed - * \param width Width the content is intended to fit - * \return true if successful, false otherwise -*/ -bool loosen_shrink_object(struct box *box, int width) -{ - assert(box->object != NULL); - - box->height = AUTO; - box->width = width; - - if (box->style) { - box->style->width.width = CSS_WIDTH_PERCENT; - box->style->width.value.percent = 100; - box->style->height.height= CSS_HEIGHT_AUTO; - } - - return true; -} - -/** - * Pass 1 of loosening - do such obvious changes as: breaking too long words, - * moving absolute positioned objects into the visibile scope of width. - * \param box - the box that should be changed - * \param width Width the content is intended to fit - * \param cx current x - not yet in use - * \param content talloc memory pool for new boxes - * \return true if successful, false otherwise -*/ -bool loosen_all_first_pass(struct box *box, int width, int cx, - struct content *content) -{ - struct box* c; - int x; - - for (c = box->children; c ; c = c->next) { - x = cx + c->x; - if (c->children != NULL) - if (!loosen_all_first_pass(c, width, x, content)) - return false; - - if (c->style) { - if (c->style->position == CSS_POSITION_RELATIVE || - c->style->position == CSS_POSITION_ABSOLUTE ) - if (!loosen_position_static(c, width, cx, content)) - return false; - if ( c->style->width.width == CSS_WIDTH_LENGTH && - css_len2px(&c->style->width.value.length, c->style) > width) - c->style->width.width = CSS_WIDTH_NOT_SET; - } - - if (c->object && c->width > width) - if (!loosen_shrink_object(c, width)) - return false; - - if (c->type == BOX_TEXT) { - if (!loosen_text(c, width, content)) - return false; - } - - c->min_width = 0; - c->max_width = UNKNOWN_MAX_WIDTH; - - } - - return true; -} - -/** - * Pass 2 of loosening - break tables - * \param box - the box that should be changed - * \param width Width the content is intended to fit - * \param cx current x - not yet in use - * \param content talloc memory pool for new boxes - * \return true if successful, false otherwise - */ -bool loosen_all_second_pass(struct box *box, int width, int cx, - struct content *content) -{ - struct box *c; - int x; - - for (c = box->children; c; c = c->next) { - x = cx + c->x; - if (c->children != NULL) - if (!loosen_all_second_pass(c, width, x, content)) - return false; - - switch (c->type) { - case BOX_TABLE: - if (!loosen_table(c, width, content)) - return false; - break; - default: - break; - } - - c->min_width = 0; - c->max_width = UNKNOWN_MAX_WIDTH; - } - - return true; -} - - -/** - * Pass 3 of loosening -zero all margins and paddings - * \param box - the box that should be changed - * \param width Width the content is intended to fit - * \param cx current x - not yet in use - * \param content talloc memory pool for new boxes - * \return true if successful, false otherwise - */ -bool loosen_all_margins_paddings(struct box *box, int width, int cx, - struct content *content) -{ - struct box *c; - int x; - - for (c = box->children; c; c = c->next) { - x = cx + c->x; - if (c->children != NULL) - if (!loosen_all_margins_paddings(c, width, x, content)) - return false; - - c->padding[LEFT] = c->padding[RIGHT] = 0; - c->margin[LEFT] = c->margin[RIGHT] = 0; - - if (c->style) { - c->style->margin[LEFT].margin = CSS_MARGIN_PERCENT; - c->style->margin[LEFT].value.percent = 0; - - c->style->margin[RIGHT].margin = CSS_MARGIN_PERCENT; - c->style->margin[RIGHT].value.percent = 0; - - c->style->padding[LEFT].padding = CSS_PADDING_PERCENT; - c->style->padding[LEFT].value.percent = 0; - - c->style->padding[RIGHT].padding = CSS_PADDING_PERCENT; - c->style->padding[RIGHT].value.percent = 0; - - } - - c->min_width = 0; - c->max_width = UNKNOWN_MAX_WIDTH; - - } - - return true; -} - diff --git a/render/loosen.h b/render/loosen.h deleted file mode 100644 index a5b3822bd..000000000 --- a/render/loosen.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2008 Adam Blokus <adamblokus@gmail.com> - * - * This file is part of NetSurf, http://www.netsurf-browser.org/ - * - * NetSurf is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * NetSurf is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/** -\file -General idea - a set of routines working themselves recursively through -the box tree and trying to change the layout of the document as little -as possible to acquire the desired width ( - to make it fit in a printed -page ), where possible - also taking the dividing height into consideration, -to prevent objects being cut by ends of pages. -*/ - -#ifndef NETSURF_RENDER_LOOSEN_H -#define NETSURF_RENDER_LOOSEN_H -#include <stdbool.h> - -bool loosen_document_layout(struct content *content, struct box *layout, - int width, int height); - -#endif diff --git a/render/parser_binding.h b/render/parser_binding.h index d50b4e3b9..1641058bd 100644 --- a/render/parser_binding.h +++ b/render/parser_binding.h @@ -39,6 +39,12 @@ typedef enum binding_encoding_source { ENCODING_SOURCE_META } binding_encoding_source; +typedef enum binding_quirks_mode { + BINDING_QUIRKS_MODE_NONE, + BINDING_QUIRKS_MODE_LIMITED, + BINDING_QUIRKS_MODE_FULL +} binding_quirks_mode; + binding_error binding_create_tree(void *arena, const char *charset, void **ctx); binding_error binding_destroy_tree(void *ctx); @@ -46,7 +52,7 @@ binding_error binding_parse_chunk(void *ctx, const uint8_t *data, size_t len); binding_error binding_parse_completed(void *ctx); const char *binding_get_encoding(void *ctx, binding_encoding_source *source); -xmlDocPtr binding_get_document(void *ctx); +xmlDocPtr binding_get_document(void *ctx, binding_quirks_mode *quirks); struct form *binding_get_forms(void *ctx); struct form_control *binding_get_control_for_node(void *ctx, xmlNodePtr node); diff --git a/render/table.c b/render/table.c index d3134f687..aad3e38c2 100644 --- a/render/table.c +++ b/render/table.c @@ -23,6 +23,7 @@ #include <assert.h> #include "css/css.h" +#include "css/utils.h" #include "render/box.h" #include "render/table.h" #define NDEBUG @@ -30,15 +31,29 @@ #undef NDEBUG #include "utils/talloc.h" - -static void table_collapse_borders_h(struct box *parent, struct box *child, - bool *first); -static void table_collapse_borders_v(struct box *row, struct box *cell, - unsigned int columns); -static void table_collapse_borders_cell(struct box *cell, struct box *right, - struct box *bottom); -static void table_remove_borders(struct css_style *style); -struct box *table_find_cell(struct box *table, unsigned int x, unsigned int y); +/** + * Container for border values during table border calculations + */ +struct border { + enum css_border_style style; /**< border-style */ + enum css_border_color color; /**< border-color type */ + css_color c; /**< border-color value */ + css_fixed width; /**< border-width length */ + css_unit unit; /**< border-width units */ +}; + +static void table_used_left_border_for_cell(struct box *cell); +static void table_used_top_border_for_cell(struct box *cell); +static void table_used_right_border_for_cell(struct box *cell); +static void table_used_bottom_border_for_cell(struct box *cell); +static bool table_border_is_more_eyecatching(const struct border *a, + box_type a_src, const struct border *b, box_type b_src); +static void table_cell_top_process_table(struct box *table, struct border *a, + box_type *a_src); +static bool table_cell_top_process_group(struct box *cell, struct box *group, + struct border *a, box_type *a_src); +static bool table_cell_top_process_row(struct box *cell, struct box *row, + struct border *a, box_type *a_src); /** @@ -75,6 +90,10 @@ bool table_calculate_column_types(struct box *table) for (row_group = table->children; row_group; row_group =row_group->next) for (row = row_group->children; row; row = row->next) for (cell = row->children; cell; cell = cell->next) { + enum css_width type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; + assert(cell->type == BOX_TABLE_CELL); assert(cell->style); @@ -82,17 +101,21 @@ bool table_calculate_column_types(struct box *table) continue; i = cell->start_column; - if (cell->style->position != CSS_POSITION_ABSOLUTE && - cell->style->position != CSS_POSITION_FIXED) { + if (css_computed_position(cell->style) != + CSS_POSITION_ABSOLUTE && + css_computed_position(cell->style) != + CSS_POSITION_FIXED) { col[i].positioned = false; } + type = css_computed_width(cell->style, &value, &unit); + /* fixed width takes priority over any other width type */ if (col[i].type != COLUMN_WIDTH_FIXED && - cell->style->width.width == CSS_WIDTH_LENGTH) { + type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT) { col[i].type = COLUMN_WIDTH_FIXED; - col[i].width = css_len2px(&cell->style-> - width.value.length, cell->style); + col[i].width = FIXTOINT(nscss_len2px(value, unit, + cell->style)); if (col[i].width < 0) col[i].width = 0; continue; @@ -101,12 +124,12 @@ bool table_calculate_column_types(struct box *table) if (col[i].type != COLUMN_WIDTH_UNKNOWN) continue; - if (cell->style->width.width == CSS_WIDTH_PERCENT) { + if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT) { col[i].type = COLUMN_WIDTH_PERCENT; - col[i].width = cell->style->width.value.percent; + col[i].width = FIXTOINT(value); if (col[i].width < 0) col[i].width = 0; - } else if (cell->style->width.width == CSS_WIDTH_AUTO) { + } else if (type == CSS_WIDTH_AUTO) { col[i].type = COLUMN_WIDTH_AUTO; } } @@ -118,6 +141,9 @@ bool table_calculate_column_types(struct box *table) unsigned int fixed_columns = 0, percent_columns = 0, auto_columns = 0, unknown_columns = 0; int fixed_width = 0, percent_width = 0; + enum css_width type; + css_fixed value = 0; + css_unit unit = CSS_UNIT_PX; if (cell->columns == 1) continue; @@ -145,14 +171,16 @@ bool table_calculate_column_types(struct box *table) if (!unknown_columns) continue; + type = css_computed_width(cell->style, &value, &unit); + /* if cell is fixed width, and all spanned columns are fixed * or unknown width, split extra width among unknown columns */ - if (cell->style->width.width == CSS_WIDTH_LENGTH && + if (type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT && fixed_columns + unknown_columns == cell->columns) { - int width = (css_len2px(&cell->style-> - width.value.length, cell->style) - - fixed_width) / unknown_columns; + int width = (FIXTOFLT(nscss_len2px(value, unit, + cell->style)) - fixed_width) / + unknown_columns; if (width < 0) width = 0; for (j = 0; j != cell->columns; j++) { @@ -164,10 +192,10 @@ bool table_calculate_column_types(struct box *table) } /* as above for percentage width */ - if (cell->style->width.width == CSS_WIDTH_PERCENT && + if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT && percent_columns + unknown_columns == cell->columns) { - int width = (cell->style->width.value.percent - + int width = (FIXTOFLT(value) - percent_width) / unknown_columns; if (width < 0) width = 0; @@ -195,212 +223,734 @@ bool table_calculate_column_types(struct box *table) return true; } - /** - * Handle collapsing border model. + * Calculate used values of border-{trbl}-{style,color,width} for table cells. * - * \param table box of type BOX_TABLE + * \param cell Table cell to consider + * + * \post \a cell's border array is populated */ +void table_used_border_for_cell(struct box *cell) +{ + int side; + + assert(cell->type == BOX_TABLE_CELL); + + if (css_computed_border_collapse(cell->style) == + CSS_BORDER_COLLAPSE_SEPARATE) { + css_fixed width = 0; + css_unit unit = CSS_UNIT_PX; + + /* Left border */ + cell->border[LEFT].style = + css_computed_border_left_style(cell->style); + cell->border[LEFT].color = + css_computed_border_left_color(cell->style, + &cell->border[LEFT].c); + css_computed_border_left_width(cell->style, &width, &unit); + cell->border[LEFT].width = + FIXTOINT(nscss_len2px(width, unit, cell->style)); + + /* Top border */ + cell->border[TOP].style = + css_computed_border_top_style(cell->style); + cell->border[TOP].color = + css_computed_border_top_color(cell->style, + &cell->border[TOP].c); + css_computed_border_top_width(cell->style, &width, &unit); + cell->border[TOP].width = + FIXTOINT(nscss_len2px(width, unit, cell->style)); + + /* Right border */ + cell->border[RIGHT].style = + css_computed_border_right_style(cell->style); + cell->border[RIGHT].color = + css_computed_border_right_color(cell->style, + &cell->border[RIGHT].c); + css_computed_border_right_width(cell->style, &width, &unit); + cell->border[RIGHT].width = + FIXTOINT(nscss_len2px(width, unit, cell->style)); + + /* Bottom border */ + cell->border[BOTTOM].style = + css_computed_border_bottom_style(cell->style); + cell->border[BOTTOM].color = + css_computed_border_bottom_color(cell->style, + &cell->border[BOTTOM].c); + css_computed_border_bottom_width(cell->style, &width, &unit); + cell->border[BOTTOM].width = + FIXTOINT(nscss_len2px(width, unit, cell->style)); + } else { + /* Left border */ + table_used_left_border_for_cell(cell); + + /* Top border */ + table_used_top_border_for_cell(cell); + + /* Right border */ + table_used_right_border_for_cell(cell); + + /* Bottom border */ + table_used_bottom_border_for_cell(cell); + } + + /* Finally, ensure that any borders configured as + * hidden or none have zero width. (c.f. layout_find_dimensions) */ + for (side = 0; side != 4; side++) { + if (cell->border[side].style == CSS_BORDER_STYLE_HIDDEN || + cell->border[side].style == + CSS_BORDER_STYLE_NONE) + cell->border[side].width = 0; + } +} + +/****************************************************************************** + * Helpers for used border calculations * + ******************************************************************************/ -void table_collapse_borders(struct box *table) +/** + * Calculate used values of border-left-{style,color,width} + * + * \param cell Table cell to consider + */ +void table_used_left_border_for_cell(struct box *cell) { - bool first; - unsigned int i, j; - struct box *row_group, *row, *cell; + struct border a, b; + box_type a_src, b_src; + + /** \todo Need column and column_group, too */ + + /* Initialise to computed left border for cell */ + a.style = css_computed_border_left_style(cell->style); + a.color = css_computed_border_left_color(cell->style, &a.c); + css_computed_border_left_width(cell->style, &a.width, &a.unit); + a_src = BOX_TABLE_CELL; + + if (cell->prev != NULL || cell->start_column != 0) { + /* Cell to the left -- consider its right border */ + struct box *prev = NULL; + + if (cell->prev == NULL) { + struct box *row; + + /* Spanned from a previous row */ + for (row = cell->parent; row != NULL; row = row->prev) { + for (prev = row->children; prev != NULL; + prev = prev->next) { + if (prev->start_column + + prev->columns == + cell->start_column) + break; + } - assert(table->type == BOX_TABLE); - - /* 1st stage: collapse all borders down to the cells */ - first = true; - for (row_group = table->children; row_group; - row_group = row_group->next) { - assert(row_group->type == BOX_TABLE_ROW_GROUP); - assert(row_group->style); - table_collapse_borders_h(table, row_group, &first); - first = row_group->children != NULL; - for (row = row_group->children; row; row = row->next) { - assert(row->type == BOX_TABLE_ROW); - assert(row->style); - table_collapse_borders_h(row_group, row, &first); - for (cell = row->children; cell; cell = cell->next) { - assert(cell->type == BOX_TABLE_CELL); - assert(cell->style); - table_collapse_borders_v(row, cell, - table->columns); + if (prev != NULL) + break; } - table_remove_borders(row->style); + + assert(prev != NULL); + } else { + prev = cell->prev; + } + + b.style = css_computed_border_right_style(prev->style); + b.color = css_computed_border_right_color(prev->style, &b.c); + css_computed_border_right_width(prev->style, &b.width, &b.unit); + b_src = BOX_TABLE_CELL; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } + } else { + /* First cell in row, so consider rows and row group */ + struct box *row = cell->parent; + struct box *group = row->parent; + struct box *table = group->parent; + unsigned int rows = cell->rows; + + while (rows-- > 0 && row != NULL) { + /* Spanned rows -- consider their left border */ + b.style = css_computed_border_left_style(row->style); + b.color = css_computed_border_left_color( + row->style, &b.c); + css_computed_border_left_width( + row->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW; + + if (table_border_is_more_eyecatching(&a, a_src, + &b, b_src)) { + a = b; + a_src = b_src; + } + + row = row->next; + } + + /** \todo can cells span row groups? */ + + /* Row group -- consider its left border */ + b.style = css_computed_border_left_style(group->style); + b.color = css_computed_border_left_color(group->style, &b.c); + css_computed_border_left_width(group->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } + + /* The table itself -- consider its left border */ + b.style = css_computed_border_left_style(table->style); + b.color = css_computed_border_left_color(table->style, &b.c); + css_computed_border_left_width(table->style, &b.width, &b.unit); + b_src = BOX_TABLE; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; } - table_remove_borders(row_group->style); } - table_remove_borders(table->style); - - /* 2nd stage: rather than building a grid of cells, we slowly look up the - * cell we want to collapse with */ - for (i = 0; i < table->columns; i++) { - for (j = 0; j < table->rows; j++) { - table_collapse_borders_cell( - table_find_cell(table, i, j), - table_find_cell(table, i + 1, j), - table_find_cell(table, i, j + 1)); + + /* a now contains the used left border for the cell */ + cell->border[LEFT].style = a.style; + cell->border[LEFT].color = a.color; + cell->border[LEFT].c = a.c; + cell->border[LEFT].width = + FIXTOINT(nscss_len2px(a.width, a.unit, cell->style)); +} + +/** + * Calculate used values of border-top-{style,color,width} + * + * \param cell Table cell to consider + */ +void table_used_top_border_for_cell(struct box *cell) +{ + struct border a, b; + box_type a_src, b_src; + struct box *row = cell->parent; + bool process_group = false; + + /* Initialise to computed top border for cell */ + a.style = css_computed_border_top_style(cell->style); + a.color = css_computed_border_top_color(cell->style, &a.c); + css_computed_border_top_width(cell->style, &a.width, &a.unit); + a_src = BOX_TABLE_CELL; + + /* Top border of row */ + b.style = css_computed_border_top_style(row->style); + b.color = css_computed_border_top_color(row->style, &b.c); + css_computed_border_top_width(row->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } + + if (row->prev != NULL) { + /* Consider row(s) above */ + while (table_cell_top_process_row(cell, row->prev, + &a, &a_src) == false) { + if (row->prev->prev == NULL) { + /* Consider row group */ + process_group = true; + break; + } else { + row = row->prev; + } } + } else { + process_group = true; } - /* 3rd stage: remove redundant borders */ - first = true; - for (row_group = table->children; row_group; - row_group = row_group->next) { - for (row = row_group->children; row; row = row->next) { - for (cell = row->children; cell; cell = cell->next) { - if (!first) { - cell->style->border[TOP].style = - CSS_BORDER_STYLE_NONE; - cell->style->border[TOP].width.value.value = - 0; - cell->style->border[TOP].width.value.unit = - CSS_UNIT_PX; - } - if (cell->start_column > 0) { - cell->style->border[LEFT].style = - CSS_BORDER_STYLE_NONE; - cell->style->border[LEFT].width.value.value = - 0; - cell->style->border[LEFT].width.value.unit = - CSS_UNIT_PX; + if (process_group) { + struct box *group = row->parent; + + /* Top border of row group */ + b.style = css_computed_border_top_style(group->style); + b.color = css_computed_border_top_color(group->style, &b.c); + css_computed_border_top_width(group->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } + + if (group->prev == NULL) { + /* Top border of table */ + table_cell_top_process_table(group->parent, &a, &a_src); + } else { + /* Process previous group(s) */ + while (table_cell_top_process_group(cell, group->prev, + &a, &a_src) == false) { + if (group->prev->prev == NULL) { + /* Top border of table */ + table_cell_top_process_table( + group->parent, + &a, &a_src); + break; + } else { + group = group->prev; } } - first = false; } } -} + /* a now contains the used top border for the cell */ + cell->border[TOP].style = a.style; + cell->border[TOP].color = a.color; + cell->border[TOP].c = a.c; + cell->border[TOP].width = + FIXTOINT(nscss_len2px(a.width, a.unit, cell->style)); +} /** - * Collapse the borders of two boxes together. + * Calculate used values of border-right-{style,color,width} + * + * \param cell Table cell to consider */ - -void table_collapse_borders_v(struct box *row, struct box *cell, unsigned int columns) +void table_used_right_border_for_cell(struct box *cell) { - struct css_border *border; + struct border a, b; + box_type a_src, b_src; + + /** \todo Need column and column_group, too */ + + /* Initialise to computed right border for cell */ + a.style = css_computed_border_right_style(cell->style); + a.color = css_computed_border_right_color(cell->style, &a.c); + css_computed_border_right_width(cell->style, &a.width, &a.unit); + a_src = BOX_TABLE_CELL; + + if (cell->next != NULL || cell->start_column + cell->columns != + cell->parent->parent->parent->columns) { + /* Cell is not at right edge of table -- no right border */ + a.style = CSS_BORDER_STYLE_NONE; + a.width = 0; + a.unit = CSS_UNIT_PX; + } else { + /* Last cell in row, so consider rows and row group */ + struct box *row = cell->parent; + struct box *group = row->parent; + struct box *table = group->parent; + unsigned int rows = cell->rows; + + while (rows-- > 0 && row != NULL) { + /* Spanned rows -- consider their right border */ + b.style = css_computed_border_right_style(row->style); + b.color = css_computed_border_right_color( + row->style, &b.c); + css_computed_border_right_width( + row->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW; + + if (table_border_is_more_eyecatching(&a, a_src, + &b, b_src)) { + a = b; + a_src = b_src; + } - if (cell->start_column == 0) { - border = css_eyecatching_border(&row->style->border[LEFT], row->style, - &cell->style->border[LEFT], cell->style); - cell->style->border[LEFT] = *border; - } - border = css_eyecatching_border(&row->style->border[TOP], row->style, - &cell->style->border[TOP], cell->style); - cell->style->border[TOP] = *border; - border = css_eyecatching_border(&row->style->border[BOTTOM], row->style, - &cell->style->border[BOTTOM], cell->style); - cell->style->border[BOTTOM] = *border; - if ((cell->start_column + cell->columns) == columns) { - border = css_eyecatching_border(&row->style->border[RIGHT], row->style, - &cell->style->border[RIGHT], cell->style); - cell->style->border[RIGHT] = *border; + row = row->next; + } + + /** \todo can cells span row groups? */ + + /* Row group -- consider its right border */ + b.style = css_computed_border_right_style(group->style); + b.color = css_computed_border_right_color(group->style, &b.c); + css_computed_border_right_width(group->style, + &b.width, &b.unit); + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } + + /* The table itself -- consider its right border */ + b.style = css_computed_border_right_style(table->style); + b.color = css_computed_border_right_color(table->style, &b.c); + css_computed_border_right_width(table->style, + &b.width, &b.unit); + b_src = BOX_TABLE; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } } -} + /* a now contains the used right border for the cell */ + cell->border[RIGHT].style = a.style; + cell->border[RIGHT].color = a.color; + cell->border[RIGHT].c = a.c; + cell->border[RIGHT].width = + FIXTOINT(nscss_len2px(a.width, a.unit, cell->style)); +} /** - * Collapse the borders of two boxes together. + * Calculate used values of border-bottom-{style,color,width} + * + * \param cell Table cell to consider */ - -void table_collapse_borders_h(struct box *parent, struct box *child, bool *first) +void table_used_bottom_border_for_cell(struct box *cell) { - struct css_border *border; + struct border a, b; + box_type a_src, b_src; + struct box *row = cell->parent; + unsigned int rows = cell->rows; + + /* Initialise to computed bottom border for cell */ + a.style = css_computed_border_bottom_style(cell->style); + a.color = css_computed_border_bottom_color(cell->style, &a.c); + css_computed_border_bottom_width(cell->style, &a.width, &a.unit); + a_src = BOX_TABLE_CELL; + + while (rows-- > 0 && row != NULL) + row = row->next; + + /** \todo Can cells span row groups? */ + + if (row != NULL) { + /* Cell is not at bottom edge of table -- no bottom border */ + a.style = CSS_BORDER_STYLE_NONE; + a.width = 0; + a.unit = CSS_UNIT_PX; + } else { + /* Cell at bottom of table, so consider row and row group */ + struct box *row = cell->parent; + struct box *group = row->parent; + struct box *table = group->parent; + + /* Bottom border of row */ + b.style = css_computed_border_bottom_style(row->style); + b.color = css_computed_border_bottom_color(row->style, &b.c); + css_computed_border_bottom_width(row->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } - if (*first) { - border = css_eyecatching_border(&parent->style->border[TOP], parent->style, - &child->style->border[TOP], child->style); - child->style->border[TOP] = *border; - *first = false; - } - border = css_eyecatching_border(&parent->style->border[LEFT], parent->style, - &child->style->border[LEFT], child->style); - child->style->border[LEFT] = *border; - border = css_eyecatching_border(&parent->style->border[RIGHT], parent->style, - &child->style->border[RIGHT], child->style); - child->style->border[RIGHT] = *border; - if (!child->next) { - border = css_eyecatching_border(&parent->style->border[BOTTOM], parent->style, - &child->style->border[BOTTOM], child->style); - child->style->border[BOTTOM] = *border; + /* Row group -- consider its bottom border */ + b.style = css_computed_border_bottom_style(group->style); + b.color = css_computed_border_bottom_color(group->style, &b.c); + css_computed_border_bottom_width(group->style, + &b.width, &b.unit); + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } + + /* The table itself -- consider its bottom border */ + b.style = css_computed_border_bottom_style(table->style); + b.color = css_computed_border_bottom_color(table->style, &b.c); + css_computed_border_bottom_width(table->style, + &b.width, &b.unit); + b_src = BOX_TABLE; + + if (table_border_is_more_eyecatching(&a, a_src, &b, b_src)) { + a = b; + a_src = b_src; + } } -} + /* a now contains the used bottom border for the cell */ + cell->border[BOTTOM].style = a.style; + cell->border[BOTTOM].color = a.color; + cell->border[BOTTOM].c = a.c; + cell->border[BOTTOM].width = + FIXTOINT(nscss_len2px(a.width, a.unit, cell->style)); +} /** - * Collapse the borders of two boxes together. + * Determine if a border style is more eyecatching than another + * + * \param a Reference border style + * \param a_src Source of \a a + * \param b Candidate border style + * \param b_src Source of \a b + * \return True if \a b is more eyecatching than \a a */ +bool table_border_is_more_eyecatching(const struct border *a, + box_type a_src, const struct border *b, box_type b_src) +{ + css_fixed awidth, bwidth; + int impact = 0; -void table_collapse_borders_cell(struct box *cell, struct box *right, - struct box *bottom) { - struct css_border *border; + /* See CSS 2.1 $17.6.2.1 */ - if (!cell) - return; + /* 1 + 2 -- hidden beats everything, none beats nothing */ + if (a->style == CSS_BORDER_STYLE_HIDDEN || + b->style == CSS_BORDER_STYLE_NONE) + return false; + + if (b->style == CSS_BORDER_STYLE_HIDDEN || + a->style == CSS_BORDER_STYLE_NONE) + return true; + + /* 3a -- wider borders beat narrow ones */ + /* The widths must be absolute, which will be the case + * if they've come from a computed style. */ + assert(a->unit != CSS_UNIT_EM && a->unit != CSS_UNIT_EX); + assert(b->unit != CSS_UNIT_EM && b->unit != CSS_UNIT_EX); + awidth = nscss_len2px(a->width, a->unit, NULL); + bwidth = nscss_len2px(b->width, b->unit, NULL); - if ((right) && (right != cell)) { - border = css_eyecatching_border(&cell->style->border[RIGHT], cell->style, - &right->style->border[LEFT], right->style); - cell->style->border[RIGHT] = *border; + if (awidth < bwidth) + return true; + else if (bwidth < awidth) + return false; + /* 3b -- sort by style */ + switch (a->style) { + case CSS_BORDER_STYLE_DOUBLE: impact++; + case CSS_BORDER_STYLE_SOLID: impact++; + case CSS_BORDER_STYLE_DASHED: impact++; + case CSS_BORDER_STYLE_DOTTED: impact++; + case CSS_BORDER_STYLE_RIDGE: impact++; + case CSS_BORDER_STYLE_OUTSET: impact++; + case CSS_BORDER_STYLE_GROOVE: impact++; + case CSS_BORDER_STYLE_INSET: impact++; + default: + break; } - if ((bottom) && (bottom != cell)) { - border = css_eyecatching_border(&cell->style->border[BOTTOM], cell->style, - &bottom->style->border[TOP], bottom->style); - cell->style->border[BOTTOM] = *border; + + switch (b->style) { + case CSS_BORDER_STYLE_DOUBLE: impact--; + case CSS_BORDER_STYLE_SOLID: impact--; + case CSS_BORDER_STYLE_DASHED: impact--; + case CSS_BORDER_STYLE_DOTTED: impact--; + case CSS_BORDER_STYLE_RIDGE: impact--; + case CSS_BORDER_STYLE_OUTSET: impact--; + case CSS_BORDER_STYLE_GROOVE: impact--; + case CSS_BORDER_STYLE_INSET: impact--; + default: + break; } + + if (impact < 0) + return true; + else if (impact > 0) + return false; + + /* 4a -- sort by origin */ + impact = 0; + + switch (a_src) { + case BOX_TABLE_CELL: impact++; + case BOX_TABLE_ROW: impact++; + case BOX_TABLE_ROW_GROUP: impact++; + /** \todo COL/COL_GROUP */ + case BOX_TABLE: impact++; + default: + break; + } + + switch (b_src) { + case BOX_TABLE_CELL: impact--; + case BOX_TABLE_ROW: impact--; + case BOX_TABLE_ROW_GROUP: impact--; + /** \todo COL/COL_GROUP */ + case BOX_TABLE: impact--; + default: + break; + } + + if (impact < 0) + return true; + else if (impact > 0) + return false; + + /* 4b -- furthest left (if direction: ltr) and towards top wins */ + /** \todo Currently assumes b satisifies this */ + return true; } +/****************************************************************************** + * Helpers for top border collapsing * + ******************************************************************************/ /** - * Removes all borders. + * Process a table + * + * \param table Table to process + * \param a Current border style for cell + * \param a_src Source of \a a + * + * \post \a a will be updated with most eyecatching style + * \post \a a_src will be updated also */ +void table_cell_top_process_table(struct box *table, struct border *a, + box_type *a_src) +{ + struct border b; + box_type b_src; + + /* Top border of table */ + b.style = css_computed_border_top_style(table->style); + b.color = css_computed_border_top_color(table->style, &b.c); + css_computed_border_top_width(table->style, &b.width, &b.unit); + b_src = BOX_TABLE; + + if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; + } +} -void table_remove_borders(struct css_style *style) +/** + * Process a group + * + * \param cell Cell being considered + * \param group Group to process + * \param a Current border style for cell + * \param a_src Source of \a a + * \return true if group has non-empty rows, false otherwise + * + * \post \a a will be updated with most eyecatching style + * \post \a a_src will be updated also + */ +bool table_cell_top_process_group(struct box *cell, struct box *group, + struct border *a, box_type *a_src) { - int i; + struct border b; + box_type b_src; + + /* Bottom border of group */ + b.style = css_computed_border_bottom_style(group->style); + b.color = css_computed_border_bottom_color(group->style, &b.c); + css_computed_border_bottom_width(group->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; + } - for (i = 0; i < 4; i++) { - style->border[i].style = CSS_BORDER_STYLE_NONE; - style->border[i].width.value.value = 0; - style->border[i].width.value.unit = CSS_UNIT_PX; + if (group->last != NULL) { + /* Process rows in group, starting with last */ + struct box *row = group->last; + + while (table_cell_top_process_row(cell, row, + a, a_src) == false) { + if (row->prev == NULL) { + return false; + } else { + row = row->prev; + } + } + } else { + /* Group is empty, so consider its top border */ + b.style = css_computed_border_top_style(group->style); + b.color = css_computed_border_top_color(group->style, &b.c); + css_computed_border_top_width(group->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW_GROUP; + + if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; + } + + return false; } -} + return true; +} /** - * Find a cell occupying a particular position in a table grid. + * Process a row + * + * \param cell Cell being considered + * \param row Row to process + * \param a Current border style for cell + * \param a_src Source of \a a + * \return true if row has cells, false otherwise + * + * \post \a a will be updated with most eyecatching style + * \post \a a_src will be updated also */ - -struct box *table_find_cell(struct box *table, unsigned int x, - unsigned int y) +bool table_cell_top_process_row(struct box *cell, struct box *row, + struct border *a, box_type *a_src) { - struct box *row_group, *row, *cell; - unsigned int row_num = 0; - - if (table->columns <= x || table->rows <= y) - return 0; + struct border b; + box_type b_src; + + /* Bottom border of row */ + b.style = css_computed_border_bottom_style(row->style); + b.color = css_computed_border_bottom_color(row->style, &b.c); + css_computed_border_bottom_width(row->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW; + + if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; + } - row_group = table->children; - row = row_group->children; + if (row->children == NULL) { + /* Row is empty, so consider its top border */ + b.style = css_computed_border_top_style(row->style); + b.color = css_computed_border_top_color(row->style, &b.c); + css_computed_border_top_width(row->style, &b.width, &b.unit); + b_src = BOX_TABLE_ROW; - while (row_num != y) { - if (row->next) { - row = row->next; - } else { - row_group = row_group->next; - row = row_group->children; + if (table_border_is_more_eyecatching(a, *a_src, &b, b_src)) { + *a = b; + *a_src = b_src; } - row_num++; - } + return false; + } else { + /* Process cells that are directly above the cell being + * considered. They may not be in this row, but in one of the + * rows above it in the case where rowspan > 1. */ + struct box *c; + bool processed = false; + + while (processed == false) { + for (c = row->children; c != NULL; c = c->next) { + /* Ignore cells to the left */ + if (c->start_column + c->columns < + cell->start_column) + continue; + /* Ignore cells to the right */ + if (c->start_column >= cell->start_column + + cell->columns) + continue; + + /* Flag that we've processed a cell */ + processed = true; + + /* Consider bottom border */ + b.style = css_computed_border_bottom_style( + c->style); + b.color = css_computed_border_bottom_color( + c->style, &b.c); + css_computed_border_bottom_width(c->style, + &b.width, &b.unit); + b_src = BOX_TABLE_CELL; + + if (table_border_is_more_eyecatching(a, *a_src, + &b, b_src)) { + *a = b; + *a_src = b_src; + } + } - for (cell = row->children; cell; cell = cell->next) - if (cell->start_column <= x && - x < cell->start_column + cell->columns) - break; + if (processed == false) { + /* There must be a preceding row */ + assert(row->prev != NULL); + + row = row->prev; + } + } + } - return cell; + return true; } + diff --git a/render/table.h b/render/table.h index 9a83d6394..ecd3043b5 100644 --- a/render/table.h +++ b/render/table.h @@ -29,6 +29,6 @@ struct box; bool table_calculate_column_types(struct box *table); -void table_collapse_borders(struct box *table); +void table_used_border_for_cell(struct box *cell); #endif diff --git a/render/textplain.c b/render/textplain.c index dd3d168f9..256d9b8db 100644 --- a/render/textplain.c +++ b/render/textplain.c @@ -30,6 +30,7 @@ #include <iconv.h> #include "content/content.h" #include "css/css.h" +#include "css/utils.h" #include "desktop/gui.h" #include "desktop/plotters.h" #include "desktop/selection.h" @@ -51,7 +52,7 @@ static plot_font_style_t textplain_style = { .family = PLOT_FONT_FAMILY_MONOSPACE, - .size = 10, + .size = 10 * FONT_SIZE_SCALE, .weight = 400, .flags = FONTF_NONE, .background = 0xffffff, @@ -69,7 +70,8 @@ static float textplain_line_height(void); * Create a CONTENT_TEXTPLAIN. */ -bool textplain_create(struct content *c, const char *params[]) +bool textplain_create(struct content *c, struct content *parent, + const char *params[]) { unsigned int i; char *utf8_data; @@ -733,6 +735,7 @@ float textplain_line_height(void) /* Size is in points, so convert to pixels. * Then use a constant line height of 1.2 x font size. */ - return (textplain_style.size * css_screen_dpi / 72) * 1.2; + return FIXTOFLT(FDIVI((FMUL(FLTTOFIX(1.2), + FMULI(nscss_screen_dpi, textplain_style.size))), 72)); } diff --git a/render/textplain.h b/render/textplain.h index 972405fe6..10609a71b 100644 --- a/render/textplain.h +++ b/render/textplain.h @@ -46,7 +46,8 @@ struct content_textplain_data { int formatted_width; }; -bool textplain_create(struct content *c, const char *params[]); +bool textplain_create(struct content *c, struct content *parent, + const char *params[]); bool textplain_process_data(struct content *c, char *data, unsigned int size); bool textplain_convert(struct content *c, int width, int height); void textplain_reformat(struct content *c, int width, int height); -- cgit v1.2.3