summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Sanders <vince@kyllikki.org>2016-04-23 23:32:21 +0100
committerVincent Sanders <vince@kyllikki.org>2016-04-23 23:32:21 +0100
commit974a4a21e16a2da85a66aae9b80eeca15ca26dd6 (patch)
tree4433113896c8f5aec5a3f4130b50f9a73075907c
parentcdd53bcffb00819360b3e62ef41de6607aca2c76 (diff)
downloadnetsurf-974a4a21e16a2da85a66aae9b80eeca15ca26dd6.tar.gz
netsurf-974a4a21e16a2da85a66aae9b80eeca15ca26dd6.tar.bz2
split out the layout glyph sizing and splitting API
This refactors the core "font" sizing API to be handled through gui function tables similar to every other core/frontend calling API.
-rw-r--r--content/content_protected.h2
-rw-r--r--desktop/browser_history.c15
-rw-r--r--desktop/gui_factory.c40
-rw-r--r--desktop/gui_layout.h (renamed from desktop/font.h)70
-rw-r--r--desktop/gui_table.h51
-rw-r--r--desktop/plot_style.h2
-rw-r--r--desktop/print.c2
-rw-r--r--desktop/print.h9
-rw-r--r--desktop/textarea.c16
-rw-r--r--desktop/treeview.c22
-rw-r--r--render/html.c4
-rw-r--r--render/html_interaction.c27
-rw-r--r--render/html_internal.h6
-rw-r--r--render/html_redraw.c44
-rw-r--r--render/layout.c6170
-rw-r--r--render/layout.h47
-rw-r--r--render/textplain.c41
17 files changed, 3330 insertions, 3238 deletions
diff --git a/content/content_protected.h b/content/content_protected.h
index 1e7916290..fb810bd07 100644
--- a/content/content_protected.h
+++ b/content/content_protected.h
@@ -108,7 +108,7 @@ struct content_user
/** Corresponds to a single URL. */
struct content {
- llcache_handle *llcache; /**< Low-level cache object */
+ llcache_handle *llcache; /**< Low-level cache object */
lwc_string *mime_type; /**< Original MIME type of data */
diff --git a/desktop/browser_history.c b/desktop/browser_history.c
index 1ee800019..f2b0fb3ee 100644
--- a/desktop/browser_history.c
+++ b/desktop/browser_history.c
@@ -37,11 +37,11 @@
#include "css/css.h"
#include "image/bitmap.h"
+#include "desktop/gui_layout.h"
#include "desktop/gui_internal.h"
#include "desktop/browser_history.h"
#include "desktop/browser_private.h"
#include "desktop/plotters.h"
-#include "desktop/font.h"
#define WIDTH 100
#define HEIGHT 86
@@ -273,6 +273,7 @@ browser_window_history__redraw_entry(struct history *history,
.stroke_width = entry == history->current ? 3 : 1,
};
plot_font_style_t fstyle = *plot_style_font;
+ nserror res;
if (clip) {
struct rect rect;
@@ -292,17 +293,21 @@ browser_window_history__redraw_entry(struct history *history,
WIDTH, HEIGHT,
entry->bitmap, 0xffffff, 0);
}
+
if (!plot->rectangle(entry->x - 1 + xoffset,
entry->y - 1 + yoffset,
entry->x + xoffset + WIDTH,
entry->y + yoffset + HEIGHT,
- &pstyle_history_rect))
+ &pstyle_history_rect)) {
return false;
+ }
- if (!nsfont.font_position_in_string(plot_style_font, entry->page.title,
- strlen(entry->page.title), WIDTH,
- &char_offset, &actual_x))
+ res = guit->layout->position(plot_style_font, entry->page.title,
+ strlen(entry->page.title), WIDTH,
+ &char_offset, &actual_x);
+ if (res != NSERROR_OK) {
return false;
+ }
fstyle.background = HISTORY_COLOUR_BACKGROUND;
fstyle.foreground = c;
diff --git a/desktop/gui_factory.c b/desktop/gui_factory.c
index 0b168f405..c15603e1c 100644
--- a/desktop/gui_factory.c
+++ b/desktop/gui_factory.c
@@ -37,6 +37,7 @@
#include "desktop/gui_search.h"
#include "desktop/gui_clipboard.h"
#include "desktop/gui_utf8.h"
+#include "desktop/gui_layout.h"
#include "desktop/netsurf.h"
/**
@@ -644,6 +645,35 @@ static nserror verify_bitmap_register(struct gui_bitmap_table *gbt)
return NSERROR_OK;
}
+/**
+ * verify layout table is valid
+ *
+ * \param glt The layout table to verify.
+ * \return NSERROR_OK if the table is valid else NSERROR_BAD_PARAMETER.
+ */
+static nserror verify_layout_register(struct gui_layout_table *glt)
+{
+ /* check table is present */
+ if (glt == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* check the mandantory fields are set */
+ if (glt->width == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ if (glt->position == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ if (glt->split == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ return NSERROR_OK;
+}
+
static void gui_default_quit(void)
{
}
@@ -725,6 +755,8 @@ nserror netsurf_register(struct netsurf_table *gt)
return NSERROR_BAD_PARAMETER;
}
+ /* mandantory tables */
+
/* miscellaneous table */
err = verify_misc_register(gt->misc);
if (err != NSERROR_OK) {
@@ -749,6 +781,14 @@ nserror netsurf_register(struct netsurf_table *gt)
return err;
}
+ /* layout table */
+ err = verify_layout_register(gt->layout);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+ /* optional tables */
+
/* file table */
if (gt->file == NULL) {
gt->file = default_file_table;
diff --git a/desktop/font.h b/desktop/gui_layout.h
index c883f8a28..1696aee91 100644
--- a/desktop/font.h
+++ b/desktop/gui_layout.h
@@ -1,7 +1,5 @@
/*
- * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
- * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
- * Copyright 2004 John Tytgat <joty@netsurf-browser.org>
+ * Copyright 2016 Vincent Sanders <vince@netsurf-browser.org>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
@@ -18,26 +16,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/** \file
- * Font handling interface.
+/**
+ * \file
*
- * These functions provide font related services. They all work on
- * UTF-8 strings with lengths given.
+ * Interface to platform-specific layout operation table.
*
- * Note that an interface to painting is not defined here. Painting is
- * redirected through platform-dependent plotters anyway, so there is
- * no gain in abstracting it here.
+ * This table is part of the layout used to measure glyphs before
+ * rendering, previously referred to as font functions.
+ *
+ * \note This is an old interface within the browser, it has been
+ * broken out purely to make the API obvious not as an indication this
+ * is the correct approach.
*/
-#ifndef _NETSURF_DESKTOP_FONT_H_
-#define _NETSURF_DESKTOP_FONT_H_
-
-#include <stdbool.h>
-#include <stddef.h>
+#ifndef _NETSURF_DESKTOP_GUI_LAYOUT_H_
+#define _NETSURF_DESKTOP_GUI_LAYOUT_H_
-#include "desktop/plot_style.h"
+struct plot_font_style;
-struct font_functions
+struct gui_layout_table
{
/**
* Measure the width of a string.
@@ -46,11 +43,12 @@ struct font_functions
* \param[in] string UTF-8 string to measure
* \param[in] length length of string, in bytes
* \param[out] width updated to width of string[0..length)
- * \return true on success and width updated else false.
+ * \return NSERROR_OK and width updated or appropriate error
+ * code on faliure
*/
- bool (*font_width)(const plot_font_style_t *fstyle,
- const char *string, size_t length,
- int *width);
+ nserror (*width)(const struct plot_font_style *fstyle, const char *string, size_t length, int *width);
+
+
/**
* Find the position in a string where an x coordinate falls.
*
@@ -60,25 +58,25 @@ struct font_functions
* \param[in] x coordinate to search for
* \param[out] char_offset updated to offset in string of actual_x, [0..length]
* \param[out] actual_x updated to x coordinate of character closest to x
- * \return true on success, false on error and error reported
+ * \return NSERROR_OK and char_offset and actual_x updated or appropriate error code on faliure
*/
- bool (*font_position_in_string)(const plot_font_style_t *fstyle,
- const char *string, size_t length,
- int x, size_t *char_offset, int *actual_x);
+ nserror (*position)(const struct plot_font_style *fstyle, const char *string, size_t length, int x, size_t *char_offset, int *actual_x);
+
+
/**
* Find where to split a string to make it fit a width.
*
- * \param fstyle style for this text
- * \param string UTF-8 string to measure
- * \param length length of string, in bytes
- * \param x width available
- * \param char_offset updated to offset in string of actual_x, [1..length]
- * \param actual_x updated to x coordinate of character closest to x
- * \return true on success, false on error and error reported
+ * \param[in] fstyle style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[in] x width available
+ * \param[out] char_offset updated to offset in string of actual_x, [1..length]
+ * \param[out] actual_x updated to x coordinate of character closest to x
+ * \return NSERROR_OK or appropriate error code on faliure
*
* On exit, char_offset indicates first character after split point.
*
- * Note: char_offset of 0 should never be returned.
+ * \note char_offset of 0 must never be returned.
*
* Returns:
* char_offset giving split point closest to x, where actual_x <= x
@@ -87,11 +85,7 @@ struct font_functions
*
* Returning char_offset == length means no split possible
*/
- bool (*font_split)(const plot_font_style_t *fstyle,
- const char *string, size_t length,
- int x, size_t *char_offset, int *actual_x);
+ nserror (*split)(const struct plot_font_style *fstyle, const char *string, size_t length, int x, size_t *char_offset, int *actual_x);
};
-extern const struct font_functions nsfont;
-
#endif
diff --git a/desktop/gui_table.h b/desktop/gui_table.h
index 6952228e2..0ac9f0417 100644
--- a/desktop/gui_table.h
+++ b/desktop/gui_table.h
@@ -38,6 +38,7 @@ struct gui_search_table;
struct gui_search_web_table;
struct gui_llcache_table;
struct gui_bitmap_table;
+struct gui_layout_table;
/**
* NetSurf operation function table
@@ -49,8 +50,9 @@ struct netsurf_table {
/**
* Browser table.
*
- * Provides miscellaneous browser functionality. The table
- * is mandantory and must be provided.
+ * Provides miscellaneous browser functionality.
+ *
+ * The table is mandantory and must be provided.
*/
struct gui_misc_table *misc;
@@ -58,6 +60,8 @@ struct netsurf_table {
* Window table.
*
* Provides all operations which affect a frontends display window.
+ *
+ * The table is mandantory and must be provided.
*/
struct gui_window_table *window;
@@ -75,15 +79,18 @@ struct netsurf_table {
/**
* Fetcher table
+ *
+ * The table is mandantory and must be provided.
*/
struct gui_fetch_table *fetch;
/**
* File table
*
- * Provides file and filename operations to the core. The
- * table is optional and may be NULL in which case the default
- * posix compliant operations will be used.
+ * Provides file and filename operations to the core.
+ *
+ * The table is optional and may be NULL in which case the
+ * default posix compliant operations will be used.
*/
struct gui_file_table *file;
@@ -91,8 +98,10 @@ struct netsurf_table {
* UTF8 table.
*
* Provides for conversion between the gui local character
- * encoding and utf8. The table optional and may be NULL which
- * implies the local encoding is utf8.
+ * encoding and utf8.
+ *
+ * The table optional and may be NULL which implies the local
+ * encoding is utf8.
*/
struct gui_utf8_table *utf8;
@@ -106,9 +115,10 @@ struct netsurf_table {
/**
* Web search table.
*
- * Used by the web search provider system. The table is
- * optional and may be NULL which uses the default empty
- * implementation.
+ * Used by the web search provider system.
+ *
+ * The table is optional and may be NULL which uses the
+ * default empty implementation.
*/
struct gui_search_web_table *search_web;
@@ -116,8 +126,10 @@ struct netsurf_table {
* Low level cache table.
*
* Used by the low level cache to push objects to persistant
- * storage. The table is optional and may be NULL which
- * uses the default implementation.
+ * storage.
+ *
+ * The table is optional and may be NULL which uses the
+ * default implementation.
*/
struct gui_llcache_table *llcache;
@@ -125,10 +137,21 @@ struct netsurf_table {
* Bitmap table.
*
* Used by the image convertors as a generic interface to
- * native platform-specific image formats. The table
- * is mandantory and must be provided.
+ * native platform-specific image formats.
+ *
+ * The table is mandantory and must be provided.
*/
struct gui_bitmap_table *bitmap;
+
+ /**
+ * Layout table
+ *
+ * Used by the layout process to measure glyphs in a frontend
+ * specific manner.
+ *
+ * The table is mandantory and must be provided.
+ */
+ struct gui_layout_table *layout;
};
#endif
diff --git a/desktop/plot_style.h b/desktop/plot_style.h
index 45813d40d..af54e79cb 100644
--- a/desktop/plot_style.h
+++ b/desktop/plot_style.h
@@ -150,7 +150,7 @@ typedef unsigned long plot_font_flags_t;
/**
* Font style for plotting
*/
-typedef struct {
+typedef struct plot_font_style {
plot_font_generic_family_t family; /**< Generic family to plot with */
int size; /**< Font size, in points * FONT_SIZE_SCALE */
int weight; /**< Font weight: value in range [100,900] as per CSS */
diff --git a/desktop/print.c b/desktop/print.c
index 64d796946..55caf5105 100644
--- a/desktop/print.c
+++ b/desktop/print.c
@@ -246,7 +246,7 @@ bool print_cleanup(hlcache_handle *content, const struct printer *printer,
* configuration or lack of memory.
*/
struct print_settings *print_make_settings(print_configuration configuration,
- const char *filename, const struct font_functions *font_func)
+ const char *filename, const struct gui_layout_table *font_func)
{
struct print_settings *settings;
css_fixed length = 0;
diff --git a/desktop/print.h b/desktop/print.h
index 63c2935f1..5c51981d1 100644
--- a/desktop/print.h
+++ b/desktop/print.h
@@ -38,6 +38,7 @@
struct hlcache_handle;
struct printer;
+struct gui_layout_table;
enum { MARGINLEFT = 0, MARGINRIGHT = 1, MARGINTOP = 2, MARGINBOTTOM = 3};
@@ -60,7 +61,7 @@ struct print_settings{
const char *output;
/*the functions used to measure fonts*/
- const struct font_functions *font_func;
+ const struct gui_layout_table *font_func;
};
@@ -73,8 +74,10 @@ bool print_draw_next_page(const struct printer *printer,
bool print_cleanup(struct hlcache_handle *, const struct printer *,
struct print_settings *settings);
-struct print_settings *print_make_settings(print_configuration configuration,
- const char *url, const struct font_functions *font_func);
+/**
+ * Setup print settings for print render operation.
+ */
+struct print_settings *print_make_settings(print_configuration configuration, const char *url, const struct gui_layout_table *font_func);
/*is the content currently redrawn for printing?*/
extern bool html_redraw_printing;
diff --git a/desktop/textarea.c b/desktop/textarea.c
index 9970bd6ab..4a7e008bc 100644
--- a/desktop/textarea.c
+++ b/desktop/textarea.c
@@ -37,8 +37,8 @@
#include "desktop/textinput.h"
#include "desktop/plotters.h"
#include "desktop/scrollbar.h"
-#include "desktop/font.h"
#include "desktop/gui_clipboard.h"
+#include "desktop/gui_layout.h"
#include "desktop/gui_internal.h"
#define CARET_COLOR 0x0000FF
@@ -404,7 +404,7 @@ static bool textarea_set_caret_internal(struct textarea *ta, int caret_b)
/* find byte offset of caret position */
b_off = index;
- nsfont.font_width(&ta->fstyle,
+ guit->layout->width(&ta->fstyle,
ta->show->data +
ta->lines[ta->caret_pos.line].b_start,
b_off - ta->lines[ta->caret_pos.line].b_start,
@@ -873,12 +873,12 @@ static bool textarea_reflow_singleline(struct textarea *ta, size_t b_off,
}
/* Measure new width */
- nsfont.font_width(&ta->fstyle, ta->show->data,
+ guit->layout->width(&ta->fstyle, ta->show->data,
ta->show->len - 1, &x);
/* Get width of retained text */
if (b_off != ta->lines[0].b_length) {
- nsfont.font_width(&ta->fstyle, ta->show->data,
+ guit->layout->width(&ta->fstyle, ta->show->data,
b_off, &retained_width);
} else {
retained_width = ta->lines[0].width;
@@ -1019,7 +1019,7 @@ static bool textarea_reflow_multiline(struct textarea *ta,
}
/* Wrap current line in paragraph */
- nsfont.font_split(&ta->fstyle, text, para_end - text,
+ guit->layout->split(&ta->fstyle, text, para_end - text,
avail_width, &b_off, &x);
/* b_off now marks space, or end of paragraph */
@@ -1201,7 +1201,7 @@ static bool textarea_reflow_multiline(struct textarea *ta,
ta->lines[start].b_start;
text = ta->text.data + ta->lines[start].b_start;
- nsfont.font_width(&ta->fstyle, text,
+ guit->layout->width(&ta->fstyle, text,
retain_end, &retained_width);
r->x0 = max(r->x0,
@@ -1252,7 +1252,7 @@ static size_t textarea_get_b_off_xy(struct textarea *ta, int x, int y,
line = 0;
/* Get byte position */
- nsfont.font_position_in_string(&ta->fstyle,
+ guit->layout->position(&ta->fstyle,
ta->show->data + ta->lines[line].b_start,
ta->lines[line].b_length, x, &bpos, &x);
@@ -2303,7 +2303,7 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg, float scale,
/* find clip left/right for this part of line */
left = right;
if (b_len_part != b_len) {
- nsfont.font_width(&fstyle, line_text, b_end,
+ guit->layout->width(&fstyle, line_text, b_end,
&right);
} else {
right = ta->lines[line].width;
diff --git a/desktop/treeview.c b/desktop/treeview.c
index 210c6f97e..97cf30d23 100644
--- a/desktop/treeview.c
+++ b/desktop/treeview.c
@@ -32,8 +32,8 @@
#include "desktop/plotters.h"
#include "desktop/textarea.h"
#include "desktop/treeview.h"
-#include "desktop/font.h"
#include "desktop/gui_clipboard.h"
+#include "desktop/gui_layout.h"
#include "desktop/gui_internal.h"
/** @todo get rid of REDRAW_MAX -- need to be able to know window size */
@@ -540,10 +540,10 @@ static inline void treeview_insert_node(treeview_node *a,
/* Parent is expanded, so inserted node will be visible and
* affect layout */
if (a->text.width == 0) {
- nsfont.font_width(&plot_style_odd.text,
- a->text.data,
- a->text.len,
- &(a->text.width));
+ guit->layout->width(&plot_style_odd.text,
+ a->text.data,
+ a->text.len,
+ &(a->text.width));
}
do {
@@ -644,7 +644,7 @@ nserror treeview_update_node_folder(treeview *tree,
if (folder->parent->flags & TV_NFLAGS_EXPANDED) {
/* Text will be seen, get its width */
- nsfont.font_width(&plot_style_odd.text,
+ guit->layout->width(&plot_style_odd.text,
folder->text.data,
folder->text.len,
&(folder->text.width));
@@ -694,7 +694,7 @@ nserror treeview_update_node_entry(treeview *tree,
if (entry->parent->flags & TV_NFLAGS_EXPANDED) {
/* Text will be seen, get its width */
- nsfont.font_width(&plot_style_odd.text,
+ guit->layout->width(&plot_style_odd.text,
entry->text.data,
entry->text.len,
&(entry->text.width));
@@ -714,7 +714,7 @@ nserror treeview_update_node_entry(treeview *tree,
if (entry->flags & TV_NFLAGS_EXPANDED) {
/* Text will be seen, get its width */
- nsfont.font_width(&plot_style_odd.text,
+ guit->layout->width(&plot_style_odd.text,
e->fields[i - 1].value.data,
e->fields[i - 1].value.len,
&(e->fields[i - 1].value.width));
@@ -1311,7 +1311,7 @@ nserror treeview_create(treeview **tree,
f->value.data = lwc_string_data(fields[i].field);
f->value.len = lwc_string_length(fields[i].field);
- nsfont.font_width(&plot_style_odd.text, f->value.data,
+ guit->layout->width(&plot_style_odd.text, f->value.data,
f->value.len, &(f->value.width));
if (f->flags & TREE_FLAG_SHOW_NAME)
@@ -1411,7 +1411,7 @@ static nserror treeview_node_expand_internal(treeview *tree,
do {
assert((child->flags & TV_NFLAGS_EXPANDED) == false);
if (child->text.width == 0) {
- nsfont.font_width(&plot_style_odd.text,
+ guit->layout->width(&plot_style_odd.text,
child->text.data,
child->text.len,
&(child->text.width));
@@ -1432,7 +1432,7 @@ static nserror treeview_node_expand_internal(treeview *tree,
for (i = 0; i < tree->n_fields - 1; i++) {
if (e->fields[i].value.width == 0) {
- nsfont.font_width(&plot_style_odd.text,
+ guit->layout->width(&plot_style_odd.text,
e->fields[i].value.data,
e->fields[i].value.len,
&(e->fields[i].value.width));
diff --git a/render/html.c b/render/html.c
index c95044f29..68eb11cd2 100644
--- a/render/html.c
+++ b/render/html.c
@@ -49,8 +49,8 @@
#include "image/bitmap.h"
#include "javascript/js.h"
#include "desktop/browser.h"
-#include "desktop/font.h"
#include "desktop/gui_utf8.h"
+#include "desktop/gui_layout.h"
#include "desktop/gui_internal.h"
#include "render/box.h"
@@ -844,7 +844,7 @@ html_create_html_data(html_content *c, const http_parameter *params)
c->frameset = NULL;
c->iframe = NULL;
c->page = NULL;
- c->font_func = &nsfont;
+ c->font_func = guit->layout;
c->drag_type = HTML_DRAG_NONE;
c->drag_owner.no_owner = true;
c->selection_type = HTML_SELECTION_NONE;
diff --git a/render/html_interaction.c b/render/html_interaction.c
index 397ce46c2..19e19f6ef 100644
--- a/render/html_interaction.c
+++ b/render/html_interaction.c
@@ -19,7 +19,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/** \file
+/**
+ * \file
* User interaction with a CONTENT_HTML (implementation).
*/
@@ -42,9 +43,9 @@
#include "desktop/selection.h"
#include "desktop/textarea.h"
#include "desktop/textinput.h"
-#include "desktop/font.h"
#include "javascript/js.h"
#include "desktop/gui_misc.h"
+#include "desktop/gui_layout.h"
#include "desktop/gui_internal.h"
#include "render/box.h"
@@ -209,8 +210,8 @@ static size_t html_selection_drag_end(struct html_content *html,
font_plot_style_from_css(box->style, &fstyle);
- nsfont.font_position_in_string(&fstyle, box->text, box->length,
- dx, &idx, &pixel_offset);
+ guit->layout->position(&fstyle, box->text, box->length,
+ dx, &idx, &pixel_offset);
idx += box->byte_offset;
}
@@ -416,9 +417,9 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
font_plot_style_from_css(box->style, &fstyle);
- nsfont.font_position_in_string(&fstyle,
- box->text, box->length,
- dx, &idx, &pixel_offset);
+ guit->layout->position(&fstyle,
+ box->text, box->length,
+ dx, &idx, &pixel_offset);
selection_track(&html->sel, mouse,
box->byte_offset + idx);
@@ -910,12 +911,12 @@ void html_mouse_action(struct content *c, struct browser_window *bw,
font_plot_style_from_css(text_box->style,
&fstyle);
- nsfont.font_position_in_string(&fstyle,
- text_box->text,
- text_box->length,
- x - text_box_x,
- &idx,
- &pixel_offset);
+ guit->layout->position(&fstyle,
+ text_box->text,
+ text_box->length,
+ x - text_box_x,
+ &idx,
+ &pixel_offset);
if (selection_click(&html->sel, mouse,
text_box->byte_offset + idx)) {
diff --git a/render/html_internal.h b/render/html_internal.h
index 419fe41b3..de28726fb 100644
--- a/render/html_internal.h
+++ b/render/html_internal.h
@@ -27,6 +27,8 @@
#include "desktop/selection.h"
#include "render/html.h"
+struct gui_layout_table;
+
typedef enum {
HTML_DRAG_NONE, /** No drag */
HTML_DRAG_SELECTION, /** Own; Text selection */
@@ -36,6 +38,7 @@ typedef enum {
HTML_DRAG_CONTENT_SELECTION, /** Not own; drag in child content */
HTML_DRAG_CONTENT_SCROLL /** Not own; drag in child content */
} html_drag_type;
+
union html_drag_owner {
bool no_owner;
struct box *content;
@@ -109,8 +112,9 @@ typedef struct html_content {
struct box *layout;
/** Document background colour. */
colour background_colour;
+
/** Font callback table */
- const struct font_functions *font_func;
+ const struct gui_layout_table *font_func;
/** Number of entries in scripts */
unsigned int scripts_count;
diff --git a/render/html_redraw.c b/render/html_redraw.c
index 091ece0f5..969b74f87 100644
--- a/render/html_redraw.c
+++ b/render/html_redraw.c
@@ -49,8 +49,9 @@
#include "desktop/print.h"
#include "desktop/scrollbar.h"
#include "desktop/textarea.h"
-#include "desktop/font.h"
#include "image/bitmap.h"
+#include "desktop/gui_layout.h"
+#include "desktop/gui_internal.h"
#include "render/box.h"
#include "render/font.h"
@@ -194,6 +195,7 @@ bool text_redraw(const char *utf8_text, size_t utf8_len,
int startx, endx;
plot_style_t pstyle_fill_hback = *plot_style_fill_white;
plot_font_style_t fstyle_hback = plot_fstyle;
+ nserror res;
if (end_idx > utf8_len) {
/* adjust for trailing space, not present in
@@ -202,13 +204,19 @@ bool text_redraw(const char *utf8_text, size_t utf8_len,
endtxt_idx = utf8_len;
}
- if (!nsfont.font_width(fstyle, utf8_text, start_idx,
- &startx))
+ res = guit->layout->width(fstyle,
+ utf8_text, start_idx,
+ &startx);
+ if (res != NSERROR_OK) {
startx = 0;
+ }
- if (!nsfont.font_width(fstyle, utf8_text, endtxt_idx,
- &endx))
+ res = guit->layout->width(fstyle,
+ utf8_text, endtxt_idx,
+ &endx);
+ if (res != NSERROR_OK) {
endx = 0;
+ }
/* is there a trailing space that should be highlighted
* as well? */
@@ -1262,6 +1270,7 @@ static bool html_redraw_file(int x, int y, int width, int height,
const char *text;
size_t length;
plot_font_style_t fstyle;
+ nserror res;
font_plot_style_from_css(box->style, &fstyle);
fstyle.background = background_colour;
@@ -1272,13 +1281,16 @@ static bool html_redraw_file(int x, int y, int width, int height,
text = messages_get("Form_Drop");
length = strlen(text);
- if (!nsfont.font_width(&fstyle, text, length, &text_width))
+ res = guit->layout->width(&fstyle, text, length, &text_width);
+ if (res != NSERROR_OK) {
return false;
+ }
text_width *= scale;
- if (width < text_width + 8)
+ if (width < text_width + 8) {
x = x + width - text_width - 4;
- else
+ } else {
x = x + 4;
+ }
return ctx->plot->text(x, y + height * 0.75, text, length, &fstyle);
}
@@ -2425,17 +2437,25 @@ bool html_redraw_box(const html_content *html, struct box *box,
const char *obj = "\xef\xbf\xbc";
int obj_width;
int obj_x = x + padding_left;
+ nserror res;
+
if (!plot->rectangle(x + padding_left,
y + padding_top,
x + padding_left + width - 1,
y + padding_top + height - 1,
- plot_style_broken_object))
+ plot_style_broken_object)) {
return false;
- if (!nsfont.font_width(plot_fstyle_broken_object, obj,
- sizeof(obj) - 1, &obj_width))
+ }
+
+ res = guit->layout->width(plot_fstyle_broken_object,
+ obj,
+ sizeof(obj) - 1,
+ &obj_width);
+ if (res != NSERROR_OK) {
obj_x += 1;
- else
+ } else {
obj_x += width / 2 - obj_width / 2;
+ }
if (!plot->text(obj_x, y + padding_top + (int)
(height * 0.75),
diff --git a/render/layout.c b/render/layout.c
index 45d905fa4..e80ec994a 100644
--- a/render/layout.c
+++ b/render/layout.c
@@ -19,8 +19,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/** \file
- * HTML layout (implementation).
+/**
+ * \file
+ * HTML layout implementation.
*
* Layout is carried out in two stages:
*
@@ -52,7 +53,7 @@
#include "desktop/browser.h"
#include "desktop/scrollbar.h"
#include "desktop/textarea.h"
-#include "desktop/font.h"
+#include "desktop/gui_layout.h"
#include "render/box.h"
#include "render/font.h"
@@ -70,619 +71,578 @@
/* Fixed point percentage (a) of an integer (b), to an integer */
#define FPCT_OF_INT_TOINT(a, b) (FIXTOINT(FDIV((a * b), F_100)))
-
-static bool layout_block_context(struct box *block, int viewport_height,
- html_content *content);
-static void layout_minmax_block(struct box *block,
- const struct font_functions *font_func);
-static struct box* layout_next_margin_block(struct box *box, struct box *block,
- int viewport_height, int *max_pos_margin, int *max_neg_margin);
-static bool layout_block_object(struct box *block);
-static void layout_get_object_dimensions(struct box *box,
- int *width, int *height, int min_width, int max_width,
- int min_height, int max_height);
-static void layout_block_find_dimensions(int available_width,
- int viewport_height, int lm, int rm,
- struct box *box);
-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(struct box *box, int available_width, int width,
- int lm, int rm, int max_width, int min_width);
-static void layout_float_find_dimensions(int available_width,
- const css_computed_style *style, struct box *box);
-static void layout_find_dimensions(int available_width, int viewport_height,
- struct box *box, const css_computed_style *style,
- int *width, int *height, int *max_width, int *min_width,
- int *max_height, int *min_height, 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, enum css_clear_e 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,
- bool *has_height, const struct font_functions *font_func);
-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,
- html_content *content, struct box **next_box);
-static struct box *layout_minmax_line(struct box *first, int *min, int *max,
- bool first_line, bool *line_has_height,
- const struct font_functions *font_func);
-static int layout_text_indent(const css_computed_style *style, int width);
-static bool layout_float(struct box *b, int width, html_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,
- html_content *content);
-static void layout_move_children(struct box *box, int x, int y);
-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);
-static void layout_position_relative(struct box *root, struct box *fp,
- int fx, int fy);
-static void layout_compute_relative_offset(struct box *box, int *x, int *y);
-static bool layout_position_absolute(struct box *box,
- struct box *containing_block,
- int cx, int cy,
- html_content *content);
-static bool layout_absolute(struct box *box, struct box *containing_block,
- int cx, int cy,
- html_content *content);
-static void layout_compute_offsets(struct box *box,
- struct box *containing_block,
- int *top, int *right, int *bottom, int *left);
+/* forward declaration to break cycles */
+static bool layout_block_context(struct box *block, int viewport_height, html_content *content);
+static void layout_minmax_block(struct box *block, const struct gui_layout_table *font_func);
/**
- * Calculate positions of boxes in a document.
+ * Compute the size of replaced boxes with auto dimensions, according to
+ * content.
*
- * \param content content of type CONTENT_HTML
- * \param width available width
- * \param height available height
- * \return true on success, false on memory exhaustion
+ * \param box Box with object
+ * \param width Width value in px or AUTO. If AUTO, updated to value in px.
+ * \param height Height value in px or AUTO. If AUTO, updated to value in px.
+ * \param min_width Box's min width, as given by layout_find_dimensions.
+ * \param max_width Box's max width, as given by layout_find_dimensions.
+ * \param min_height Box's min height, as given by layout_find_dimensions.
+ * \param max_height Box's max height, as given by layout_find_dimensions.
+ *
+ * See CSS 2.1 sections 10.3 and 10.6.
*/
-
-bool layout_document(html_content *content, int width, int height)
+static void
+layout_get_object_dimensions(struct box *box,
+ int *width, int *height,
+ int min_width, int max_width,
+ int min_height, int max_height)
{
- bool ret;
- struct box *doc = content->layout;
- const struct font_functions *font_func = content->font_func;
+ assert(box->object != NULL);
+ assert(width != NULL && height != NULL);
- layout_minmax_block(doc, font_func);
+ if (*width == AUTO && *height == AUTO) {
+ /* No given dimensions */
- layout_block_find_dimensions(width, height, 0, 0, doc);
- 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;
+ bool scaled = false;
+ int intrinsic_width = content_get_width(box->object);
+ int intrinsic_height = content_get_height(box->object);
- ret = layout_block_context(doc, height, content);
+ /* use intrinsic dimensions */
+ *width = intrinsic_width;
+ *height = intrinsic_height;
- /* make <html> and <body> fill available height */
- if (doc->y + doc->padding[TOP] + doc->height + doc->padding[BOTTOM] +
- doc->border[BOTTOM].width + doc->margin[BOTTOM] <
- height) {
- doc->height = height - (doc->y + doc->padding[TOP] +
- 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].width +
- doc->children->padding[TOP] +
- doc->children->padding[BOTTOM] +
- doc->children->border[BOTTOM].width +
- doc->children->margin[BOTTOM]);
- }
+ /* Deal with min/max-width first */
+ if (min_width > 0 && min_width > *width) {
+ *width = min_width;
+ scaled = true;
+ }
+ if (max_width >= 0 && max_width < *width) {
+ *width = max_width;
+ scaled = true;
+ }
- layout_lists(doc, font_func);
- layout_position_absolute(doc, doc, 0, 0, content);
- layout_position_relative(doc, doc, 0, 0);
+ if (scaled && (intrinsic_width != 0)) {
+ /* Update height */
+ *height = (*width * intrinsic_height) /
+ intrinsic_width;
+ }
- layout_calculate_descendant_bboxes(doc);
+ scaled = false;
+ /* Deal with min/max-height */
+ if (min_height > 0 && min_height > *height) {
+ *height = min_height;
+ scaled = true;
+ }
+ if (max_height >= 0 && max_height < *height) {
+ *height = max_height;
+ scaled = true;
+ }
- return ret;
+ if (scaled && (intrinsic_height != 0)) {
+ /* Update width */
+ *width = (*height * intrinsic_width) /
+ intrinsic_height;
+ }
+
+ } else if (*width == AUTO) {
+ /* Have given height; width is calculated from the given height
+ * and ratio of intrinsic dimensions */
+ int intrinsic_width = content_get_width(box->object);
+ int intrinsic_height = content_get_height(box->object);
+
+ if (intrinsic_height != 0)
+ *width = (*height * intrinsic_width) /
+ intrinsic_height;
+ else
+ *width = intrinsic_width;
+
+ if (min_width > 0 && min_width > *width)
+ *width = min_width;
+ if (max_width >= 0 && max_width < *width)
+ *width = max_width;
+
+ } else if (*height == AUTO) {
+ /* Have given width; height is calculated from the given width
+ * and ratio of intrinsic dimensions */
+ int intrinsic_width = content_get_width(box->object);
+ int intrinsic_height = content_get_height(box->object);
+
+ if (intrinsic_width != 0)
+ *height = (*width * intrinsic_height) /
+ intrinsic_width;
+ else
+ *height = intrinsic_height;
+ }
}
/**
- * Layout a block formatting context.
- *
- * \param block BLOCK, INLINE_BLOCK, or TABLE_CELL to layout
- * \param viewport_height Height of viewport in pixels or -ve if unknown
- * \param content Memory pool for any new boxes
- * \return true on success, false on memory exhaustion
+ * Calculate the text-indent length.
*
- * This function carries out layout of a block and its children, as described
- * in CSS 2.1 9.4.1.
+ * \param style style of block
+ * \param width width of containing block
+ * \return length of indent
*/
-
-bool layout_block_context(struct box *block, int viewport_height,
- html_content *content)
+static int layout_text_indent(const css_computed_style *style, int width)
{
- struct box *box;
- int cx, cy; /**< current coordinates */
- int max_pos_margin = 0;
- int max_neg_margin = 0;
- int y = 0;
- int lm, rm;
- struct box *margin_collapse = NULL;
- bool in_margin = false;
- css_fixed gadget_size;
- css_unit gadget_unit; /* Checkbox / radio buttons */
-
- assert(block->type == BOX_BLOCK ||
- block->type == BOX_INLINE_BLOCK ||
- block->type == BOX_TABLE_CELL);
- assert(block->width != UNKNOWN_WIDTH);
- assert(block->width != AUTO);
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
- block->float_children = NULL;
- block->cached_place_below_level = 0;
- block->clear_level = 0;
+ css_computed_text_indent(style, &value, &unit);
- /* special case if the block contains an object */
- if (block->object) {
- int temp_width = block->width;
- if (!layout_block_object(block))
- return false;
- layout_get_object_dimensions(block, &temp_width,
- &block->height, INT_MIN, INT_MAX,
- INT_MIN, INT_MAX);
- return true;
- } else if (block->flags & REPLACE_DIM) {
- return true;
+ if (unit == CSS_UNIT_PCT) {
+ return FPCT_OF_INT_TOINT(value, width);
+ } else {
+ return FIXTOINT(nscss_len2px(value, unit, style));
}
+}
- /* special case if the block contains an radio button or checkbox */
- if (block->gadget && (block->gadget->type == GADGET_RADIO ||
- block->gadget->type == GADGET_CHECKBOX)) {
- /* form checkbox or radio button
- * if width or height is AUTO, set it to 1em */
- gadget_unit = CSS_UNIT_EM;
- gadget_size = INTTOFIX(1);
- if (block->height == AUTO)
- block->height = FIXTOINT(nscss_len2px(gadget_size,
- gadget_unit, block->style));
- }
- box = block->children;
- /* set current coordinates to top-left of the block */
- cx = 0;
- y = cy = block->padding[TOP];
- if (box)
- box->y = block->padding[TOP];
+/**
+ * Determine width of margin, borders, and padding on one side of a box.
+ *
+ * \param style style to measure
+ * \param side side of box to measure
+ * \param margin whether margin width is required
+ * \param border whether border width is required
+ * \param padding whether padding width is required
+ * \param fixed increased by sum of fixed margin, border, and padding
+ * \param frac increased by sum of fractional margin and padding
+ */
+static 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);
- /* Step through the descendants of the block in depth-first order, but
- * not into the children of boxes which aren't blocks. For example, if
- * the tree passed to this function looks like this (box->type shown):
- *
- * block -> BOX_BLOCK
- * BOX_BLOCK * (1)
- * BOX_INLINE_CONTAINER * (2)
- * BOX_INLINE
- * BOX_TEXT
- * ...
- * BOX_BLOCK * (3)
- * BOX_TABLE * (4)
- * BOX_TABLE_ROW
- * BOX_TABLE_CELL
- * ...
- * BOX_TABLE_CELL
- * ...
- * BOX_BLOCK * (5)
- * BOX_INLINE_CONTAINER * (6)
- * BOX_TEXT
- * ...
- * then the while loop will visit each box marked with *, setting box
- * to each in the order shown. */
- while (box) {
- enum css_overflow_e overflow_x = CSS_OVERFLOW_VISIBLE;
- enum css_overflow_e overflow_y = CSS_OVERFLOW_VISIBLE;
+ 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 }
+ };
- assert(box->type == BOX_BLOCK || box->type == BOX_TABLE ||
- box->type == BOX_INLINE_CONTAINER);
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
- /* Tables are laid out before being positioned, because the
- * position depends on the width which is calculated in
- * table layout. Blocks and inline containers are positioned
- * before being laid out, because width is not dependent on
- * content, and the position is required during layout for
- * correct handling of floats.
- */
+ assert(style);
- if (box->style &&
- (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,
- * so no need to look at its children now. */
- goto advance_to_next_box;
- }
+ /* margin */
+ if (margin) {
+ enum css_margin_e type;
- /* If we don't know which box the current margin collapses
- * through to, find out. Update the pos/neg margin values. */
- if (margin_collapse == NULL) {
- margin_collapse = layout_next_margin_block(box, block,
- viewport_height,
- &max_pos_margin, &max_neg_margin);
- /* We have a margin that has not yet been applied. */
- in_margin = true;
+ type = margin_funcs[side](style, &value, &unit);
+ if (type == CSS_MARGIN_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ *frac += FIXTOINT(FDIV(value, F_100));
+ } else {
+ *fixed += FIXTOINT(nscss_len2px(value, unit,
+ style));
+ }
}
+ }
- /* Clearance. */
- y = 0;
- if (box->style && css_computed_clear(box->style) !=
- CSS_CLEAR_NONE)
- y = layout_clear(block->float_children,
- css_computed_clear(box->style));
+ /* border */
+ if (border) {
+ if (border_funcs[side].style(style) !=
+ CSS_BORDER_STYLE_NONE) {
+ border_funcs[side].width(style, &value, &unit);
- /* Find box's overflow properties */
- if (box->style) {
- overflow_x = css_computed_overflow_x(box->style);
- overflow_y = css_computed_overflow_y(box->style);
+ *fixed += FIXTOINT(nscss_len2px(value, unit, style));
}
+ }
- /* Blocks establishing a block formatting context get minimum
- * left and right margins to avoid any floats. */
- lm = rm = 0;
+ /* padding */
+ if (padding) {
+ padding_funcs[side](style, &value, &unit);
+ if (unit == CSS_UNIT_PCT) {
+ *frac += FIXTOINT(FDIV(value, F_100));
+ } else {
+ *fixed += FIXTOINT(nscss_len2px(value, unit, style));
+ }
+ }
+}
- if (box->type == BOX_BLOCK || box->flags & IFRAME) {
- if (!box->object && !(box->flags & IFRAME) &&
- !(box->flags & REPLACE_DIM) &&
- box->style &&
- (overflow_x != CSS_OVERFLOW_VISIBLE ||
- overflow_y != CSS_OVERFLOW_VISIBLE)) {
- /* box establishes new block formatting context
- * so available width may be diminished due to
- * floats. */
- int x0, x1, top;
- struct box *left, *right;
- top = cy + max_pos_margin - max_neg_margin;
- top = (top > y) ? top : y;
- x0 = cx;
- x1 = cx + box->parent->width -
- box->parent->padding[LEFT] -
- box->parent->padding[RIGHT];
- find_sides(block->float_children, top, top,
- &x0, &x1, &left, &right);
- /* calculate min required left & right margins
- * needed to avoid floats */
- lm = x0 - cx;
- rm = cx + box->parent->width -
- box->parent->padding[LEFT] -
- box->parent->padding[RIGHT] -
- x1;
- }
- layout_block_find_dimensions(box->parent->width,
- viewport_height, lm, rm, box);
- if (box->type == BOX_BLOCK && !(box->flags & IFRAME)) {
- layout_block_add_scrollbar(box, RIGHT);
- layout_block_add_scrollbar(box, BOTTOM);
- }
- } else if (box->type == BOX_TABLE) {
- if (box->style != NULL) {
- enum css_width_e wtype;
- css_fixed width = 0;
- css_unit unit = CSS_UNIT_PX;
- wtype = css_computed_width(box->style, &width,
- &unit);
+/**
+ * Calculate minimum and maximum width of a line.
+ *
+ * \param first a box in an inline container
+ * \param line_min updated to minimum width of line starting at first
+ * \param line_max updated to maximum width of line starting at first
+ * \param first_line true iff this is the first line in the inline container
+ * \param line_has_height updated to true or false, depending on line
+ * \param font_func Font functions.
+ * \return first box in next line, or 0 if no more lines
+ * \post 0 <= *line_min <= *line_max
+ */
+static struct box *
+layout_minmax_line(struct box *first,
+ int *line_min,
+ int *line_max,
+ bool first_line,
+ bool *line_has_height,
+ const struct gui_layout_table *font_func)
+{
+ int min = 0, max = 0, width, height, fixed;
+ float frac;
+ size_t i, j;
+ struct box *b;
+ struct box *block;
+ plot_font_style_t fstyle;
+ bool no_wrap;
- if (wtype == CSS_WIDTH_AUTO) {
- /* max available width may be
- * diminished due to floats. */
- int x0, x1, top;
- struct box *left, *right;
- top = cy + max_pos_margin -
- max_neg_margin;
- top = (top > y) ? top : y;
- x0 = cx;
- x1 = cx + box->parent->width -
- box->parent->padding[LEFT] -
- box->parent->padding[RIGHT];
- find_sides(block->float_children,
- top, top, &x0, &x1,
- &left, &right);
- /* calculate min required left & right
- * margins needed to avoid floats */
- lm = x0 - cx;
- rm = cx + box->parent->width -
- box->parent->padding[LEFT] -
- box->parent->padding[RIGHT] -
- x1;
- }
- }
- if (!layout_table(box, box->parent->width - lm - rm,
- content))
- return false;
- layout_solve_width(box, box->parent->width, box->width,
- lm, rm, -1, -1);
- }
+ assert(first->parent);
+ assert(first->parent->parent);
+ assert(first->parent->parent->style);
- /* Position box: horizontal. */
- box->x = box->parent->padding[LEFT] + box->margin[LEFT] +
- box->border[LEFT].width;
- cx += box->x;
+ block = first->parent->parent;
+ no_wrap = (css_computed_white_space(block->style) ==
+ CSS_WHITE_SPACE_NOWRAP ||
+ css_computed_white_space(block->style) ==
+ CSS_WHITE_SPACE_PRE);
- /* Position box: vertical. */
- if (box->border[TOP].width) {
- box->y += box->border[TOP].width;
- cy += box->border[TOP].width;
- }
+ *line_has_height = false;
- /* Vertical margin */
- if (((box->type == BOX_BLOCK &&
- (box->flags & HAS_HEIGHT)) ||
- box->type == BOX_TABLE ||
- (box->type == BOX_INLINE_CONTAINER &&
- box != box->parent->children) ||
- margin_collapse == box) &&
- in_margin == true) {
- /* Margin goes above this box. */
- cy += max_pos_margin - max_neg_margin;
- box->y += max_pos_margin - max_neg_margin;
+ /* corresponds to the pass 1 loop in layout_line() */
+ for (b = first; b; b = b->next) {
+ enum css_width_e wtype;
+ enum css_height_e htype;
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
- /* Current margin has been applied. */
- in_margin = false;
- max_pos_margin = max_neg_margin = 0;
+ assert(b->type == BOX_INLINE || b->type == BOX_INLINE_BLOCK ||
+ b->type == BOX_FLOAT_LEFT ||
+ b->type == BOX_FLOAT_RIGHT ||
+ b->type == BOX_BR || b->type == BOX_TEXT ||
+ b->type == BOX_INLINE_END);
+
+#ifdef LAYOUT_DEBUG
+ LOG("%p: min %i, max %i", b, min, max);
+#endif
+
+ if (b->type == BOX_BR) {
+ b = b->next;
+ break;
}
- /* Handle clearance */
- if (box->type != BOX_INLINE_CONTAINER &&
- (y > 0) && (cy < y)) {
- /* box clears something*/
- box->y += y - cy;
- cy = y;
+ if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT) {
+ assert(b->children);
+ if (b->children->type == BOX_BLOCK)
+ layout_minmax_block(b->children, font_func);
+ else
+ layout_minmax_table(b->children, font_func);
+ b->min_width = b->children->min_width;
+ b->max_width = b->children->max_width;
+ if (min < b->min_width)
+ min = b->min_width;
+ max += b->max_width;
+ continue;
}
- /* Unless the box has an overflow style of visible, the box
- * establishes a new block context. */
- if (box->type == BOX_BLOCK && box->style &&
- (overflow_x != CSS_OVERFLOW_VISIBLE ||
- overflow_y != CSS_OVERFLOW_VISIBLE)) {
+ if (b->type == BOX_INLINE_BLOCK) {
+ layout_minmax_block(b, font_func);
+ if (min < b->min_width)
+ min = b->min_width;
+ max += b->max_width;
- layout_block_context(box, viewport_height, content);
+ if (b->flags & HAS_HEIGHT)
+ *line_has_height = true;
+ continue;
+ }
- cy += box->padding[TOP];
+ assert(b->style);
+ font_plot_style_from_css(b->style, &fstyle);
- if (box->height == AUTO) {
- box->height = 0;
- layout_block_add_scrollbar(box, BOTTOM);
- }
+ if (b->type == BOX_INLINE && !b->object &&
+ !(b->flags & REPLACE_DIM) &&
+ !(b->flags & IFRAME)) {
+ fixed = frac = 0;
+ calculate_mbp_width(b->style, LEFT, true, true, true,
+ &fixed, &frac);
+ if (!b->inline_end)
+ calculate_mbp_width(b->style, RIGHT,
+ true, true, true,
+ &fixed, &frac);
+ if (0 < fixed)
+ max += fixed;
+ *line_has_height = true;
+ /* \todo update min width, consider fractional extra */
+ } else if (b->type == BOX_INLINE_END) {
+ fixed = frac = 0;
+ calculate_mbp_width(b->inline_end->style, RIGHT,
+ true, true, true,
+ &fixed, &frac);
+ if (0 < fixed)
+ max += fixed;
- cx -= box->x;
- cy += box->height + box->padding[BOTTOM] +
- box->border[BOTTOM].width;
- y = box->y + box->padding[TOP] + box->height +
- box->padding[BOTTOM] +
- box->border[BOTTOM].width;
+ if (b->next) {
+ if (b->space == UNKNOWN_WIDTH) {
+ font_func->width(&fstyle, " ", 1,
+ &b->space);
+ }
+ max += b->space;
+ }
- /* Skip children, because they are done in the new
- * block context */
- goto advance_to_next_box;
+ *line_has_height = true;
+ continue;
}
-#ifdef LAYOUT_DEBUG
- LOG("box %p, cx %i, cy %i", box, cx, cy);
-#endif
+ if (!b->object && !(b->flags & IFRAME) && !b->gadget &&
+ !(b->flags & REPLACE_DIM)) {
+ /* inline non-replaced, 10.3.1 and 10.6.1 */
+ bool no_wrap_box;
+ if (!b->text)
+ continue;
- /* Layout (except tables). */
- if (box->object) {
- if (!layout_block_object(box))
- return false;
+ no_wrap_box = (css_computed_white_space(b->style) ==
+ CSS_WHITE_SPACE_NOWRAP ||
+ css_computed_white_space(b->style) ==
+ CSS_WHITE_SPACE_PRE);
- } else if (box->type == BOX_INLINE_CONTAINER) {
- box->width = box->parent->width;
- if (!layout_inline_container(box, box->width, block,
- cx, cy, content))
- return false;
+ if (b->width == UNKNOWN_WIDTH) {
+ /** \todo handle errors */
- } else if (box->type == BOX_TABLE) {
- /* Move down to avoid floats if necessary. */
- int x0, x1;
- struct box *left, *right;
- y = cy;
- while (1) {
- enum css_width_e wtype;
- css_fixed width = 0;
- css_unit unit = CSS_UNIT_PX;
+ /* If it's a select element, we must use the
+ * width of the widest option text */
+ if (b->parent->parent->gadget &&
+ b->parent->parent->gadget->type
+ == GADGET_SELECT) {
+ int opt_maxwidth = 0;
+ struct form_option *o;
- wtype = css_computed_width(box->style,
- &width, &unit);
+ for (o = b->parent->parent->gadget->
+ data.select.items; o;
+ o = o->next) {
+ int opt_width;
+ font_func->width(&fstyle,
+ o->text,
+ strlen(o->text),
+ &opt_width);
- x0 = cx;
- x1 = cx + box->parent->width;
- find_sides(block->float_children, y,
- y + box->height,
- &x0, &x1, &left, &right);
- if (wtype == CSS_WIDTH_AUTO)
- break;
- if (box->width <= x1 - x0)
- break;
- if (!left && !right)
- break;
- else if (!left)
- y = right->y + right->height + 1;
- else if (!right)
- y = left->y + left->height + 1;
- else if (left->y + left->height <
- right->y + right->height)
- y = left->y + left->height + 1;
- else
- y = right->y + right->height + 1;
+ if (opt_maxwidth < opt_width)
+ opt_maxwidth =opt_width;
+ }
+
+ b->width = opt_maxwidth;
+ if (nsoption_bool(core_select_menu))
+ b->width += SCROLLBAR_WIDTH;
+
+ } else {
+ font_func->width(&fstyle, b->text,
+ b->length, &b->width);
+ b->flags |= MEASURED;
+ }
+ }
+ max += b->width;
+ if (b->next) {
+ if (b->space == UNKNOWN_WIDTH) {
+ font_func->width(&fstyle, " ", 1,
+ &b->space);
+ }
+ max += b->space;
}
- box->x += x0 - cx;
- cx = x0;
- box->y += y - cy;
- cy = y;
- }
- /* Advance to next box. */
- if (box->type == BOX_BLOCK && !box->object && !(box->iframe) &&
- box->children) {
- /* Down into children. */
+ if (no_wrap) {
+ /* Don't wrap due to block style,
+ * so min is the same as max */
+ min = max;
- if (box == margin_collapse) {
- /* Current margin collapsed though to this box.
- * Unset margin_collapse. */
- margin_collapse = NULL;
+ } else if (no_wrap_box) {
+ /* This inline box can't be wrapped,
+ * for min, consider box's width */
+ if (min < b->width)
+ min = b->width;
+
+ } else if (b->parent->flags & NEED_MIN) {
+ /* If we care what the minimum width is,
+ * calculate it. (It's only needed if we're
+ * shrinking-to-fit.) */
+ /* min = widest single word */
+ i = 0;
+ do {
+ for (j = i; j != b->length &&
+ b->text[j] != ' '; j++)
+ ;
+ font_func->width(&fstyle, b->text + i,
+ j - i, &width);
+ if (min < width)
+ min = width;
+ i = j + 1;
+ } while (j != b->length);
}
- y = box->padding[TOP];
- box = box->children;
- box->y = y;
- cy += y;
- continue;
- } else if (box->type == BOX_BLOCK || box->object ||
- box->flags & IFRAME)
- cy += box->padding[TOP];
+ *line_has_height = true;
- if (box->type == BOX_BLOCK && box->height == AUTO) {
- box->height = 0;
- layout_block_add_scrollbar(box, BOTTOM);
+ continue;
}
- cy += box->height + box->padding[BOTTOM] +
- box->border[BOTTOM].width;
- cx -= box->x;
- y = box->y + box->padding[TOP] + box->height +
- box->padding[BOTTOM] +
- box->border[BOTTOM].width;
+ /* inline replaced, 10.3.2 and 10.6.2 */
+ assert(b->style);
- advance_to_next_box:
- if (!box->next) {
- /* No more siblings:
- * up to first ancestor with a sibling. */
+ /* calculate box width */
+ wtype = css_computed_width(b->style, &value, &unit);
+ if (wtype == CSS_WIDTH_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ /*
+ b->width = FPCT_OF_INT_TOINT(value, width);
+ */
- do {
- if (box == margin_collapse) {
- /* Current margin collapsed though to
- * this box. Unset margin_collapse. */
- margin_collapse = NULL;
- }
+ width = AUTO;
+ } else {
+ width = FIXTOINT(nscss_len2px(value, unit,
+ b->style));
+ if (width < 0)
+ width = 0;
+ }
+ } else {
+ width = AUTO;
+ }
- /* Apply bottom margin */
- if (max_pos_margin < box->margin[BOTTOM])
- max_pos_margin = box->margin[BOTTOM];
- else if (max_neg_margin < -box->margin[BOTTOM])
- max_neg_margin = -box->margin[BOTTOM];
+ /* height */
+ htype = css_computed_height(b->style, &value, &unit);
+ if (htype == CSS_HEIGHT_SET) {
+ height = FIXTOINT(nscss_len2px(value, unit, b->style));
+ } else {
+ height = AUTO;
+ }
- box = box->parent;
- if (box == block)
- break;
+ if (b->object || (b->flags & REPLACE_DIM)) {
+ if (b->object) {
+ int temp_height = height;
+ layout_get_object_dimensions(b,
+ &width, &temp_height,
+ INT_MIN, INT_MAX,
+ INT_MIN, INT_MAX);
+ }
- /* Margin is invalidated if this is a box
- * margins can't collapse through. */
- if (box->type == BOX_BLOCK &&
- box->flags & MAKE_HEIGHT) {
- margin_collapse = NULL;
- in_margin = false;
- max_pos_margin = max_neg_margin = 0;
- }
+ fixed = frac = 0;
+ calculate_mbp_width(b->style, LEFT, true, true, true,
+ &fixed, &frac);
+ calculate_mbp_width(b->style, RIGHT, true, true, true,
+ &fixed, &frac);
- if (box->height == AUTO) {
- box->height = y - box->padding[TOP];
+ if (0 < width + fixed)
+ width += fixed;
+ } else if (b->flags & IFRAME) {
+ /* TODO: handle percentage widths properly */
+ if (width == AUTO)
+ width = 400;
- if (box->type == BOX_BLOCK)
- layout_block_add_scrollbar(box,
- BOTTOM);
- } else
- cy += box->height -
- (y - box->padding[TOP]);
+ fixed = frac = 0;
+ calculate_mbp_width(b->style, LEFT, true, true, true,
+ &fixed, &frac);
+ calculate_mbp_width(b->style, RIGHT, true, true, true,
+ &fixed, &frac);
- /* Apply any min-height and max-height to
- * boxes in normal flow */
- if (box->style &&
- css_computed_position(box->style) !=
- CSS_POSITION_ABSOLUTE &&
- layout_apply_minmax_height(box,
- NULL)) {
- /* Height altered */
- /* Set current cy */
- cy += box->height -
- (y - box->padding[TOP]);
- }
+ if (0 < width + fixed)
+ width += fixed;
+ } else {
+ /* form control with no object */
+ if (width == AUTO)
+ width = FIXTOINT(nscss_len2px(INTTOFIX(1),
+ CSS_UNIT_EM, b->style));
+ }
- cy += box->padding[BOTTOM] +
- box->border[BOTTOM].width;
- cx -= box->x;
- y = box->y + box->padding[TOP] + box->height +
- box->padding[BOTTOM] +
- box->border[BOTTOM].width;
+ if (min < width)
+ min = width;
+ max += width;
- } while (box->next == NULL);
- if (box == block)
- break;
- }
+ *line_has_height = true;
+ }
- /* To next sibling. */
+ if (first_line) {
+ /* todo: handle percentage values properly */
+ /* todo: handle text-indent interaction with floats */
+ int text_indent = layout_text_indent(
+ first->parent->parent->style, 100);
+ min = (min + text_indent < 0) ? 0 : min + text_indent;
+ max = (max + text_indent < 0) ? 0 : max + text_indent;
+ }
- if (box == margin_collapse) {
- /* Current margin collapsed though to this box.
- * Unset margin_collapse. */
- margin_collapse = NULL;
- }
+ *line_min = min;
+ *line_max = max;
- if (max_pos_margin < box->margin[BOTTOM])
- max_pos_margin = box->margin[BOTTOM];
- else if (max_neg_margin < -box->margin[BOTTOM])
- max_neg_margin = -box->margin[BOTTOM];
+#ifdef LAYOUT_DEBUG
+ LOG("line_min %i, line_max %i", min, max);
+#endif
- box = box->next;
- box->y = y;
- }
+ assert(b != first);
+ assert(0 <= *line_min);
+ assert(*line_min <= *line_max);
+ return b;
+}
- /* Account for bottom margin of last contained block */
- cy += max_pos_margin - max_neg_margin;
- /* 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].width + box->margin[BOTTOM];
- if (cy < y)
- cy = y;
- }
+/**
+ * Calculate minimum and maximum width of an inline container.
+ *
+ * \param inline_container box of type INLINE_CONTAINER
+ * \param[out] has_height set to true if container has height
+ * \param font_func Font functions.
+ * \post inline_container->min_width and inline_container->max_width filled in,
+ * 0 <= inline_container->min_width <= inline_container->max_width
+ */
+static void
+layout_minmax_inline_container(struct box *inline_container,
+ bool *has_height,
+ const struct gui_layout_table *font_func)
+{
+ struct box *child;
+ int line_min = 0, line_max = 0;
+ int min = 0, max = 0;
+ bool first_line = true;
+ bool line_has_height;
- if (block->height == AUTO) {
- block->height = cy - block->padding[TOP];
- if (block->type == BOX_BLOCK)
- layout_block_add_scrollbar(block, BOTTOM);
- }
+ assert(inline_container->type == BOX_INLINE_CONTAINER);
- if (block->style && css_computed_position(block->style) !=
- CSS_POSITION_ABSOLUTE) {
- /* Block is in normal flow */
- layout_apply_minmax_height(block, NULL);
- }
+ /* check if the widths have already been calculated */
+ if (inline_container->max_width != UNKNOWN_MAX_WIDTH)
+ return;
- if (block->gadget &&
- (block->gadget->type == GADGET_TEXTAREA ||
- block->gadget->type == GADGET_PASSWORD ||
- block->gadget->type == GADGET_TEXTBOX)) {
- int ta_width = block->padding[LEFT] + block->width +
- block->padding[RIGHT];
- int ta_height = block->padding[TOP] + block->height +
- block->padding[BOTTOM];
- textarea_set_layout(block->gadget->data.text.ta,
- ta_width, ta_height,
- block->padding[TOP], block->padding[RIGHT],
- block->padding[BOTTOM], block->padding[LEFT]);
+ *has_height = false;
+
+ for (child = inline_container->children; child; ) {
+ child = layout_minmax_line(child, &line_min, &line_max,
+ first_line, &line_has_height, font_func);
+ if (min < line_min)
+ min = line_min;
+ if (max < line_max)
+ max = line_max;
+ first_line = false;
+ *has_height |= line_has_height;
}
- return true;
+ inline_container->min_width = min;
+ inline_container->max_width = max;
+
+ assert(0 <= inline_container->min_width &&
+ inline_container->min_width <=
+ inline_container->max_width);
}
@@ -694,9 +654,8 @@ bool layout_block_context(struct box *block, int viewport_height,
* \post block->min_width and block->max_width filled in,
* 0 <= block->min_width <= block->max_width
*/
-
-void layout_minmax_block(struct box *block,
- const struct font_functions *font_func)
+static void
+layout_minmax_block(struct box *block, const struct gui_layout_table *font_func)
{
struct box *child;
int min = 0, max = 0;
@@ -891,710 +850,39 @@ void layout_minmax_block(struct box *block,
/**
- * Find next block that current margin collapses to.
- *
- * \param box box to start tree-order search from (top margin is included)
- * \param block box responsible for current block fromatting context
- * \param viewport_height height of viewport in px
- * \param max_pos_margin updated to to maximum positive margin encountered
- * \param max_neg_margin updated to to maximum negative margin encountered
- * \return next box that current margin collapses to, or NULL if none.
- */
-
-struct box* layout_next_margin_block(struct box *box, struct box *block,
- int viewport_height, int *max_pos_margin, int *max_neg_margin)
-{
- assert(block != NULL);
-
- while (box != NULL) {
-
- if (box->type == BOX_INLINE_CONTAINER || (box->style &&
- (css_computed_position(box->style) !=
- CSS_POSITION_ABSOLUTE &&
- css_computed_position(box->style) !=
- CSS_POSITION_FIXED))) {
- /* Not positioned */
-
- /* Get margins */
- if (box->style) {
- layout_find_dimensions(box->parent->width,
- viewport_height, box,
- box->style,
- NULL, NULL, NULL, NULL,
- NULL, NULL, box->margin,
- box->padding, box->border);
-
- /* Apply top margin */
- if (*max_pos_margin < box->margin[TOP])
- *max_pos_margin = box->margin[TOP];
- else if (*max_neg_margin < -box->margin[TOP])
- *max_neg_margin = -box->margin[TOP];
- }
-
- /* Check whether box is the box current margin collapses
- * to */
- if (box->flags & MAKE_HEIGHT ||
- box->border[TOP].width ||
- box->padding[TOP] ||
- (box->style &&
- css_computed_overflow_y(box->style) !=
- CSS_OVERFLOW_VISIBLE) ||
- (box->type == BOX_INLINE_CONTAINER &&
- box != box->parent->children)) {
- /* Collapse to this box; return it */
- return box;
- }
- }
-
-
- /* Find next box */
- if (box->type == BOX_BLOCK && !box->object && box->children &&
- box->style &&
- css_computed_overflow_y(box->style) ==
- CSS_OVERFLOW_VISIBLE) {
- /* Down into children. */
- box = box->children;
- } else {
- if (!box->next) {
- /* No more siblings:
- * Go up to first ancestor with a sibling. */
- do {
- /* Apply bottom margin */
- if (*max_pos_margin <
- box->margin[BOTTOM])
- *max_pos_margin =
- box->margin[BOTTOM];
- else if (*max_neg_margin <
- -box->margin[BOTTOM])
- *max_neg_margin =
- -box->margin[BOTTOM];
-
- box = box->parent;
- } while (box != block && !box->next);
-
- if (box == block) {
- /* Margins don't collapse with stuff
- * outside the block formatting context
- */
- return block;
- }
- }
-
- /* Apply bottom margin */
- if (*max_pos_margin < box->margin[BOTTOM])
- *max_pos_margin = box->margin[BOTTOM];
- else if (*max_neg_margin < -box->margin[BOTTOM])
- *max_neg_margin = -box->margin[BOTTOM];
-
- /* To next sibling. */
- box = box->next;
-
- /* Get margins */
- if (box->style) {
- layout_find_dimensions(box->parent->width,
- viewport_height, box,
- box->style,
- NULL, NULL, NULL, NULL,
- NULL, NULL, box->margin,
- box->padding, box->border);
- }
- }
- }
-
- return NULL;
-}
-
-
-/**
- * Layout a block which contains an object.
- *
- * \param block box of type BLOCK, INLINE_BLOCK, TABLE, or TABLE_CELL
- * \return true on success, false on memory exhaustion
- */
-
-bool layout_block_object(struct box *block)
-{
- assert(block);
- assert(block->type == BOX_BLOCK ||
- block->type == BOX_INLINE_BLOCK ||
- block->type == BOX_TABLE ||
- block->type == BOX_TABLE_CELL);
- assert(block->object);
-
-#ifdef LAYOUT_DEBUG
- LOG("block %p, object %s, width %i", block, hlcache_handle_get_url(block->object), block->width);
-#endif
-
- if (content_get_type(block->object) == CONTENT_HTML) {
- content_reformat(block->object, false, block->width, 1);
- } else {
- /* Non-HTML objects */
- /* this case handled already in
- * layout_block_find_dimensions() */
- }
-
- return true;
-}
-
-
-/**
- * Compute the size of replaced boxes with auto dimensions, according to
- * content.
- *
- * \param box Box with object
- * \param width Width value in px or AUTO. If AUTO, updated to value in px.
- * \param height Height value in px or AUTO. If AUTO, updated to value in px.
- * \param min_width Box's min width, as given by layout_find_dimensions.
- * \param max_width Box's max width, as given by layout_find_dimensions.
- * \param min_height Box's min height, as given by layout_find_dimensions.
- * \param max_height Box's max height, as given by layout_find_dimensions.
- *
- * See CSS 2.1 sections 10.3 and 10.6.
- */
-
-void layout_get_object_dimensions(struct box *box, int *width, int *height,
- int min_width, int max_width, int min_height, int max_height)
-{
- assert(box->object != NULL);
- assert(width != NULL && height != NULL);
-
- if (*width == AUTO && *height == AUTO) {
- /* No given dimensions */
-
- bool scaled = false;
- int intrinsic_width = content_get_width(box->object);
- int intrinsic_height = content_get_height(box->object);
-
- /* use intrinsic dimensions */
- *width = intrinsic_width;
- *height = intrinsic_height;
-
- /* Deal with min/max-width first */
- if (min_width > 0 && min_width > *width) {
- *width = min_width;
- scaled = true;
- }
- if (max_width >= 0 && max_width < *width) {
- *width = max_width;
- scaled = true;
- }
-
- if (scaled && (intrinsic_width != 0)) {
- /* Update height */
- *height = (*width * intrinsic_height) /
- intrinsic_width;
- }
-
- scaled = false;
- /* Deal with min/max-height */
- if (min_height > 0 && min_height > *height) {
- *height = min_height;
- scaled = true;
- }
- if (max_height >= 0 && max_height < *height) {
- *height = max_height;
- scaled = true;
- }
-
- if (scaled && (intrinsic_height != 0)) {
- /* Update width */
- *width = (*height * intrinsic_width) /
- intrinsic_height;
- }
-
- } else if (*width == AUTO) {
- /* Have given height; width is calculated from the given height
- * and ratio of intrinsic dimensions */
- int intrinsic_width = content_get_width(box->object);
- int intrinsic_height = content_get_height(box->object);
-
- if (intrinsic_height != 0)
- *width = (*height * intrinsic_width) /
- intrinsic_height;
- else
- *width = intrinsic_width;
-
- if (min_width > 0 && min_width > *width)
- *width = min_width;
- if (max_width >= 0 && max_width < *width)
- *width = max_width;
-
- } else if (*height == AUTO) {
- /* Have given width; height is calculated from the given width
- * and ratio of intrinsic dimensions */
- int intrinsic_width = content_get_width(box->object);
- int intrinsic_height = content_get_height(box->object);
-
- if (intrinsic_width != 0)
- *height = (*width * intrinsic_height) /
- intrinsic_width;
- else
- *height = intrinsic_height;
- }
-}
-
-
-/**
- * Compute dimensions of box, margins, paddings, and borders for a block-level
- * element.
- *
- * \param available_width Max width available in pixels
- * \param viewport_height Height of viewport in pixels or -ve if unknown
- * \param lm min left margin required to avoid floats in px.
- * zero if not applicable
- * \param rm min right margin required to avoid floats in px.
- * zero if not applicable
- * \param box box to find dimensions of. updated with new width,
- * height, margins, borders and paddings
- *
- * See CSS 2.1 10.3.3, 10.3.4, 10.6.2, and 10.6.3.
- */
-
-void layout_block_find_dimensions(int available_width, int viewport_height,
- int lm, int rm, struct box *box)
-{
- int width, max_width, min_width;
- int height, max_height, min_height;
- int *margin = box->margin;
- int *padding = box->padding;
- 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,
- &max_height, &min_height, margin, padding, border);
-
- if (box->object && !(box->flags & REPLACE_DIM) &&
- content_get_type(box->object) != CONTENT_HTML) {
- /* block-level replaced element, see 10.3.4 and 10.6.2 */
- layout_get_object_dimensions(box, &width, &height,
- min_width, max_width, min_height, max_height);
- }
-
- box->width = layout_solve_width(box, available_width, width, lm, rm,
- max_width, min_width);
- box->height = height;
-
- if (margin[TOP] == AUTO)
- margin[TOP] = 0;
- if (margin[BOTTOM] == AUTO)
- margin[BOTTOM] = 0;
-}
-
-/**
- * Manimpulate box height according to CSS min-height and max-height properties
- *
- * \param box block to modify with any min-height or max-height
- * \param container containing block for absolutely positioned elements, or
- * NULL for non absolutely positioned elements.
- * \return whether the height has been changed
- */
-
-bool layout_apply_minmax_height(struct box *box, struct box *container)
-{
- int h;
- struct box *containing_block = NULL;
- bool updated = false;
-
- /* Find containing block for percentage heights */
- if (box->style != NULL && css_computed_position(box->style) ==
- CSS_POSITION_ABSOLUTE) {
- /* Box is absolutely positioned */
- assert(container);
- containing_block = container;
- } else if (box->float_container && box->style != NULL &&
- (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 (box->style) {
- enum css_height_e htype = CSS_HEIGHT_AUTO;
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
-
- if (containing_block) {
- htype = css_computed_height(containing_block->style,
- &value, &unit);
- }
-
- /* max-height */
- if (css_computed_max_height(box->style, &value, &unit) ==
- CSS_MAX_HEIGHT_SET) {
- if (unit == CSS_UNIT_PCT) {
- if (containing_block &&
- containing_block->height != AUTO &&
- (css_computed_position(box->style) ==
- CSS_POSITION_ABSOLUTE ||
- htype == CSS_HEIGHT_SET)) {
- /* Box is absolutely positioned or its
- * containing block has a valid
- * specified height. (CSS 2.1
- * Section 10.5) */
- h = FPCT_OF_INT_TOINT(value,
- containing_block->height);
- if (h < box->height) {
- box->height = h;
- updated = true;
- }
- }
- } else {
- h = FIXTOINT(nscss_len2px(value, unit,
- box->style));
- if (h < box->height) {
- box->height = h;
- updated = true;
- }
- }
- }
-
- /* min-height */
- if (css_computed_min_height(box->style, &value, &unit) ==
- CSS_MIN_HEIGHT_SET) {
- if (unit == CSS_UNIT_PCT) {
- if (containing_block &&
- containing_block->height != AUTO &&
- (css_computed_position(box->style) ==
- CSS_POSITION_ABSOLUTE ||
- htype == CSS_HEIGHT_SET)) {
- /* Box is absolutely positioned or its
- * containing block has a valid
- * specified height. (CSS 2.1
- * Section 10.5) */
- h = FPCT_OF_INT_TOINT(value,
- containing_block->height);
- if (h > box->height) {
- box->height = h;
- updated = true;
- }
- }
- } else {
- h = FIXTOINT(nscss_len2px(value, unit,
- box->style));
- if (h > box->height) {
- box->height = h;
- updated = true;
- }
- }
- }
- }
- return updated;
-}
-
-/**
- * Manipulate a block's [RB]padding/height/width to accommodate scrollbars
- *
- * \param box Box to apply scrollbar space too. Must be BOX_BLOCK.
- * \param which Which scrollbar to make space for. Must be RIGHT or BOTTOM.
- */
-
-void layout_block_add_scrollbar(struct box *box, int which)
-{
- enum css_overflow_e overflow_x, overflow_y;
-
- assert(box->type == BOX_BLOCK && (which == RIGHT || which == BOTTOM));
-
- if (box->style == NULL)
- return;
-
- overflow_x = css_computed_overflow_x(box->style);
- overflow_y = css_computed_overflow_y(box->style);
-
- if (which == BOTTOM &&
- (overflow_x == CSS_OVERFLOW_SCROLL ||
- overflow_x == CSS_OVERFLOW_AUTO ||
- (box->object &&
- content_get_type(box->object) == CONTENT_HTML))) {
- /* make space for scrollbar, unless height is AUTO */
- if (box->height != AUTO &&
- (overflow_x == CSS_OVERFLOW_SCROLL ||
- box_hscrollbar_present(box))) {
- box->padding[BOTTOM] += SCROLLBAR_WIDTH;
- }
-
- } else if (which == RIGHT &&
- (overflow_y == CSS_OVERFLOW_SCROLL ||
- overflow_y == CSS_OVERFLOW_AUTO ||
- (box->object &&
- content_get_type(box->object) == CONTENT_HTML))) {
- /* make space for scrollbars, unless width is AUTO */
- enum css_height_e htype;
- css_fixed height = 0;
- css_unit hunit = CSS_UNIT_PX;
- htype = css_computed_height(box->style, &height, &hunit);
-
- if (which == RIGHT && box->width != AUTO &&
- htype == CSS_HEIGHT_SET &&
- (overflow_y == CSS_OVERFLOW_SCROLL ||
- box_vscrollbar_present(box))) {
- box->width -= SCROLLBAR_WIDTH;
- box->padding[RIGHT] += SCROLLBAR_WIDTH;
- }
- }
-}
-
-/**
- * Solve the width constraint as given in CSS 2.1 section 10.3.3.
- *
- * \param box Box to solve constraint for
- * \param available_width Max width available in pixels
- * \param width Current box width
- * \param lm Min left margin required to avoid floats in px.
- * zero if not applicable
- * \param rm Min right margin required to avoid floats in px.
- * zero if not applicable
- * \param max_width Box max-width ( -ve means no max-width to apply)
- * \param min_width Box min-width ( <=0 means no min-width to apply)
- * \return New box width
- *
- * \post \a box's left/right margins will be updated.
- */
-
-int layout_solve_width(struct box *box, int available_width, int width,
- int lm, int rm, int max_width, int min_width)
-{
- bool auto_width = false;
-
- /* Increase specified left/right margins */
- if (box->margin[LEFT] != AUTO && box->margin[LEFT] < lm &&
- box->margin[LEFT] >= 0)
- box->margin[LEFT] = lm;
- if (box->margin[RIGHT] != AUTO && box->margin[RIGHT] < rm &&
- box->margin[RIGHT] >= 0)
- box->margin[RIGHT] = rm;
-
- /* Find width */
- if (width == AUTO) {
- /* any other 'auto' become 0 or the minimum required values */
- if (box->margin[LEFT] == AUTO)
- box->margin[LEFT] = lm;
- if (box->margin[RIGHT] == AUTO)
- box->margin[RIGHT] = rm;
-
- width = available_width -
- (box->margin[LEFT] + box->border[LEFT].width +
- box->padding[LEFT] + box->padding[RIGHT] +
- box->border[RIGHT].width + box->margin[RIGHT]);
- width = width < 0 ? 0 : width;
- auto_width = true;
- }
-
- if (max_width >= 0 && width > max_width) {
- /* max-width is admissable and width exceeds max-width */
- width = max_width;
- auto_width = false;
- }
-
- if (min_width > 0 && width < min_width) {
- /* min-width is admissable and width is less than max-width */
- width = min_width;
- auto_width = false;
- }
-
- /* Width was auto, and unconstrained by min/max width, so we're done */
- if (auto_width)
- return width;
-
- /* Width was not auto, or was constrained by min/max width
- * Need to compute left/right margins */
-
- /* HTML alignment (only applies to over-constrained boxes) */
- if (box->margin[LEFT] != AUTO && box->margin[RIGHT] != AUTO &&
- box->parent != NULL && box->parent->style != NULL) {
- switch (css_computed_text_align(box->parent->style)) {
- case CSS_TEXT_ALIGN_LIBCSS_RIGHT:
- box->margin[LEFT] = AUTO;
- box->margin[RIGHT] = 0;
- break;
- case CSS_TEXT_ALIGN_LIBCSS_CENTER:
- box->margin[LEFT] = box->margin[RIGHT] = AUTO;
- break;
- case CSS_TEXT_ALIGN_LIBCSS_LEFT:
- box->margin[LEFT] = 0;
- box->margin[RIGHT] = AUTO;
- break;
- default:
- /* Leave it alone; no HTML alignment */
- break;
- }
- }
-
- if (box->margin[LEFT] == AUTO && box->margin[RIGHT] == AUTO) {
- /* make the margins equal, centering the element */
- box->margin[LEFT] = box->margin[RIGHT] =
- (available_width - lm - rm -
- (box->border[LEFT].width + box->padding[LEFT] +
- width + box->padding[RIGHT] +
- box->border[RIGHT].width)) / 2;
-
- if (box->margin[LEFT] < 0) {
- box->margin[RIGHT] += box->margin[LEFT];
- box->margin[LEFT] = 0;
- }
-
- box->margin[LEFT] += lm;
-
- } else if (box->margin[LEFT] == AUTO) {
- box->margin[LEFT] = available_width - lm -
- (box->border[LEFT].width + box->padding[LEFT] +
- width + box->padding[RIGHT] +
- box->border[RIGHT].width + box->margin[RIGHT]);
- box->margin[LEFT] = box->margin[LEFT] < lm
- ? lm : box->margin[LEFT];
- } else {
- /* margin-right auto or "over-constrained" */
- box->margin[RIGHT] = available_width - rm -
- (box->margin[LEFT] + box->border[LEFT].width +
- box->padding[LEFT] + width +
- box->padding[RIGHT] +
- box->border[RIGHT].width);
- }
-
- return width;
-}
-
-
-/**
- * Compute dimensions of box, margins, paddings, and borders for a floating
- * element using shrink-to-fit. Also used for inline-blocks.
+ * Under some circumstances, specified dimensions for form elements include
+ * borders and padding.
*
- * \param available_width Max width available in pixels
- * \param style Box's style
- * \param box Box for which to find dimensions
- * Box margins, borders, paddings, width and
- * height are updated.
+ * \param box gadget to adjust dimensions of
+ * \param percentage whether the gadget has its dimension specified as a
+ * percentage
+ * \param available_width width of containing block
+ * \param setwidth set true if the dimension to be tweaked is a width,
+ * else set false for a height
+ * \param dimension current value for given width/height dimension.
+ * updated to new value after consideration of
+ * gadget properties.
*/
-
-void layout_float_find_dimensions(int available_width,
- const css_computed_style *style, struct box *box)
+static void layout_tweak_form_dimensions(struct box *box, bool percentage,
+ int available_width, bool setwidth, int *dimension)
{
- int width, height, max_width, min_width, max_height, min_height;
- int *margin = box->margin;
- int *padding = box->padding;
- struct box_border *border = box->border;
- enum css_overflow_e overflow_x = css_computed_overflow_x(style);
- enum css_overflow_e overflow_y = css_computed_overflow_y(style);
- int scrollbar_width_x =
- (overflow_x == CSS_OVERFLOW_SCROLL ||
- overflow_x == CSS_OVERFLOW_AUTO) ?
- SCROLLBAR_WIDTH : 0;
- int scrollbar_width_y =
- (overflow_y == CSS_OVERFLOW_SCROLL ||
- overflow_y == CSS_OVERFLOW_AUTO) ?
- SCROLLBAR_WIDTH : 0;
-
- layout_find_dimensions(available_width, -1, box, style, &width, &height,
- &max_width, &min_width, &max_height, &min_height,
- margin, padding, border);
-
- if (margin[LEFT] == AUTO)
- margin[LEFT] = 0;
- if (margin[RIGHT] == AUTO)
- margin[RIGHT] = 0;
-
- if (box->gadget == NULL) {
- padding[RIGHT] += scrollbar_width_y;
- padding[BOTTOM] += scrollbar_width_x;
- }
-
- if (box->object && !(box->flags & REPLACE_DIM) &&
- content_get_type(box->object) != CONTENT_HTML) {
- /* Floating replaced element, with intrinsic width or height.
- * See 10.3.6 and 10.6.2 */
- layout_get_object_dimensions(box, &width, &height,
- min_width, max_width, min_height, max_height);
- } else if (box->gadget && (box->gadget->type == GADGET_TEXTBOX ||
- box->gadget->type == GADGET_PASSWORD ||
- box->gadget->type == GADGET_FILE ||
- box->gadget->type == GADGET_TEXTAREA)) {
- 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);
-
- if (box->gadget->type == GADGET_TEXTBOX ||
- box->gadget->type == GADGET_PASSWORD ||
- box->gadget->type == GADGET_FILE) {
- if (width == AUTO) {
- size = INTTOFIX(10);
- width = FIXTOINT(nscss_len2px(size, unit,
- box->style));
- }
- if (box->gadget->type == GADGET_FILE &&
- height == AUTO) {
- size = FLTTOFIX(1.5);
- height = FIXTOINT(nscss_len2px(size, unit,
- box->style));
- }
- }
- if (box->gadget->type == GADGET_TEXTAREA) {
- if (width == AUTO) {
- size = INTTOFIX(10);
- width = FIXTOINT(nscss_len2px(size, unit,
- box->style));
- }
- if (height == AUTO) {
- 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 includes margin, borders and padding */
- if (width == available_width) {
- width -= box->margin[LEFT] + box->border[LEFT].width +
- box->padding[LEFT] +
- box->padding[RIGHT] +
- box->border[RIGHT].width +
- box->margin[RIGHT];
- } else {
- /* width was obtained from a min_width or max_width
- * value, so need to use the same method for calculating
- * mbp as was used in layout_minmax_block() */
- int fixed = 0;
- float frac = 0;
- calculate_mbp_width(box->style, LEFT, true, true, true,
- &fixed, &frac);
- calculate_mbp_width(box->style, RIGHT, true, true, true,
- &fixed, &frac);
- if (fixed < 0)
- fixed = 0;
-
- width -= fixed;
- }
+ int fixed = 0;
+ float frac = 0;
- if (max_width >= 0 && width > max_width) width = max_width;
- if (min_width > 0 && width < min_width) width = min_width;
+ assert(box && box->gadget);
- } else {
- if (max_width >= 0 && width > max_width) width = max_width;
- if (min_width > 0 && width < min_width) width = min_width;
- width -= scrollbar_width_y;
+ /* specified gadget widths include borders and padding in some
+ * cases */
+ if (percentage || box->gadget->type == GADGET_SUBMIT ||
+ box->gadget->type == GADGET_RESET ||
+ box->gadget->type == GADGET_BUTTON) {
+ calculate_mbp_width(box->style, setwidth ? LEFT : TOP,
+ false, true, true, &fixed, &frac);
+ calculate_mbp_width(box->style, setwidth ? RIGHT : BOTTOM,
+ false, true, true, &fixed, &frac);
+ *dimension -= frac * available_width + fixed;
+ *dimension = *dimension > 0 ? *dimension : 0;
}
-
- box->width = width;
- box->height = height;
-
- if (margin[TOP] == AUTO)
- margin[TOP] = 0;
- if (margin[BOTTOM] == AUTO)
- margin[BOTTOM] = 0;
}
@@ -1616,12 +904,20 @@ void layout_float_find_dimensions(int available_width,
* \param padding filled with paddings, may be NULL
* \param border filled with border widths, may be NULL
*/
-
-void layout_find_dimensions(int available_width, int viewport_height,
- struct box *box, const css_computed_style *style,
- int *width, int *height, int *max_width, int *min_width,
- int *max_height, int *min_height, int margin[4], int padding[4],
- struct box_border border[4])
+static void
+layout_find_dimensions(int available_width,
+ int viewport_height,
+ struct box *box,
+ const css_computed_style *style,
+ int *width,
+ int *height,
+ int *max_width,
+ int *min_width,
+ int *max_height,
+ int *min_height,
+ int margin[4],
+ int padding[4],
+ struct box_border border[4])
{
struct box *containing_block = NULL;
unsigned int i;
@@ -1978,40 +1274,120 @@ void layout_find_dimensions(int available_width, int viewport_height,
/**
- * Under some circumstances, specified dimensions for form elements include
- * borders and padding.
+ * Find next block that current margin collapses to.
*
- * \param box gadget to adjust dimensions of
- * \param percentage whether the gadget has its dimension specified as a
- * percentage
- * \param available_width width of containing block
- * \param setwidth set true if the dimension to be tweaked is a width,
- * else set false for a height
- * \param dimension current value for given width/height dimension.
- * updated to new value after consideration of
- * gadget properties.
+ * \param box box to start tree-order search from (top margin is included)
+ * \param block box responsible for current block fromatting context
+ * \param viewport_height height of viewport in px
+ * \param max_pos_margin updated to to maximum positive margin encountered
+ * \param max_neg_margin updated to to maximum negative margin encountered
+ * \return next box that current margin collapses to, or NULL if none.
*/
-
-void layout_tweak_form_dimensions(struct box *box, bool percentage,
- int available_width, bool setwidth, int *dimension)
+static struct box*
+layout_next_margin_block(struct box *box,
+ struct box *block,
+ int viewport_height,
+ int *max_pos_margin,
+ int *max_neg_margin)
{
- int fixed = 0;
- float frac = 0;
+ assert(block != NULL);
- assert(box && box->gadget);
+ while (box != NULL) {
- /* specified gadget widths include borders and padding in some
- * cases */
- if (percentage || box->gadget->type == GADGET_SUBMIT ||
- box->gadget->type == GADGET_RESET ||
- box->gadget->type == GADGET_BUTTON) {
- calculate_mbp_width(box->style, setwidth ? LEFT : TOP,
- false, true, true, &fixed, &frac);
- calculate_mbp_width(box->style, setwidth ? RIGHT : BOTTOM,
- false, true, true, &fixed, &frac);
- *dimension -= frac * available_width + fixed;
- *dimension = *dimension > 0 ? *dimension : 0;
+ if (box->type == BOX_INLINE_CONTAINER || (box->style &&
+ (css_computed_position(box->style) !=
+ CSS_POSITION_ABSOLUTE &&
+ css_computed_position(box->style) !=
+ CSS_POSITION_FIXED))) {
+ /* Not positioned */
+
+ /* Get margins */
+ if (box->style) {
+ layout_find_dimensions(box->parent->width,
+ viewport_height, box,
+ box->style,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, box->margin,
+ box->padding, box->border);
+
+ /* Apply top margin */
+ if (*max_pos_margin < box->margin[TOP])
+ *max_pos_margin = box->margin[TOP];
+ else if (*max_neg_margin < -box->margin[TOP])
+ *max_neg_margin = -box->margin[TOP];
+ }
+
+ /* Check whether box is the box current margin collapses
+ * to */
+ if (box->flags & MAKE_HEIGHT ||
+ box->border[TOP].width ||
+ box->padding[TOP] ||
+ (box->style &&
+ css_computed_overflow_y(box->style) !=
+ CSS_OVERFLOW_VISIBLE) ||
+ (box->type == BOX_INLINE_CONTAINER &&
+ box != box->parent->children)) {
+ /* Collapse to this box; return it */
+ return box;
+ }
+ }
+
+
+ /* Find next box */
+ if (box->type == BOX_BLOCK && !box->object && box->children &&
+ box->style &&
+ css_computed_overflow_y(box->style) ==
+ CSS_OVERFLOW_VISIBLE) {
+ /* Down into children. */
+ box = box->children;
+ } else {
+ if (!box->next) {
+ /* No more siblings:
+ * Go up to first ancestor with a sibling. */
+ do {
+ /* Apply bottom margin */
+ if (*max_pos_margin <
+ box->margin[BOTTOM])
+ *max_pos_margin =
+ box->margin[BOTTOM];
+ else if (*max_neg_margin <
+ -box->margin[BOTTOM])
+ *max_neg_margin =
+ -box->margin[BOTTOM];
+
+ box = box->parent;
+ } while (box != block && !box->next);
+
+ if (box == block) {
+ /* Margins don't collapse with stuff
+ * outside the block formatting context
+ */
+ return block;
+ }
+ }
+
+ /* Apply bottom margin */
+ if (*max_pos_margin < box->margin[BOTTOM])
+ *max_pos_margin = box->margin[BOTTOM];
+ else if (*max_neg_margin < -box->margin[BOTTOM])
+ *max_neg_margin = -box->margin[BOTTOM];
+
+ /* To next sibling. */
+ box = box->next;
+
+ /* Get margins */
+ if (box->style) {
+ layout_find_dimensions(box->parent->width,
+ viewport_height, box,
+ box->style,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, box->margin,
+ box->padding, box->border);
+ }
+ }
}
+
+ return NULL;
}
@@ -2022,8 +1398,7 @@ void layout_tweak_form_dimensions(struct box *box, bool percentage,
* \param clear type of clear
* \return y coordinate relative to ancestor box for floats
*/
-
-int layout_clear(struct box *fl, enum css_clear_e clear)
+static int layout_clear(struct box *fl, enum css_clear_e clear)
{
int y = 0;
for (; fl; fl = fl->next_float) {
@@ -2051,9 +1426,12 @@ int layout_clear(struct box *fl, enum css_clear_e clear)
* \param left returns float on left if present
* \param right returns float on right if present
*/
-
-void find_sides(struct box *fl, int y0, int y1,
- int *x0, int *x1, struct box **left, struct box **right)
+static void
+find_sides(struct box *fl,
+ int y0, int y1,
+ int *x0, int *x1,
+ struct box **left,
+ struct box **right)
{
int fy0, fy1, fx0, fx1;
@@ -2094,1393 +1472,252 @@ void find_sides(struct box *fl, int y0, int y1,
}
-/**
- * Insert a float into a container.
- *
- * \param cont block formatting context block, used to contain float
- * \param b box to add to float
- *
- * This sorts floats in order of descending bottom edges.
- */
-static void add_float_to_container(struct box *cont, struct box *b)
-{
- struct box *box = cont->float_children;
- int b_bottom = b->y + b->height;
-
- assert(b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT);
-
- if (box == NULL) {
- /* No other float children */
- b->next_float = NULL;
- cont->float_children = b;
- return;
- } else if (b_bottom >= box->y + box->height) {
- /* Goes at start of list */
- b->next_float = cont->float_children;
- cont->float_children = b;
- } else {
- struct box *prev = NULL;
- while (box != NULL && b_bottom < box->y + box->height) {
- prev = box;
- box = box->next_float;
- }
- if (prev != NULL) {
- b->next_float = prev->next_float;
- prev->next_float = b;
- }
- }
-}
/**
- * Layout lines of text or inline boxes with floats.
+ * Solve the width constraint as given in CSS 2.1 section 10.3.3.
*
- * \param inline_container inline container box
- * \param width horizontal space available
- * \param cont ancestor box which defines horizontal space, for floats
- * \param cx box position relative to cont
- * \param cy box position relative to cont
- * \param content memory pool for any new boxes
- * \return true on success, false on memory exhaustion
- */
-
-bool layout_inline_container(struct box *inline_container, int width,
- struct box *cont, int cx, int cy, html_content *content)
-{
- bool first_line = true;
- bool has_text_children;
- struct box *c, *next;
- int y = 0;
- int curwidth,maxwidth = width;
-
- assert(inline_container->type == BOX_INLINE_CONTAINER);
-
-#ifdef LAYOUT_DEBUG
- LOG("inline_container %p, width %i, cont %p, cx %i, cy %i", inline_container, width, cont, cx, cy);
-#endif
-
- has_text_children = false;
- for (c = inline_container->children; c; c = c->next) {
- bool is_pre = false;
-
- if (c->style) {
- enum css_white_space_e 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->flags & REPLACE_DIM) &&
- !(c->flags & IFRAME) &&
- c->text && (c->length || is_pre)) ||
- c->type == BOX_BR)
- has_text_children = true;
- }
-
- /** \todo fix wrapping so that a box with horizontal scrollbar will
- * shrink back to 'width' if no word is wider than 'width' (Or just set
- * curwidth = width and have the multiword lines wrap to the min width)
- */
- for (c = inline_container->children; c; ) {
-#ifdef LAYOUT_DEBUG
- LOG("c %p", c);
-#endif
- curwidth = inline_container->width;
- if (!layout_line(c, &curwidth, &y, cx, cy + y, cont, first_line,
- has_text_children, content, &next))
- return false;
- maxwidth = max(maxwidth,curwidth);
- c = next;
- first_line = false;
- }
-
- inline_container->width = maxwidth;
- inline_container->height = y;
-
- return true;
-}
-
-
-/**
- * Calculate minimum and maximum width of an inline container.
+ * \param box Box to solve constraint for
+ * \param available_width Max width available in pixels
+ * \param width Current box width
+ * \param lm Min left margin required to avoid floats in px.
+ * zero if not applicable
+ * \param rm Min right margin required to avoid floats in px.
+ * zero if not applicable
+ * \param max_width Box max-width ( -ve means no max-width to apply)
+ * \param min_width Box min-width ( <=0 means no min-width to apply)
+ * \return New box width
*
- * \param inline_container box of type INLINE_CONTAINER
- * \param[out] has_height set to true if container has height
- * \param font_func Font functions.
- * \post inline_container->min_width and inline_container->max_width filled in,
- * 0 <= inline_container->min_width <= inline_container->max_width
+ * \post \a box's left/right margins will be updated.
*/
-
-void layout_minmax_inline_container(struct box *inline_container,
- bool *has_height, const struct font_functions *font_func)
+static int
+layout_solve_width(struct box *box,
+ int available_width,
+ int width,
+ int lm,
+ int rm,
+ int max_width,
+ int min_width)
{
- struct box *child;
- int line_min = 0, line_max = 0;
- int min = 0, max = 0;
- bool first_line = true;
- bool line_has_height;
-
- assert(inline_container->type == BOX_INLINE_CONTAINER);
-
- /* check if the widths have already been calculated */
- if (inline_container->max_width != UNKNOWN_MAX_WIDTH)
- return;
-
- *has_height = false;
-
- for (child = inline_container->children; child; ) {
- child = layout_minmax_line(child, &line_min, &line_max,
- first_line, &line_has_height, font_func);
- if (min < line_min)
- min = line_min;
- if (max < line_max)
- max = line_max;
- first_line = false;
- *has_height |= line_has_height;
- }
-
- inline_container->min_width = min;
- inline_container->max_width = max;
-
- assert(0 <= inline_container->min_width &&
- inline_container->min_width <=
- inline_container->max_width);
-}
-
-
-/**
- * Calculate line height from a style.
- */
+ bool auto_width = false;
-int line_height(const css_computed_style *style)
-{
- enum css_line_height_e lhtype;
- css_fixed lhvalue = 0;
- css_unit lhunit = CSS_UNIT_PX;
- css_fixed line_height;
+ /* Increase specified left/right margins */
+ if (box->margin[LEFT] != AUTO && box->margin[LEFT] < lm &&
+ box->margin[LEFT] >= 0)
+ box->margin[LEFT] = lm;
+ if (box->margin[RIGHT] != AUTO && box->margin[RIGHT] < rm &&
+ box->margin[RIGHT] >= 0)
+ box->margin[RIGHT] = rm;
- assert(style);
+ /* Find width */
+ if (width == AUTO) {
+ /* any other 'auto' become 0 or the minimum required values */
+ if (box->margin[LEFT] == AUTO)
+ box->margin[LEFT] = lm;
+ if (box->margin[RIGHT] == AUTO)
+ box->margin[RIGHT] = rm;
- 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;
+ width = available_width -
+ (box->margin[LEFT] + box->border[LEFT].width +
+ box->padding[LEFT] + box->padding[RIGHT] +
+ box->border[RIGHT].width + box->margin[RIGHT]);
+ width = width < 0 ? 0 : width;
+ auto_width = true;
}
- if (lhtype == CSS_LINE_HEIGHT_NUMBER ||
- lhunit == CSS_UNIT_PCT) {
- line_height = nscss_len2px(lhvalue, CSS_UNIT_EM, style);
-
- if (lhtype != CSS_LINE_HEIGHT_NUMBER)
- line_height = FDIV(line_height, F_100);
- } else {
- assert(lhunit != CSS_UNIT_PCT);
-
- line_height = nscss_len2px(lhvalue, lhunit, style);
+ if (max_width >= 0 && width > max_width) {
+ /* max-width is admissable and width exceeds max-width */
+ width = max_width;
+ auto_width = false;
}
- return FIXTOINT(line_height);
-}
-
-
-/**
- * Split a text box.
- *
- * \param content memory pool for any new boxes
- * \param fstyle style for text in text box
- * \param split_box box with text to split
- * \param new_length new length for text in split_box, after splitting
- * \param new_width new width for text in split_box, after splitting
- * \return true on success, false on memory exhaustion
- *
- * A new box is created and inserted into the box tree after split_box,
- * containing the text after new_length excluding the initial space character.
- */
-
-static bool layout_text_box_split(html_content *content,
- plot_font_style_t *fstyle, struct box *split_box,
- size_t new_length, int new_width)
-{
- int space_width = split_box->space;
- struct box *c2;
- const struct font_functions *font_func = content->font_func;
- bool space = (split_box->text[new_length] == ' ');
- int used_length = new_length + (space ? 1 : 0);
-
- if ((space && space_width == 0) || space_width == UNKNOWN_WIDTH) {
- /* We're need to add a space, and we don't know how big
- * it's to be, OR we have a space of unknown width anyway;
- * Calculate space width */
- font_func->font_width(fstyle, " ", 1, &space_width);
+ if (min_width > 0 && width < min_width) {
+ /* min-width is admissable and width is less than max-width */
+ width = min_width;
+ auto_width = false;
}
- if (split_box->space == UNKNOWN_WIDTH)
- split_box->space = space_width;
- if (!space)
- space_width = 0;
-
- /* Create clone of split_box, c2 */
- c2 = talloc_memdup(content->bctx, split_box, sizeof *c2);
- if (!c2)
- return false;
- c2->flags |= CLONE;
-
- /* Set remaining text in c2 */
- c2->text += used_length;
-
- /* Set c2 according to the remaining text */
- c2->width -= new_width + space_width;
- c2->flags &= ~MEASURED; /* width has been estimated */
- c2->length = split_box->length - used_length;
-
- /* Update split_box for its reduced text */
- split_box->width = new_width;
- split_box->flags |= MEASURED;
- split_box->length = new_length;
- split_box->space = space_width;
-
- /* Insert c2 into box list */
- c2->next = split_box->next;
- split_box->next = c2;
- c2->prev = split_box;
- if (c2->next)
- c2->next->prev = c2;
- else
- c2->parent->last = c2;
-#ifdef LAYOUT_DEBUG
- LOG("split_box %p len: %u \"%.*s\"", split_box, split_box->length, split_box->length, split_box->text);
- LOG(" new_box %p len: %u \"%.*s\"", c2, c2->length, c2->length, c2->text);
-#endif
- return true;
-}
-
-
-/**
- * Position a line of boxes in inline formatting context.
- *
- * \param first box at start of line
- * \param width available width on input, updated with actual width on output
- * (may be incorrect if the line gets split?)
- * \param y coordinate of top of line, updated on exit to bottom
- * \param cx coordinate of left of line relative to cont
- * \param cy coordinate of top of line relative to cont
- * \param cont ancestor box which defines horizontal space, for floats
- * \param indent apply any first-line indent
- * \param has_text_children at least one TEXT in the inline_container
- * \param next_box updated to first box for next line, or 0 at end
- * \param content memory pool for any new boxes
- * \return true on success, false on memory exhaustion
- */
-
-bool layout_line(struct box *first, int *width, int *y,
- int cx, int cy, struct box *cont, bool indent,
- bool has_text_children,
- html_content *content, struct box **next_box)
-{
- int height, used_height;
- int x0 = 0;
- int x1 = *width;
- int x, h, x_previous;
- int fy = cy;
- struct box *left;
- struct box *right;
- struct box *b;
- struct box *split_box = 0;
- struct box *d;
- struct box *br_box = 0;
- bool move_y = false;
- bool place_below = false;
- int space_before = 0, space_after = 0;
- unsigned int inline_count = 0;
- unsigned int i;
- const struct font_functions *font_func = content->font_func;
- plot_font_style_t fstyle;
-
-#ifdef LAYOUT_DEBUG
- LOG("first %p, first->text '%.*s', width %i, y %i, cx %i, cy %i", first, (int)first->length, first->text, *width, *y, cx, cy);
-#endif
-
- /* find sides at top of line */
- x0 += cx;
- x1 += cx;
- find_sides(cont->float_children, cy, cy, &x0, &x1, &left, &right);
- x0 -= cx;
- x1 -= cx;
-
- if (indent)
- x0 += layout_text_indent(first->parent->parent->style, *width);
-
- if (x1 < x0)
- x1 = x0;
-
- /* get minimum line height from containing block.
- * this is the line-height if there are text children and also in the
- * case of an initially empty text input */
- if (has_text_children || first->parent->parent->gadget)
- used_height = height =
- line_height(first->parent->parent->style);
- else
- /* inline containers with no text are usually for layout and
- * look better with no minimum line-height */
- used_height = height = 0;
-
- /* pass 1: find height of line assuming sides at top of line: loop
- * body executed at least once
- * keep in sync with the loop in layout_minmax_line() */
-#ifdef LAYOUT_DEBUG
- LOG("x0 %i, x1 %i, x1 - x0 %i", x0, x1, x1 - x0);
-#endif
-
- for (x = 0, b = first; x <= x1 - x0 && b != 0; b = b->next) {
- int min_width, max_width, min_height, max_height;
-
- assert(b->type == BOX_INLINE || b->type == BOX_INLINE_BLOCK ||
- b->type == BOX_FLOAT_LEFT ||
- b->type == BOX_FLOAT_RIGHT ||
- b->type == BOX_BR || b->type == BOX_TEXT ||
- b->type == BOX_INLINE_END);
+ /* Width was auto, and unconstrained by min/max width, so we're done */
+ if (auto_width)
+ return width;
-#ifdef LAYOUT_DEBUG
- LOG("pass 1: b %p, x %i", b, x);
-#endif
+ /* Width was not auto, or was constrained by min/max width
+ * Need to compute left/right margins */
- if (b->type == BOX_BR)
+ /* HTML alignment (only applies to over-constrained boxes) */
+ if (box->margin[LEFT] != AUTO && box->margin[RIGHT] != AUTO &&
+ box->parent != NULL && box->parent->style != NULL) {
+ switch (css_computed_text_align(box->parent->style)) {
+ case CSS_TEXT_ALIGN_LIBCSS_RIGHT:
+ box->margin[LEFT] = AUTO;
+ box->margin[RIGHT] = 0;
break;
-
- if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT)
- continue;
- if (b->type == BOX_INLINE_BLOCK &&
- (css_computed_position(b->style) ==
- CSS_POSITION_ABSOLUTE ||
- css_computed_position(b->style) ==
- CSS_POSITION_FIXED))
- continue;
-
- assert(b->style != NULL);
- font_plot_style_from_css(b->style, &fstyle);
-
- x += space_after;
-
- if (b->type == BOX_INLINE_BLOCK) {
- if (b->max_width != UNKNOWN_WIDTH)
- if (!layout_float(b, *width, content))
- return false;
- 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].width +
- b->padding[LEFT] + b->width +
- b->padding[RIGHT] +
- b->border[RIGHT].width +
- b->margin[RIGHT];
- space_after = 0;
- continue;
- }
-
- if (b->type == BOX_INLINE) {
- /* calculate borders, margins, and padding */
- layout_find_dimensions(*width, -1, b, b->style, 0, 0,
- 0, 0, 0, 0, b->margin, b->padding,
- b->border);
- for (i = 0; i != 4; i++)
- if (b->margin[i] == AUTO)
- b->margin[i] = 0;
- x += b->margin[LEFT] + b->border[LEFT].width +
- b->padding[LEFT];
- if (b->inline_end) {
- b->inline_end->margin[RIGHT] = b->margin[RIGHT];
- b->inline_end->padding[RIGHT] =
- b->padding[RIGHT];
- b->inline_end->border[RIGHT] =
- b->border[RIGHT];
- } else {
- x += b->padding[RIGHT] +
- b->border[RIGHT].width +
- b->margin[RIGHT];
- }
- } else if (b->type == BOX_INLINE_END) {
- b->width = 0;
- if (b->space == UNKNOWN_WIDTH) {
- font_func->font_width(&fstyle, " ", 1,
- &b->space);
- /** \todo handle errors */
- }
- space_after = b->space;
-
- x += b->padding[RIGHT] + b->border[RIGHT].width +
- b->margin[RIGHT];
- continue;
- }
-
- if (!b->object && !(b->flags & IFRAME) && !b->gadget &&
- !(b->flags & REPLACE_DIM)) {
- /* inline non-replaced, 10.3.1 and 10.6.1 */
- b->height = line_height(b->style ? b->style :
- b->parent->parent->style);
- if (height < b->height)
- height = b->height;
-
- if (!b->text) {
- b->width = 0;
- space_after = 0;
- continue;
- }
-
- if (b->width == UNKNOWN_WIDTH) {
- /** \todo handle errors */
-
- /* If it's a select element, we must use the
- * width of the widest option text */
- if (b->parent->parent->gadget &&
- b->parent->parent->gadget->type
- == GADGET_SELECT) {
- int opt_maxwidth = 0;
- struct form_option *o;
-
- for (o = b->parent->parent->gadget->
- data.select.items; o;
- o = o->next) {
- int opt_width;
- font_func->font_width(&fstyle,
- o->text,
- strlen(o->text),
- &opt_width);
-
- if (opt_maxwidth < opt_width)
- opt_maxwidth =opt_width;
- }
- b->width = opt_maxwidth;
- if (nsoption_bool(core_select_menu))
- b->width += SCROLLBAR_WIDTH;
- } else {
- font_func->font_width(&fstyle, b->text,
- b->length, &b->width);
- b->flags |= MEASURED;
- }
- }
-
- /* If the current text has not been measured (i.e. its
- * width was estimated after splitting), and it fits on
- * the line, measure it properly, so next box is placed
- * correctly. */
- if (b->text && (x + b->width < x1 - x0) &&
- !(b->flags & MEASURED) &&
- b->next) {
- font_func->font_width(&fstyle, b->text,
- b->length, &b->width);
- b->flags |= MEASURED;
- }
-
- x += b->width;
- if (b->space == UNKNOWN_WIDTH) {
- font_func->font_width(&fstyle, " ", 1,
- &b->space);
- /** \todo handle errors */
- }
- space_after = b->space;
- continue;
- }
-
- space_after = 0;
-
- /* inline replaced, 10.3.2 and 10.6.2 */
- assert(b->style);
-
- layout_find_dimensions(*width, -1, b, b->style,
- &b->width, &b->height, &max_width, &min_width,
- &max_height, &min_height, NULL, NULL, NULL);
-
- if (b->object && !(b->flags & REPLACE_DIM)) {
- layout_get_object_dimensions(b, &b->width, &b->height,
- min_width, max_width,
- min_height, max_height);
- } else if (b->flags & IFRAME) {
- /* TODO: should we look at the content dimensions? */
- if (b->width == AUTO)
- b->width = 400;
- if (b->height == AUTO)
- b->height = 300;
-
- /* We reformat the iframe browser window to new
- * dimensions in pass 2 */
- } else {
- /* form control with no object */
- if (b->width == AUTO)
- b->width = FIXTOINT(nscss_len2px(INTTOFIX(1),
- CSS_UNIT_EM, b->style));
- if (b->height == AUTO)
- b->height = FIXTOINT(nscss_len2px(INTTOFIX(1),
- CSS_UNIT_EM, b->style));
- }
-
- /* Reformat object to new box size */
- if (b->object && content_get_type(b->object) == CONTENT_HTML &&
- b->width !=
- content_get_available_width(b->object)) {
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
- enum css_height_e htype = css_computed_height(b->style,
- &value, &unit);
-
- content_reformat(b->object, false, b->width, b->height);
-
- if (htype == CSS_HEIGHT_AUTO)
- b->height = content_get_height(b->object);
- }
-
- if (height < b->height)
- height = b->height;
-
- x += b->width;
- }
-
- /* find new sides using this height */
- x0 = cx;
- x1 = cx + *width;
- find_sides(cont->float_children, cy, cy + height, &x0, &x1,
- &left, &right);
- x0 -= cx;
- x1 -= cx;
-
- if (indent)
- x0 += layout_text_indent(first->parent->parent->style, *width);
-
- if (x1 < x0)
- x1 = x0;
-
- space_after = space_before = 0;
-
- /* pass 2: place boxes in line: loop body executed at least once */
-#ifdef LAYOUT_DEBUG
- LOG("x0 %i, x1 %i, x1 - x0 %i", x0, x1, x1 - x0);
-#endif
-
- for (x = x_previous = 0, b = first; x <= x1 - x0 && b; b = b->next) {
-#ifdef LAYOUT_DEBUG
- LOG("pass 2: b %p, x %i", b, x);
-#endif
-
- if (b->type == BOX_INLINE_BLOCK &&
- (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 ||
- b->type == BOX_INLINE_BLOCK ||
- b->type == BOX_TEXT ||
- b->type == BOX_INLINE_END) {
- assert(b->width != UNKNOWN_WIDTH);
-
- x_previous = x;
- x += space_after;
- b->x = x;
-
- if ((b->type == BOX_INLINE && !b->inline_end) ||
- b->type == BOX_INLINE_BLOCK) {
- b->x += b->margin[LEFT] + b->border[LEFT].width;
- x = b->x + b->padding[LEFT] + b->width +
- b->padding[RIGHT] +
- b->border[RIGHT].width +
- b->margin[RIGHT];
- } else if (b->type == BOX_INLINE) {
- b->x += b->margin[LEFT] + b->border[LEFT].width;
- x = b->x + b->padding[LEFT] + b->width;
- } else if (b->type == BOX_INLINE_END) {
- b->height = b->inline_end->height;
- x += b->padding[RIGHT] +
- b->border[RIGHT].width +
- b->margin[RIGHT];
- } else {
- x += b->width;
- }
-
- space_before = space_after;
- if (b->object || b->flags & REPLACE_DIM ||
- b->flags & IFRAME)
- space_after = 0;
- else if (b->text || b->type == BOX_INLINE_END) {
- if (b->space == UNKNOWN_WIDTH) {
- font_plot_style_from_css(b->style,
- &fstyle);
- /** \todo handle errors */
- font_func->font_width(&fstyle, " ", 1,
- &b->space);
- }
- space_after = b->space;
- } else {
- space_after = 0;
- }
- split_box = b;
- move_y = true;
- inline_count++;
- } else if (b->type == BOX_BR) {
- b->x = x;
- b->width = 0;
- br_box = b;
- b = b->next;
- split_box = 0;
- move_y = true;
+ case CSS_TEXT_ALIGN_LIBCSS_CENTER:
+ box->margin[LEFT] = box->margin[RIGHT] = AUTO;
break;
-
- } else {
- /* float */
-#ifdef LAYOUT_DEBUG
- LOG("float %p", b);
-#endif
-
- d = b->children;
- d->float_children = 0;
- d->cached_place_below_level = 0;
- b->float_container = d->float_container = cont;
-
- if (!layout_float(d, *width, content))
- return false;
-
-#ifdef LAYOUT_DEBUG
- LOG("%p : %d %d", d, d->margin[TOP], d->border[TOP].width);
-#endif
-
- 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].width +
- d->margin[RIGHT];
- b->height = d->margin[TOP] + d->border[TOP].width +
- d->padding[TOP] + d->height +
- d->padding[BOTTOM] +
- d->border[BOTTOM].width +
- d->margin[BOTTOM];
-
- if (b->width > (x1 - x0) - x)
- place_below = true;
- 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 &&
- cy >= cont->cached_place_below_level) {
- /* + not cleared or,
- * cleared and there are no floats to clear
- * + fits without needing to be placed below or,
- * this line is empty with no floats
- * + current y, cy, is below the clear level
- *
- * Float affects current line */
- if (b->type == BOX_FLOAT_LEFT) {
- b->x = cx + x0;
- if (b->width > 0)
- x0 += b->width;
- left = b;
- } else {
- b->x = cx + x1 - b->width;
- if (b->width > 0)
- x1 -= b->width;
- right = b;
- }
- b->y = cy;
- } else {
- /* cleared or doesn't fit on line */
- /* place below into next available space */
- int fcy = (cy > cont->clear_level) ? cy :
- cont->clear_level;
- fcy = (fcy > cont->cached_place_below_level) ?
- fcy :
- cont->cached_place_below_level;
- fy = (fy > fcy) ? fy : fcy;
- fy = (fy == cy) ? fy + height : fy;
-
- place_float_below(b, *width, cx, fy, cont);
- fy = b->y;
- if (d->style && (
- (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)))) {
- /* to be cleared below existing
- * floats */
- if (b->type == BOX_FLOAT_LEFT)
- b->x = cx;
- else
- b->x = cx + *width - b->width;
-
- fcy = layout_clear(cont->float_children,
- css_computed_clear(d->style));
- if (fcy > cont->clear_level)
- cont->clear_level = fcy;
- if (b->y < fcy)
- b->y = fcy;
- }
- if (b->type == BOX_FLOAT_LEFT)
- left = b;
- else
- right = b;
- }
- add_float_to_container(cont, b);
-
- split_box = 0;
- }
- }
-
- if (x1 - x0 < x && split_box) {
- /* the last box went over the end */
- size_t split = 0;
- int w;
- bool no_wrap = css_computed_white_space(
- split_box->style) == CSS_WHITE_SPACE_NOWRAP ||
- css_computed_white_space(
- split_box->style) == CSS_WHITE_SPACE_PRE;
-
- x = x_previous;
-
- if (!no_wrap && (split_box->type == BOX_INLINE ||
- split_box->type == BOX_TEXT) &&
- !split_box->object &&
- !(split_box->flags & REPLACE_DIM) &&
- !(split_box->flags & IFRAME) &&
- !split_box->gadget && split_box->text) {
-
- font_plot_style_from_css(split_box->style, &fstyle);
- /** \todo handle errors */
- font_func->font_split(&fstyle,
- split_box->text, split_box->length,
- x1 - x0 - x - space_before, &split, &w);
- }
-
- /* split == 0 implies that text can't be split */
-
- if (split == 0)
- w = split_box->width;
-
-#ifdef LAYOUT_DEBUG
- LOG("splitting: split_box %p \"%.*s\", spilt %zu, w %i, ""left %p, right %p, inline_count %u", split_box, (int)split_box->length, split_box->text, split, w, left, right, inline_count);
-#endif
-
- if ((split == 0 || x1 - x0 <= x + space_before + w) &&
- !left && !right && inline_count == 1) {
- /* first word of box doesn't fit, but no floats and
- * first box on line so force in */
- if (split == 0 || split == split_box->length) {
- /* only one word in this box, or not text
- * or white-space:nowrap */
- b = split_box->next;
- } else {
- /* cut off first word for this line */
- if (!layout_text_box_split(content, &fstyle,
- split_box, split, w))
- return false;
- b = split_box->next;
- }
- x += space_before + w;
-#ifdef LAYOUT_DEBUG
- LOG("forcing");
-#endif
- } else if ((split == 0 || x1 - x0 <= x + space_before + w) &&
- inline_count == 1) {
- /* first word of first box doesn't fit, but a float is
- * taking some of the width so move below it */
- assert(left || right);
- used_height = 0;
- if (left) {
-#ifdef LAYOUT_DEBUG
- LOG("cy %i, left->y %i, left->height %i", cy, left->y, left->height);
-#endif
- used_height = left->y + left->height - cy + 1;
-#ifdef LAYOUT_DEBUG
- LOG("used_height %i", used_height);
-#endif
- }
- if (right && used_height <
- right->y + right->height - cy + 1)
- used_height = right->y + right->height - cy + 1;
-
- if (used_height < 0)
- used_height = 0;
-
- b = split_box;
-#ifdef LAYOUT_DEBUG
- LOG("moving below float");
-#endif
- } else if (split == 0 || x1 - x0 <= x + space_before + w) {
- /* first word of box doesn't fit so leave box for next
- * line */
- b = split_box;
-#ifdef LAYOUT_DEBUG
- LOG("leaving for next line");
-#endif
- } else {
- /* fit as many words as possible */
- assert(split != 0);
-#ifdef LAYOUT_DEBUG
- LOG("'%.*s' %i %zu %i", (int)split_box->length, split_box->text, x1 - x0, split, w);
-#endif
- if (split != split_box->length) {
- if (!layout_text_box_split(content, &fstyle,
- split_box, split, w))
- return false;
- b = split_box->next;
- }
- x += space_before + w;
-#ifdef LAYOUT_DEBUG
- LOG("fitting words");
-#endif
- }
- move_y = true;
- }
-
- /* set positions */
- switch (css_computed_text_align(first->parent->parent->style)) {
- case CSS_TEXT_ALIGN_RIGHT:
- case CSS_TEXT_ALIGN_LIBCSS_RIGHT:
- x0 = x1 - x;
- break;
- case CSS_TEXT_ALIGN_CENTER:
- case CSS_TEXT_ALIGN_LIBCSS_CENTER:
- x0 = (x0 + (x1 - x)) / 2;
- break;
- case CSS_TEXT_ALIGN_LEFT:
- case CSS_TEXT_ALIGN_LIBCSS_LEFT:
- case CSS_TEXT_ALIGN_JUSTIFY:
- /* leave on left */
- break;
- case CSS_TEXT_ALIGN_DEFAULT:
- /* None; consider text direction */
- switch (css_computed_direction(first->parent->parent->style)) {
- case CSS_DIRECTION_LTR:
- /* leave on left */
+ case CSS_TEXT_ALIGN_LIBCSS_LEFT:
+ box->margin[LEFT] = 0;
+ box->margin[RIGHT] = AUTO;
break;
- case CSS_DIRECTION_RTL:
- x0 = x1 - x;
+ default:
+ /* Leave it alone; no HTML alignment */
break;
}
- break;
}
- for (d = first; d != b; d = d->next) {
- d->flags &= ~NEW_LINE;
-
- if (d->type == BOX_INLINE_BLOCK &&
- (css_computed_position(d->style) ==
- CSS_POSITION_ABSOLUTE ||
- css_computed_position(d->style) ==
- CSS_POSITION_FIXED)) {
- /* positioned inline-blocks:
- * set static position (x,y) only, rest of positioning
- * is handled later */
- d->x += x0;
- d->y = *y;
- continue;
- } else if ((d->type == BOX_INLINE &&
- ((d->object || d->gadget) == false) &&
- !(d->flags & IFRAME) &&
- !(d->flags & REPLACE_DIM)) ||
- d->type == BOX_BR ||
- d->type == BOX_TEXT ||
- d->type == BOX_INLINE_END) {
- /* regular (non-replaced) inlines */
- d->x += x0;
- d->y = *y - d->padding[TOP];
+ if (box->margin[LEFT] == AUTO && box->margin[RIGHT] == AUTO) {
+ /* make the margins equal, centering the element */
+ box->margin[LEFT] = box->margin[RIGHT] =
+ (available_width - lm - rm -
+ (box->border[LEFT].width + box->padding[LEFT] +
+ width + box->padding[RIGHT] +
+ box->border[RIGHT].width)) / 2;
- if (d->type == BOX_TEXT && d->height > used_height) {
- /* text */
- used_height = d->height;
- }
- } else if ((d->type == BOX_INLINE) ||
- d->type == BOX_INLINE_BLOCK) {
- /* replaced inlines and inline-blocks */
- d->x += x0;
- d->y = *y + d->border[TOP].width + d->margin[TOP];
- 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;
+ if (box->margin[LEFT] < 0) {
+ box->margin[RIGHT] += box->margin[LEFT];
+ box->margin[LEFT] = 0;
}
- }
-
- first->flags |= NEW_LINE;
- assert(b != first || (move_y && 0 < used_height && (left || right)));
-
- /* handle vertical-align by adjusting box y values */
- /** \todo proper vertical alignment handling */
- for (d = first; d != b; d = d->next) {
- if ((d->type == BOX_INLINE && d->inline_end) ||
- d->type == BOX_BR ||
- d->type == BOX_TEXT ||
- d->type == BOX_INLINE_END) {
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
- switch (css_computed_vertical_align(d->style, &value,
- &unit)) {
- case CSS_VERTICAL_ALIGN_SUPER:
- case CSS_VERTICAL_ALIGN_TOP:
- case CSS_VERTICAL_ALIGN_TEXT_TOP:
- /* already at top */
- break;
- case CSS_VERTICAL_ALIGN_SUB:
- case CSS_VERTICAL_ALIGN_BOTTOM:
- case CSS_VERTICAL_ALIGN_TEXT_BOTTOM:
- d->y += used_height - d->height;
- break;
- default:
- case CSS_VERTICAL_ALIGN_BASELINE:
- d->y += 0.75 * (used_height - d->height);
- break;
- }
- }
- }
+ box->margin[LEFT] += lm;
- /* handle clearance for br */
- if (br_box && css_computed_clear(br_box->style) != CSS_CLEAR_NONE) {
- int clear_y = layout_clear(cont->float_children,
- css_computed_clear(br_box->style));
- if (used_height < clear_y - cy)
- used_height = clear_y - cy;
+ } else if (box->margin[LEFT] == AUTO) {
+ box->margin[LEFT] = available_width - lm -
+ (box->border[LEFT].width + box->padding[LEFT] +
+ width + box->padding[RIGHT] +
+ box->border[RIGHT].width + box->margin[RIGHT]);
+ box->margin[LEFT] = box->margin[LEFT] < lm
+ ? lm : box->margin[LEFT];
+ } else {
+ /* margin-right auto or "over-constrained" */
+ box->margin[RIGHT] = available_width - rm -
+ (box->margin[LEFT] + box->border[LEFT].width +
+ box->padding[LEFT] + width +
+ box->padding[RIGHT] +
+ box->border[RIGHT].width);
}
- if (move_y)
- *y += used_height;
- *next_box = b;
- *width = x; /* return actual width */
- return true;
+ return width;
}
/**
- * Calculate minimum and maximum width of a line.
+ * Compute dimensions of box, margins, paddings, and borders for a block-level
+ * element.
*
- * \param first a box in an inline container
- * \param line_min updated to minimum width of line starting at first
- * \param line_max updated to maximum width of line starting at first
- * \param first_line true iff this is the first line in the inline container
- * \param line_has_height updated to true or false, depending on line
- * \param font_func Font functions.
- * \return first box in next line, or 0 if no more lines
- * \post 0 <= *line_min <= *line_max
+ * \param available_width Max width available in pixels
+ * \param viewport_height Height of viewport in pixels or -ve if unknown
+ * \param lm min left margin required to avoid floats in px.
+ * zero if not applicable
+ * \param rm min right margin required to avoid floats in px.
+ * zero if not applicable
+ * \param box box to find dimensions of. updated with new width,
+ * height, margins, borders and paddings
+ *
+ * See CSS 2.1 10.3.3, 10.3.4, 10.6.2, and 10.6.3.
*/
-
-struct box *layout_minmax_line(struct box *first,
- int *line_min, int *line_max, bool first_line,
- bool *line_has_height, const struct font_functions *font_func)
+static void
+layout_block_find_dimensions(int available_width,
+ int viewport_height,
+ int lm,
+ int rm,
+ struct box *box)
{
- int min = 0, max = 0, width, height, fixed;
- float frac;
- size_t i, j;
- struct box *b;
- struct box *block;
- plot_font_style_t fstyle;
- bool no_wrap;
-
- assert(first->parent);
- assert(first->parent->parent);
- assert(first->parent->parent->style);
-
- block = first->parent->parent;
- no_wrap = (css_computed_white_space(block->style) ==
- CSS_WHITE_SPACE_NOWRAP ||
- css_computed_white_space(block->style) ==
- CSS_WHITE_SPACE_PRE);
-
- *line_has_height = false;
-
- /* corresponds to the pass 1 loop in layout_line() */
- for (b = first; b; b = b->next) {
- enum css_width_e wtype;
- enum css_height_e 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 ||
- b->type == BOX_BR || b->type == BOX_TEXT ||
- b->type == BOX_INLINE_END);
-
-#ifdef LAYOUT_DEBUG
- LOG("%p: min %i, max %i", b, min, max);
-#endif
-
- if (b->type == BOX_BR) {
- b = b->next;
- break;
- }
-
- if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT) {
- assert(b->children);
- if (b->children->type == BOX_BLOCK)
- layout_minmax_block(b->children, font_func);
- else
- layout_minmax_table(b->children, font_func);
- b->min_width = b->children->min_width;
- b->max_width = b->children->max_width;
- if (min < b->min_width)
- min = b->min_width;
- max += b->max_width;
- continue;
- }
-
- if (b->type == BOX_INLINE_BLOCK) {
- layout_minmax_block(b, font_func);
- if (min < b->min_width)
- min = b->min_width;
- max += b->max_width;
-
- if (b->flags & HAS_HEIGHT)
- *line_has_height = true;
- continue;
- }
-
- assert(b->style);
- font_plot_style_from_css(b->style, &fstyle);
-
- if (b->type == BOX_INLINE && !b->object &&
- !(b->flags & REPLACE_DIM) &&
- !(b->flags & IFRAME)) {
- fixed = frac = 0;
- calculate_mbp_width(b->style, LEFT, true, true, true,
- &fixed, &frac);
- if (!b->inline_end)
- calculate_mbp_width(b->style, RIGHT,
- true, true, true,
- &fixed, &frac);
- if (0 < fixed)
- max += fixed;
- *line_has_height = true;
- /* \todo update min width, consider fractional extra */
- } else if (b->type == BOX_INLINE_END) {
- fixed = frac = 0;
- calculate_mbp_width(b->inline_end->style, RIGHT,
- true, true, true,
- &fixed, &frac);
- if (0 < fixed)
- max += fixed;
-
- if (b->next) {
- if (b->space == UNKNOWN_WIDTH) {
- font_func->font_width(&fstyle, " ", 1,
- &b->space);
- }
- max += b->space;
- }
-
- *line_has_height = true;
- continue;
- }
-
- if (!b->object && !(b->flags & IFRAME) && !b->gadget &&
- !(b->flags & REPLACE_DIM)) {
- /* inline non-replaced, 10.3.1 and 10.6.1 */
- bool no_wrap_box;
- if (!b->text)
- continue;
-
- no_wrap_box = (css_computed_white_space(b->style) ==
- CSS_WHITE_SPACE_NOWRAP ||
- css_computed_white_space(b->style) ==
- CSS_WHITE_SPACE_PRE);
-
- if (b->width == UNKNOWN_WIDTH) {
- /** \todo handle errors */
-
- /* If it's a select element, we must use the
- * width of the widest option text */
- if (b->parent->parent->gadget &&
- b->parent->parent->gadget->type
- == GADGET_SELECT) {
- int opt_maxwidth = 0;
- struct form_option *o;
-
- for (o = b->parent->parent->gadget->
- data.select.items; o;
- o = o->next) {
- int opt_width;
- font_func->font_width(&fstyle,
- o->text,
- strlen(o->text),
- &opt_width);
-
- if (opt_maxwidth < opt_width)
- opt_maxwidth =opt_width;
- }
-
- b->width = opt_maxwidth;
- if (nsoption_bool(core_select_menu))
- b->width += SCROLLBAR_WIDTH;
-
- } else {
- font_func->font_width(&fstyle, b->text,
- b->length, &b->width);
- b->flags |= MEASURED;
- }
- }
- max += b->width;
- if (b->next) {
- if (b->space == UNKNOWN_WIDTH) {
- font_func->font_width(&fstyle, " ", 1,
- &b->space);
- }
- max += b->space;
- }
-
- if (no_wrap) {
- /* Don't wrap due to block style,
- * so min is the same as max */
- min = max;
-
- } else if (no_wrap_box) {
- /* This inline box can't be wrapped,
- * for min, consider box's width */
- if (min < b->width)
- min = b->width;
-
- } else if (b->parent->flags & NEED_MIN) {
- /* If we care what the minimum width is,
- * calculate it. (It's only needed if we're
- * shrinking-to-fit.) */
- /* min = widest single word */
- i = 0;
- do {
- for (j = i; j != b->length &&
- b->text[j] != ' '; j++)
- ;
- font_func->font_width(&fstyle,
- b->text + i,
- j - i, &width);
- if (min < width)
- min = width;
- i = j + 1;
- } while (j != b->length);
- }
-
- *line_has_height = true;
-
- continue;
- }
-
- /* inline replaced, 10.3.2 and 10.6.2 */
- assert(b->style);
-
- /* calculate box width */
- wtype = css_computed_width(b->style, &value, &unit);
- if (wtype == CSS_WIDTH_SET) {
- if (unit == CSS_UNIT_PCT) {
- /*
- b->width = FPCT_OF_INT_TOINT(value, width);
- */
-
- width = AUTO;
- } else {
- width = FIXTOINT(nscss_len2px(value, unit,
- b->style));
- if (width < 0)
- width = 0;
- }
- } else {
- width = AUTO;
- }
-
- /* height */
- htype = css_computed_height(b->style, &value, &unit);
- if (htype == CSS_HEIGHT_SET) {
- height = FIXTOINT(nscss_len2px(value, unit, b->style));
- } else {
- height = AUTO;
- }
-
- if (b->object || (b->flags & REPLACE_DIM)) {
- if (b->object) {
- int temp_height = height;
- layout_get_object_dimensions(b,
- &width, &temp_height,
- INT_MIN, INT_MAX,
- INT_MIN, INT_MAX);
- }
-
- fixed = frac = 0;
- calculate_mbp_width(b->style, LEFT, true, true, true,
- &fixed, &frac);
- calculate_mbp_width(b->style, RIGHT, true, true, true,
- &fixed, &frac);
-
- if (0 < width + fixed)
- width += fixed;
- } else if (b->flags & IFRAME) {
- /* TODO: handle percentage widths properly */
- if (width == AUTO)
- width = 400;
-
- fixed = frac = 0;
- calculate_mbp_width(b->style, LEFT, true, true, true,
- &fixed, &frac);
- calculate_mbp_width(b->style, RIGHT, true, true, true,
- &fixed, &frac);
-
- if (0 < width + fixed)
- width += fixed;
- } else {
- /* form control with no object */
- if (width == AUTO)
- width = FIXTOINT(nscss_len2px(INTTOFIX(1),
- CSS_UNIT_EM, b->style));
- }
-
- if (min < width)
- min = width;
- max += width;
+ int width, max_width, min_width;
+ int height, max_height, min_height;
+ int *margin = box->margin;
+ int *padding = box->padding;
+ struct box_border *border = box->border;
+ const css_computed_style *style = box->style;
- *line_has_height = true;
- }
+ layout_find_dimensions(available_width, viewport_height, box, style,
+ &width, &height, &max_width, &min_width,
+ &max_height, &min_height, margin, padding, border);
- if (first_line) {
- /* todo: handle percentage values properly */
- /* todo: handle text-indent interaction with floats */
- int text_indent = layout_text_indent(
- first->parent->parent->style, 100);
- min = (min + text_indent < 0) ? 0 : min + text_indent;
- max = (max + text_indent < 0) ? 0 : max + text_indent;
+ if (box->object && !(box->flags & REPLACE_DIM) &&
+ content_get_type(box->object) != CONTENT_HTML) {
+ /* block-level replaced element, see 10.3.4 and 10.6.2 */
+ layout_get_object_dimensions(box, &width, &height,
+ min_width, max_width, min_height, max_height);
}
- *line_min = min;
- *line_max = max;
-
-#ifdef LAYOUT_DEBUG
- LOG("line_min %i, line_max %i", min, max);
-#endif
+ box->width = layout_solve_width(box, available_width, width, lm, rm,
+ max_width, min_width);
+ box->height = height;
- assert(b != first);
- assert(0 <= *line_min);
- assert(*line_min <= *line_max);
- return b;
+ if (margin[TOP] == AUTO)
+ margin[TOP] = 0;
+ if (margin[BOTTOM] == AUTO)
+ margin[BOTTOM] = 0;
}
/**
- * Calculate the text-indent length.
+ * Manipulate a block's [RB]padding/height/width to accommodate scrollbars
*
- * \param style style of block
- * \param width width of containing block
- * \return length of indent
+ * \param box Box to apply scrollbar space too. Must be BOX_BLOCK.
+ * \param which Which scrollbar to make space for. Must be RIGHT or BOTTOM.
*/
-
-int layout_text_indent(const css_computed_style *style, int width)
+static void layout_block_add_scrollbar(struct box *box, int which)
{
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
+ enum css_overflow_e overflow_x, overflow_y;
- css_computed_text_indent(style, &value, &unit);
+ assert(box->type == BOX_BLOCK && (which == RIGHT || which == BOTTOM));
- if (unit == CSS_UNIT_PCT) {
- return FPCT_OF_INT_TOINT(value, width);
- } else {
- return FIXTOINT(nscss_len2px(value, unit, style));
- }
-}
+ if (box->style == NULL)
+ return;
+ overflow_x = css_computed_overflow_x(box->style);
+ overflow_y = css_computed_overflow_y(box->style);
-/**
- * Layout the contents of a float or inline block.
- *
- * \param b float or inline block box
- * \param width available width
- * \param content memory pool for any new boxes
- * \return true on success, false on memory exhaustion
- */
+ if (which == BOTTOM &&
+ (overflow_x == CSS_OVERFLOW_SCROLL ||
+ overflow_x == CSS_OVERFLOW_AUTO ||
+ (box->object &&
+ content_get_type(box->object) == CONTENT_HTML))) {
+ /* make space for scrollbar, unless height is AUTO */
+ if (box->height != AUTO &&
+ (overflow_x == CSS_OVERFLOW_SCROLL ||
+ box_hscrollbar_present(box))) {
+ box->padding[BOTTOM] += SCROLLBAR_WIDTH;
+ }
-bool layout_float(struct box *b, int width, html_content *content)
-{
- assert(b->type == BOX_TABLE || b->type == BOX_BLOCK ||
- b->type == BOX_INLINE_BLOCK);
- layout_float_find_dimensions(width, b->style, b);
- if (b->type == BOX_TABLE) {
- if (!layout_table(b, width, content))
- return false;
- if (b->margin[LEFT] == AUTO)
- b->margin[LEFT] = 0;
- if (b->margin[RIGHT] == AUTO)
- b->margin[RIGHT] = 0;
- if (b->margin[TOP] == AUTO)
- b->margin[TOP] = 0;
- if (b->margin[BOTTOM] == AUTO)
- b->margin[BOTTOM] = 0;
- } else
- return layout_block_context(b, -1, content);
- return true;
+ } else if (which == RIGHT &&
+ (overflow_y == CSS_OVERFLOW_SCROLL ||
+ overflow_y == CSS_OVERFLOW_AUTO ||
+ (box->object &&
+ content_get_type(box->object) == CONTENT_HTML))) {
+ /* make space for scrollbars, unless width is AUTO */
+ enum css_height_e htype;
+ css_fixed height = 0;
+ css_unit hunit = CSS_UNIT_PX;
+ htype = css_computed_height(box->style, &height, &hunit);
+
+ if (which == RIGHT && box->width != AUTO &&
+ htype == CSS_HEIGHT_SET &&
+ (overflow_y == CSS_OVERFLOW_SCROLL ||
+ box_vscrollbar_present(box))) {
+ box->width -= SCROLLBAR_WIDTH;
+ box->padding[RIGHT] += SCROLLBAR_WIDTH;
+ }
+ }
}
/**
- * Position a float in the first available space.
+ * Moves the children of a box by a specified amount
*
- * \param c float box to position
- * \param width available width
- * \param cx x coordinate relative to cont to place float right of
- * \param y y coordinate relative to cont to place float below
- * \param cont ancestor box which defines horizontal space, for floats
+ * \param box top of tree of boxes
+ * \param x the amount to move children by horizontally
+ * \param y the amount to move children by vertically
*/
-
-void place_float_below(struct box *c, int width, int cx, int y,
- struct box *cont)
+static void layout_move_children(struct box *box, int x, int y)
{
- int x0, x1, yy;
- struct box *left;
- struct box *right;
-
- yy = y > cont->cached_place_below_level ?
- y : cont->cached_place_below_level;
-
-#ifdef LAYOUT_DEBUG
- LOG("c %p, width %i, cx %i, y %i, cont %p", c, width, cx, y, cont);
-#endif
-
- do {
- y = yy;
- x0 = cx;
- x1 = cx + width;
- find_sides(cont->float_children, y, y + c->height, &x0, &x1,
- &left, &right);
- if (left != 0 && right != 0) {
- yy = (left->y + left->height <
- right->y + right->height ?
- left->y + left->height :
- right->y + right->height);
- } else if (left == 0 && right != 0) {
- yy = right->y + right->height;
- } else if (left != 0 && right == 0) {
- yy = left->y + left->height;
- }
- } while ((left != 0 || right != 0) && (c->width > x1 - x0));
+ assert(box);
- if (c->type == BOX_FLOAT_LEFT) {
- c->x = x0;
- } else {
- c->x = x1 - c->width;
+ for (box = box->children; box; box = box->next) {
+ box->x += x;
+ box->y += y;
}
- c->y = y;
- cont->cached_place_below_level = y;
}
@@ -3492,8 +1729,7 @@ void place_float_below(struct box *c, int width, int cx, int y,
* \param content memory pool for any new boxes
* \return true on success, false on memory exhaustion
*/
-
-bool layout_table(struct box *table, int available_width,
+static bool layout_table(struct box *table, int available_width,
html_content *content)
{
unsigned int columns = table->columns; /* total columns */
@@ -3688,7 +1924,7 @@ bool layout_table(struct box *table, int available_width,
"FIXED",
"AUTO",
"PERCENT",
- "RELATIVE"
+ "RELATIVE"
})[col[i].type], col[i].width, col[i].min, col[i].max);
#endif
@@ -4042,292 +2278,680 @@ bool layout_table(struct box *table, int available_width,
/**
- * Calculate minimum and maximum width of a table.
+ * Manimpulate box height according to CSS min-height and max-height properties
*
- * \param table box of type TABLE
- * \param font_func Font functions
- * \post table->min_width and table->max_width filled in,
- * 0 <= table->min_width <= table->max_width
+ * \param box block to modify with any min-height or max-height
+ * \param container containing block for absolutely positioned elements, or
+ * NULL for non absolutely positioned elements.
+ * \return whether the height has been changed
*/
-
-void layout_minmax_table(struct box *table,
- const struct font_functions *font_func)
+static bool layout_apply_minmax_height(struct box *box, struct box *container)
{
- unsigned int i, j;
- int border_spacing_h = 0;
- int table_min = 0, table_max = 0;
- int extra_fixed = 0;
- float extra_frac = 0;
- struct column *col = table->col;
- struct box *row_group, *row, *cell;
- enum css_width_e wtype;
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
+ int h;
+ struct box *containing_block = NULL;
+ bool updated = false;
- /* check if the widths have already been calculated */
- if (table->max_width != UNKNOWN_MAX_WIDTH)
- return;
+ /* Find containing block for percentage heights */
+ if (box->style != NULL && css_computed_position(box->style) ==
+ CSS_POSITION_ABSOLUTE) {
+ /* Box is absolutely positioned */
+ assert(container);
+ containing_block = container;
+ } else if (box->float_container && box->style != NULL &&
+ (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;
+ }
- /* start with 0 except for fixed-width columns */
- for (i = 0; i != table->columns; i++) {
- if (col[i].type == COLUMN_WIDTH_FIXED)
- col[i].min = col[i].max = col[i].width;
- else
- col[i].min = col[i].max = 0;
+ if (box->style) {
+ enum css_height_e htype = CSS_HEIGHT_AUTO;
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
+
+ if (containing_block) {
+ htype = css_computed_height(containing_block->style,
+ &value, &unit);
+ }
+
+ /* max-height */
+ if (css_computed_max_height(box->style, &value, &unit) ==
+ CSS_MAX_HEIGHT_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ if (containing_block &&
+ containing_block->height != AUTO &&
+ (css_computed_position(box->style) ==
+ CSS_POSITION_ABSOLUTE ||
+ htype == CSS_HEIGHT_SET)) {
+ /* Box is absolutely positioned or its
+ * containing block has a valid
+ * specified height. (CSS 2.1
+ * Section 10.5) */
+ h = FPCT_OF_INT_TOINT(value,
+ containing_block->height);
+ if (h < box->height) {
+ box->height = h;
+ updated = true;
+ }
+ }
+ } else {
+ h = FIXTOINT(nscss_len2px(value, unit,
+ box->style));
+ if (h < box->height) {
+ box->height = h;
+ updated = true;
+ }
+ }
+ }
+
+ /* min-height */
+ if (css_computed_min_height(box->style, &value, &unit) ==
+ CSS_MIN_HEIGHT_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ if (containing_block &&
+ containing_block->height != AUTO &&
+ (css_computed_position(box->style) ==
+ CSS_POSITION_ABSOLUTE ||
+ htype == CSS_HEIGHT_SET)) {
+ /* Box is absolutely positioned or its
+ * containing block has a valid
+ * specified height. (CSS 2.1
+ * Section 10.5) */
+ h = FPCT_OF_INT_TOINT(value,
+ containing_block->height);
+ if (h > box->height) {
+ box->height = h;
+ updated = true;
+ }
+ }
+ } else {
+ h = FIXTOINT(nscss_len2px(value, unit,
+ box->style));
+ if (h > box->height) {
+ box->height = h;
+ updated = true;
+ }
+ }
+ }
}
+ return updated;
+}
- /* border-spacing is used in the separated borders model */
- 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);
+/**
+ * Layout a block which contains an object.
+ *
+ * \param block box of type BLOCK, INLINE_BLOCK, TABLE, or TABLE_CELL
+ * \return true on success, false on memory exhaustion
+ */
+static bool layout_block_object(struct box *block)
+{
+ assert(block);
+ assert(block->type == BOX_BLOCK ||
+ block->type == BOX_INLINE_BLOCK ||
+ block->type == BOX_TABLE ||
+ block->type == BOX_TABLE_CELL);
+ assert(block->object);
- border_spacing_h = FIXTOINT(nscss_len2px(h, hu, table->style));
+#ifdef LAYOUT_DEBUG
+ LOG("block %p, object %s, width %i", block, hlcache_handle_get_url(block->object), block->width);
+#endif
+
+ if (content_get_type(block->object) == CONTENT_HTML) {
+ content_reformat(block->object, false, block->width, 1);
+ } else {
+ /* Non-HTML objects */
+ /* this case handled already in
+ * layout_block_find_dimensions() */
}
- /* 1st pass: consider cells with colspan 1 only */
- 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) {
- assert(cell->type == BOX_TABLE_CELL);
- assert(cell->style);
- /** TODO: Handle colspan="0" correctly.
- * It's currently converted to 1 in box normaisation */
- assert(cell->columns != 0);
+ return true;
+}
- if (cell->columns != 1)
- continue;
- layout_minmax_block(cell, font_func);
- i = cell->start_column;
+/**
+ * Layout a block formatting context.
+ *
+ * \param block BLOCK, INLINE_BLOCK, or TABLE_CELL to layout
+ * \param viewport_height Height of viewport in pixels or -ve if unknown
+ * \param content Memory pool for any new boxes
+ * \return true on success, false on memory exhaustion
+ *
+ * This function carries out layout of a block and its children, as described
+ * in CSS 2.1 9.4.1.
+ */
+static bool
+layout_block_context(struct box *block,
+ int viewport_height,
+ html_content *content)
+{
+ struct box *box;
+ int cx, cy; /**< current coordinates */
+ int max_pos_margin = 0;
+ int max_neg_margin = 0;
+ int y = 0;
+ int lm, rm;
+ struct box *margin_collapse = NULL;
+ bool in_margin = false;
+ css_fixed gadget_size;
+ css_unit gadget_unit; /* Checkbox / radio buttons */
- if (col[i].positioned)
- continue;
+ assert(block->type == BOX_BLOCK ||
+ block->type == BOX_INLINE_BLOCK ||
+ block->type == BOX_TABLE_CELL);
+ assert(block->width != UNKNOWN_WIDTH);
+ assert(block->width != AUTO);
- /* update column min, max widths using cell widths */
- if (col[i].min < cell->min_width)
- col[i].min = cell->min_width;
- if (col[i].max < cell->max_width)
- col[i].max = cell->max_width;
+ block->float_children = NULL;
+ block->cached_place_below_level = 0;
+ block->clear_level = 0;
+
+ /* special case if the block contains an object */
+ if (block->object) {
+ int temp_width = block->width;
+ if (!layout_block_object(block))
+ return false;
+ layout_get_object_dimensions(block, &temp_width,
+ &block->height, INT_MIN, INT_MAX,
+ INT_MIN, INT_MAX);
+ return true;
+ } else if (block->flags & REPLACE_DIM) {
+ return true;
}
- /* 2nd pass: cells which span multiple columns */
- 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) {
- unsigned int flexible_columns = 0;
- int min = 0, max = 0, fixed_width = 0, extra;
+ /* special case if the block contains an radio button or checkbox */
+ if (block->gadget && (block->gadget->type == GADGET_RADIO ||
+ block->gadget->type == GADGET_CHECKBOX)) {
+ /* form checkbox or radio button
+ * if width or height is AUTO, set it to 1em */
+ gadget_unit = CSS_UNIT_EM;
+ gadget_size = INTTOFIX(1);
+ if (block->height == AUTO)
+ block->height = FIXTOINT(nscss_len2px(gadget_size,
+ gadget_unit, block->style));
+ }
- if (cell->columns == 1)
- continue;
+ box = block->children;
+ /* set current coordinates to top-left of the block */
+ cx = 0;
+ y = cy = block->padding[TOP];
+ if (box)
+ box->y = block->padding[TOP];
- layout_minmax_block(cell, font_func);
- i = cell->start_column;
+ /* Step through the descendants of the block in depth-first order, but
+ * not into the children of boxes which aren't blocks. For example, if
+ * the tree passed to this function looks like this (box->type shown):
+ *
+ * block -> BOX_BLOCK
+ * BOX_BLOCK * (1)
+ * BOX_INLINE_CONTAINER * (2)
+ * BOX_INLINE
+ * BOX_TEXT
+ * ...
+ * BOX_BLOCK * (3)
+ * BOX_TABLE * (4)
+ * BOX_TABLE_ROW
+ * BOX_TABLE_CELL
+ * ...
+ * BOX_TABLE_CELL
+ * ...
+ * BOX_BLOCK * (5)
+ * BOX_INLINE_CONTAINER * (6)
+ * BOX_TEXT
+ * ...
+ * then the while loop will visit each box marked with *, setting box
+ * to each in the order shown. */
+ while (box) {
+ enum css_overflow_e overflow_x = CSS_OVERFLOW_VISIBLE;
+ enum css_overflow_e overflow_y = CSS_OVERFLOW_VISIBLE;
- /* find min width so far of spanned columns, and count
- * number of non-fixed spanned columns and total fixed width */
- for (j = 0; j != cell->columns; j++) {
- min += col[i + j].min;
- if (col[i + j].type == COLUMN_WIDTH_FIXED)
- fixed_width += col[i + j].width;
- else
- flexible_columns++;
+ assert(box->type == BOX_BLOCK || box->type == BOX_TABLE ||
+ box->type == BOX_INLINE_CONTAINER);
+
+ /* Tables are laid out before being positioned, because the
+ * position depends on the width which is calculated in
+ * table layout. Blocks and inline containers are positioned
+ * before being laid out, because width is not dependent on
+ * content, and the position is required during layout for
+ * correct handling of floats.
+ */
+
+ if (box->style &&
+ (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,
+ * so no need to look at its children now. */
+ goto advance_to_next_box;
}
- min += (cell->columns - 1) * border_spacing_h;
- /* distribute extra min to spanned columns */
- if (min < cell->min_width) {
- if (flexible_columns == 0) {
- extra = 1 + (cell->min_width - min) /
- cell->columns;
- for (j = 0; j != cell->columns; j++) {
- col[i + j].min += extra;
- if (col[i + j].max < col[i + j].min)
- col[i + j].max = col[i + j].min;
- }
- } else {
- extra = 1 + (cell->min_width - min) /
- flexible_columns;
- for (j = 0; j != cell->columns; j++) {
- if (col[i + j].type !=
- COLUMN_WIDTH_FIXED) {
- col[i + j].min += extra;
- if (col[i + j].max <
- col[i + j].min)
- col[i + j].max =
- col[i + j].min;
- }
+ /* If we don't know which box the current margin collapses
+ * through to, find out. Update the pos/neg margin values. */
+ if (margin_collapse == NULL) {
+ margin_collapse = layout_next_margin_block(box, block,
+ viewport_height,
+ &max_pos_margin, &max_neg_margin);
+ /* We have a margin that has not yet been applied. */
+ in_margin = true;
+ }
+
+ /* Clearance. */
+ y = 0;
+ if (box->style && css_computed_clear(box->style) !=
+ CSS_CLEAR_NONE)
+ y = layout_clear(block->float_children,
+ css_computed_clear(box->style));
+
+ /* Find box's overflow properties */
+ if (box->style) {
+ overflow_x = css_computed_overflow_x(box->style);
+ overflow_y = css_computed_overflow_y(box->style);
+ }
+
+ /* Blocks establishing a block formatting context get minimum
+ * left and right margins to avoid any floats. */
+ lm = rm = 0;
+
+ if (box->type == BOX_BLOCK || box->flags & IFRAME) {
+ if (!box->object && !(box->flags & IFRAME) &&
+ !(box->flags & REPLACE_DIM) &&
+ box->style &&
+ (overflow_x != CSS_OVERFLOW_VISIBLE ||
+ overflow_y != CSS_OVERFLOW_VISIBLE)) {
+ /* box establishes new block formatting context
+ * so available width may be diminished due to
+ * floats. */
+ int x0, x1, top;
+ struct box *left, *right;
+ top = cy + max_pos_margin - max_neg_margin;
+ top = (top > y) ? top : y;
+ x0 = cx;
+ x1 = cx + box->parent->width -
+ box->parent->padding[LEFT] -
+ box->parent->padding[RIGHT];
+ find_sides(block->float_children, top, top,
+ &x0, &x1, &left, &right);
+ /* calculate min required left & right margins
+ * needed to avoid floats */
+ lm = x0 - cx;
+ rm = cx + box->parent->width -
+ box->parent->padding[LEFT] -
+ box->parent->padding[RIGHT] -
+ x1;
+ }
+ layout_block_find_dimensions(box->parent->width,
+ viewport_height, lm, rm, box);
+ if (box->type == BOX_BLOCK && !(box->flags & IFRAME)) {
+ layout_block_add_scrollbar(box, RIGHT);
+ layout_block_add_scrollbar(box, BOTTOM);
+ }
+ } else if (box->type == BOX_TABLE) {
+ if (box->style != NULL) {
+ enum css_width_e 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;
+ struct box *left, *right;
+ top = cy + max_pos_margin -
+ max_neg_margin;
+ top = (top > y) ? top : y;
+ x0 = cx;
+ x1 = cx + box->parent->width -
+ box->parent->padding[LEFT] -
+ box->parent->padding[RIGHT];
+ find_sides(block->float_children,
+ top, top, &x0, &x1,
+ &left, &right);
+ /* calculate min required left & right
+ * margins needed to avoid floats */
+ lm = x0 - cx;
+ rm = cx + box->parent->width -
+ box->parent->padding[LEFT] -
+ box->parent->padding[RIGHT] -
+ x1;
}
}
+ if (!layout_table(box, box->parent->width - lm - rm,
+ content))
+ return false;
+ layout_solve_width(box, box->parent->width, box->width,
+ lm, rm, -1, -1);
}
- /* find max width so far of spanned columns */
- for (j = 0; j != cell->columns; j++)
- max += col[i + j].max;
- max += (cell->columns - 1) * border_spacing_h;
+ /* Position box: horizontal. */
+ box->x = box->parent->padding[LEFT] + box->margin[LEFT] +
+ box->border[LEFT].width;
+ cx += box->x;
- /* distribute extra max to spanned columns */
- if (max < cell->max_width && flexible_columns) {
- extra = 1 + (cell->max_width - max) / flexible_columns;
- for (j = 0; j != cell->columns; j++)
- if (col[i + j].type != COLUMN_WIDTH_FIXED)
- col[i + j].max += extra;
+ /* Position box: vertical. */
+ if (box->border[TOP].width) {
+ box->y += box->border[TOP].width;
+ cy += box->border[TOP].width;
}
- }
- for (i = 0; i != table->columns; i++) {
- if (col[i].max < col[i].min) {
- box_dump(stderr, table, 0, true);
- assert(0);
+ /* Vertical margin */
+ if (((box->type == BOX_BLOCK &&
+ (box->flags & HAS_HEIGHT)) ||
+ box->type == BOX_TABLE ||
+ (box->type == BOX_INLINE_CONTAINER &&
+ box != box->parent->children) ||
+ margin_collapse == box) &&
+ in_margin == true) {
+ /* Margin goes above this box. */
+ cy += max_pos_margin - max_neg_margin;
+ box->y += max_pos_margin - max_neg_margin;
+
+ /* Current margin has been applied. */
+ in_margin = false;
+ max_pos_margin = max_neg_margin = 0;
}
- table_min += col[i].min;
- table_max += col[i].max;
- }
- /* fixed width takes priority, unless it is too narrow */
- 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)
- table_max = width;
- }
+ /* Handle clearance */
+ if (box->type != BOX_INLINE_CONTAINER &&
+ (y > 0) && (cy < y)) {
+ /* box clears something*/
+ box->y += y - cy;
+ cy = y;
+ }
- /* add margins, border, padding to min, max widths */
- calculate_mbp_width(table->style, LEFT, true, true, true,
- &extra_fixed, &extra_frac);
- calculate_mbp_width(table->style, RIGHT, true, true, true,
- &extra_fixed, &extra_frac);
- if (extra_fixed < 0)
- extra_fixed = 0;
- if (extra_frac < 0)
- extra_frac = 0;
- if (1.0 <= extra_frac)
- extra_frac = 0.9;
- table->min_width = (table_min + extra_fixed) / (1.0 - extra_frac);
- table->max_width = (table_max + extra_fixed) / (1.0 - extra_frac);
- table->min_width += (table->columns + 1) * border_spacing_h;
- table->max_width += (table->columns + 1) * border_spacing_h;
+ /* Unless the box has an overflow style of visible, the box
+ * establishes a new block context. */
+ if (box->type == BOX_BLOCK && box->style &&
+ (overflow_x != CSS_OVERFLOW_VISIBLE ||
+ overflow_y != CSS_OVERFLOW_VISIBLE)) {
- assert(0 <= table->min_width && table->min_width <= table->max_width);
-}
+ layout_block_context(box, viewport_height, content);
+ cy += box->padding[TOP];
-/**
- * Moves the children of a box by a specified amount
- *
- * \param box top of tree of boxes
- * \param x the amount to move children by horizontally
- * \param y the amount to move children by vertically
- */
+ if (box->height == AUTO) {
+ box->height = 0;
+ layout_block_add_scrollbar(box, BOTTOM);
+ }
-void layout_move_children(struct box *box, int x, int y)
-{
- assert(box);
+ cx -= box->x;
+ cy += box->height + box->padding[BOTTOM] +
+ box->border[BOTTOM].width;
+ y = box->y + box->padding[TOP] + box->height +
+ box->padding[BOTTOM] +
+ box->border[BOTTOM].width;
- for (box = box->children; box; box = box->next) {
- box->x += x;
- box->y += y;
- }
-}
+ /* Skip children, because they are done in the new
+ * block context */
+ goto advance_to_next_box;
+ }
+#ifdef LAYOUT_DEBUG
+ LOG("box %p, cx %i, cy %i", box, cx, cy);
+#endif
-/**
- * Determine width of margin, borders, and padding on one side of a box.
- *
- * \param style style to measure
- * \param side side of box to measure
- * \param margin whether margin width is required
- * \param border whether border width is required
- * \param padding whether padding width is required
- * \param fixed increased by sum of fixed margin, border, and padding
- * \param frac increased by sum of fractional margin and padding
- */
+ /* Layout (except tables). */
+ if (box->object) {
+ if (!layout_block_object(box))
+ return false;
-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);
+ } else if (box->type == BOX_INLINE_CONTAINER) {
+ box->width = box->parent->width;
+ if (!layout_inline_container(box, box->width, block,
+ cx, cy, content))
+ return false;
- 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 }
- };
+ } else if (box->type == BOX_TABLE) {
+ /* Move down to avoid floats if necessary. */
+ int x0, x1;
+ struct box *left, *right;
+ y = cy;
+ while (1) {
+ enum css_width_e wtype;
+ css_fixed width = 0;
+ css_unit unit = CSS_UNIT_PX;
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
+ wtype = css_computed_width(box->style,
+ &width, &unit);
- assert(style);
+ x0 = cx;
+ x1 = cx + box->parent->width;
+ find_sides(block->float_children, y,
+ y + box->height,
+ &x0, &x1, &left, &right);
+ if (wtype == CSS_WIDTH_AUTO)
+ break;
+ if (box->width <= x1 - x0)
+ break;
+ if (!left && !right)
+ break;
+ else if (!left)
+ y = right->y + right->height + 1;
+ else if (!right)
+ y = left->y + left->height + 1;
+ else if (left->y + left->height <
+ right->y + right->height)
+ y = left->y + left->height + 1;
+ else
+ y = right->y + right->height + 1;
+ }
+ box->x += x0 - cx;
+ cx = x0;
+ box->y += y - cy;
+ cy = y;
+ }
- /* margin */
- if (margin) {
- enum css_margin_e type;
+ /* Advance to next box. */
+ if (box->type == BOX_BLOCK && !box->object && !(box->iframe) &&
+ box->children) {
+ /* Down into children. */
- type = margin_funcs[side](style, &value, &unit);
- if (type == CSS_MARGIN_SET) {
- if (unit == CSS_UNIT_PCT) {
- *frac += FIXTOINT(FDIV(value, F_100));
- } else {
- *fixed += FIXTOINT(nscss_len2px(value, unit,
- style));
+ if (box == margin_collapse) {
+ /* Current margin collapsed though to this box.
+ * Unset margin_collapse. */
+ margin_collapse = NULL;
}
+
+ y = box->padding[TOP];
+ box = box->children;
+ box->y = y;
+ cy += y;
+ continue;
+ } else if (box->type == BOX_BLOCK || box->object ||
+ box->flags & IFRAME)
+ cy += box->padding[TOP];
+
+ if (box->type == BOX_BLOCK && box->height == AUTO) {
+ box->height = 0;
+ layout_block_add_scrollbar(box, BOTTOM);
}
- }
- /* border */
- if (border) {
- if (border_funcs[side].style(style) !=
- CSS_BORDER_STYLE_NONE) {
- border_funcs[side].width(style, &value, &unit);
+ cy += box->height + box->padding[BOTTOM] +
+ box->border[BOTTOM].width;
+ cx -= box->x;
+ y = box->y + box->padding[TOP] + box->height +
+ box->padding[BOTTOM] +
+ box->border[BOTTOM].width;
- *fixed += FIXTOINT(nscss_len2px(value, unit, style));
+ advance_to_next_box:
+ if (!box->next) {
+ /* No more siblings:
+ * up to first ancestor with a sibling. */
+
+ do {
+ if (box == margin_collapse) {
+ /* Current margin collapsed though to
+ * this box. Unset margin_collapse. */
+ margin_collapse = NULL;
+ }
+
+ /* Apply bottom margin */
+ if (max_pos_margin < box->margin[BOTTOM])
+ max_pos_margin = box->margin[BOTTOM];
+ else if (max_neg_margin < -box->margin[BOTTOM])
+ max_neg_margin = -box->margin[BOTTOM];
+
+ box = box->parent;
+ if (box == block)
+ break;
+
+ /* Margin is invalidated if this is a box
+ * margins can't collapse through. */
+ if (box->type == BOX_BLOCK &&
+ box->flags & MAKE_HEIGHT) {
+ margin_collapse = NULL;
+ in_margin = false;
+ max_pos_margin = max_neg_margin = 0;
+ }
+
+ if (box->height == AUTO) {
+ box->height = y - box->padding[TOP];
+
+ if (box->type == BOX_BLOCK)
+ layout_block_add_scrollbar(box,
+ BOTTOM);
+ } else
+ cy += box->height -
+ (y - box->padding[TOP]);
+
+ /* Apply any min-height and max-height to
+ * boxes in normal flow */
+ if (box->style &&
+ css_computed_position(box->style) !=
+ CSS_POSITION_ABSOLUTE &&
+ layout_apply_minmax_height(box,
+ NULL)) {
+ /* Height altered */
+ /* Set current cy */
+ cy += box->height -
+ (y - box->padding[TOP]);
+ }
+
+ cy += box->padding[BOTTOM] +
+ box->border[BOTTOM].width;
+ cx -= box->x;
+ y = box->y + box->padding[TOP] + box->height +
+ box->padding[BOTTOM] +
+ box->border[BOTTOM].width;
+
+ } while (box->next == NULL);
+ if (box == block)
+ break;
}
- }
- /* padding */
- if (padding) {
- padding_funcs[side](style, &value, &unit);
- if (unit == CSS_UNIT_PCT) {
- *frac += FIXTOINT(FDIV(value, F_100));
- } else {
- *fixed += FIXTOINT(nscss_len2px(value, unit, style));
+ /* To next sibling. */
+
+ if (box == margin_collapse) {
+ /* Current margin collapsed though to this box.
+ * Unset margin_collapse. */
+ margin_collapse = NULL;
}
+
+ if (max_pos_margin < box->margin[BOTTOM])
+ max_pos_margin = box->margin[BOTTOM];
+ else if (max_neg_margin < -box->margin[BOTTOM])
+ max_neg_margin = -box->margin[BOTTOM];
+
+ box = box->next;
+ box->y = y;
+ }
+
+ /* Account for bottom margin of last contained block */
+ cy += max_pos_margin - max_neg_margin;
+
+ /* 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].width + box->margin[BOTTOM];
+ if (cy < y)
+ cy = y;
+ }
+
+ if (block->height == AUTO) {
+ block->height = cy - block->padding[TOP];
+ if (block->type == BOX_BLOCK)
+ layout_block_add_scrollbar(block, BOTTOM);
+ }
+
+ if (block->style && css_computed_position(block->style) !=
+ CSS_POSITION_ABSOLUTE) {
+ /* Block is in normal flow */
+ layout_apply_minmax_height(block, NULL);
}
+
+ if (block->gadget &&
+ (block->gadget->type == GADGET_TEXTAREA ||
+ block->gadget->type == GADGET_PASSWORD ||
+ block->gadget->type == GADGET_TEXTBOX)) {
+ int ta_width = block->padding[LEFT] + block->width +
+ block->padding[RIGHT];
+ int ta_height = block->padding[TOP] + block->height +
+ block->padding[BOTTOM];
+ textarea_set_layout(block->gadget->data.text.ta,
+ ta_width, ta_height,
+ block->padding[TOP], block->padding[RIGHT],
+ block->padding[BOTTOM], block->padding[LEFT]);
+ }
+
+ return true;
}
/**
- * Layout list markers.
+ * Calculate line height from a style.
*/
+static int line_height(const css_computed_style *style)
+{
+ enum css_line_height_e lhtype;
+ css_fixed lhvalue = 0;
+ css_unit lhunit = CSS_UNIT_PX;
+ css_fixed line_height;
-void layout_lists(struct box *box,
- const struct font_functions *font_func)
+ assert(style);
+
+ 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;
+ }
+
+ if (lhtype == CSS_LINE_HEIGHT_NUMBER ||
+ lhunit == CSS_UNIT_PCT) {
+ line_height = nscss_len2px(lhvalue, CSS_UNIT_EM, style);
+
+ if (lhtype != CSS_LINE_HEIGHT_NUMBER)
+ line_height = FDIV(line_height, F_100);
+ } else {
+ assert(lhunit != CSS_UNIT_PCT);
+
+ line_height = nscss_len2px(lhvalue, lhunit, style);
+ }
+
+ return FIXTOINT(line_height);
+}
+
+
+/**
+ * Layout list markers.
+ */
+static void
+layout_lists(struct box *box,
+ const struct gui_layout_table *font_func)
{
struct box *child;
struct box *marker;
@@ -4337,10 +2961,10 @@ void layout_lists(struct box *box,
if (child->list_marker) {
marker = child->list_marker;
if (marker->object) {
- marker->width =
+ marker->width =
content_get_width(marker->object);
marker->x = -marker->width;
- marker->height =
+ marker->height =
content_get_height(marker->object);
marker->y = (line_height(marker->style) -
marker->height) / 2;
@@ -4348,7 +2972,7 @@ void layout_lists(struct box *box,
if (marker->width == UNKNOWN_WIDTH) {
font_plot_style_from_css(marker->style,
&fstyle);
- font_func->font_width(&fstyle,
+ font_func->width(&fstyle,
marker->text,
marker->length,
&marker->width);
@@ -4372,241 +2996,87 @@ void layout_lists(struct box *box,
/**
- * Adjust positions of relatively positioned boxes.
+ * Compute box offsets for a relatively or absolutely positioned box with
+ * respect to a box.
*
- * \param root box to adjust the position of
- * \param fp box which forms the block formatting context for children of
- * "root" which are floats
- * \param fx x offset due to intervening relatively positioned boxes
- * between current box, "root", and the block formatting context
- * box, "fp", for float children of "root"
- * \param fy y offset due to intervening relatively positioned boxes
- * between current box, "root", and the block formatting context
- * box, "fp", for float children of "root"
+ * \param box box to compute offsets for
+ * \param containing_block box to compute percentages with respect to
+ * \param top updated to top offset, or AUTO
+ * \param right updated to right offset, or AUTO
+ * \param bottom updated to bottom offset, or AUTO
+ * \param left updated to left offset, or AUTO
+ *
+ * See CSS 2.1 9.3.2. containing_block must have width and height.
*/
-
-void layout_position_relative(struct box *root, struct box *fp, int fx, int fy)
+static void
+layout_compute_offsets(struct box *box,
+ struct box *containing_block,
+ int *top,
+ int *right,
+ int *bottom,
+ int *left)
{
- struct box *box; /* for children of "root" */
- struct box *fn; /* for block formatting context box for children of
- * "box" */
- struct box *fc; /* for float children of the block formatting context,
- * "fp" */
- int x, y; /* for the offsets resulting from any relative
- * positioning on the current block */
- int fnx, fny; /* for affsets which apply to flat children of "box" */
-
- /**\todo ensure containing box is large enough after moving boxes */
-
- assert(root);
-
- /* Normal children */
- for (box = root->children; box; box = box->next) {
-
- if (box->type == BOX_TEXT)
- continue;
-
- /* If relatively positioned, get offsets */
- if (box->style && css_computed_position(box->style) ==
- CSS_POSITION_RELATIVE)
- layout_compute_relative_offset(box, &x, &y);
- else
- x = y = 0;
+ uint32_t type;
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
- /* Adjust float coordinates.
- * (note float x and y are relative to their block formatting
- * context box and not their parent) */
- 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 */
- for (fc = fp->float_children; fc; fc = fc->next_float) {
- if (box == fc->children) {
- /* Box is floated in the block
- * formatting context block, fp.
- * Apply float offsets. */
- box->x += fx;
- box->y += fy;
- fx = fy = 0;
- }
- }
- }
+ assert(containing_block->width != UNKNOWN_WIDTH &&
+ containing_block->width != AUTO &&
+ containing_block->height != AUTO);
- if (box->float_children) {
- fn = box;
- fnx = fny = 0;
+ /* left */
+ type = css_computed_left(box->style, &value, &unit);
+ if (type == CSS_LEFT_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ *left = FPCT_OF_INT_TOINT(value,
+ containing_block->width);
} else {
- fn = fp;
- fnx = fx + x;
- fny = fy + y;
- }
-
- /* recurse first */
- layout_position_relative(box, fn, fnx, fny);
-
- /* Ignore things we're not interested in. */
- if (!box->style || (box->style &&
- css_computed_position(box->style) !=
- CSS_POSITION_RELATIVE))
- continue;
-
- box->x += x;
- box->y += y;
-
- /* Handle INLINEs - their "children" are in fact
- * the sibling boxes between the INLINE and
- * INLINE_END boxes */
- if (box->type == BOX_INLINE && box->inline_end) {
- struct box *b;
- for (b = box->next; b && b != box->inline_end;
- b = b->next) {
- b->x += x;
- b->y += y;
- }
+ *left = FIXTOINT(nscss_len2px(value, unit, box->style));
}
- }
-}
-
-
-/**
- * Compute a box's relative offset as per CSS 2.1 9.4.3
- *
- * \param box Box to compute relative offsets for.
- * \param x Receives relative offset in x.
- * \param y Receives relative offset in y.
- */
-
-void layout_compute_relative_offset(struct box *box, int *x, int *y)
-{
- int left, right, top, bottom;
- struct box *containing_block;
-
- assert(box && box->parent && box->style &&
- css_computed_position(box->style) ==
- CSS_POSITION_RELATIVE);
-
- 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;
+ *left = AUTO;
}
- layout_compute_offsets(box, containing_block,
- &top, &right, &bottom, &left);
-
- if (left == AUTO && right == AUTO)
- left = right = 0;
- else if (left == AUTO)
- /* left is auto => computed = -right */
- left = -right;
- else if (right == AUTO)
- /* right is auto => computed = -left */
- right = -left;
- else {
- /* over constrained => examine direction property
- * of containing block */
- if (containing_block->style &&
- css_computed_direction(
- containing_block->style) ==
- CSS_DIRECTION_RTL) {
- /* right wins */
- left = -right;
+ /* right */
+ type = css_computed_right(box->style, &value, &unit);
+ if (type == CSS_RIGHT_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ *right = FPCT_OF_INT_TOINT(value,
+ containing_block->width);
} else {
- /* assume LTR in all other cases */
- right = -left;
+ *right = FIXTOINT(nscss_len2px(value, unit,
+ box->style));
}
+ } else {
+ *right = AUTO;
}
- assert(left == -right);
-
- if (top == AUTO && bottom == AUTO) {
- top = bottom = 0;
- } else if (top == AUTO) {
- top = -bottom;
+ /* top */
+ type = css_computed_top(box->style, &value, &unit);
+ if (type == CSS_TOP_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ *top = FPCT_OF_INT_TOINT(value,
+ containing_block->height);
+ } else {
+ *top = FIXTOINT(nscss_len2px(value, unit, box->style));
+ }
} else {
- /* bottom is AUTO, or neither are AUTO */
- bottom = -top;
+ *top = AUTO;
}
-#ifdef LAYOUT_DEBUG
- LOG("left %i, right %i, top %i, bottom %i", left, right, top, bottom);
-#endif
-
- *x = left;
- *y = top;
-}
-
-
-/**
- * Recursively layout and position absolutely positioned boxes.
- *
- * \param box tree of boxes to layout
- * \param containing_block current containing block
- * \param cx position of box relative to containing_block
- * \param cy position of box relative to containing_block
- * \param content memory pool for any new boxes
- * \return true on success, false on memory exhaustion
- */
-
-bool layout_position_absolute(struct box *box,
- struct box *containing_block,
- int cx, int cy,
- html_content *content)
-{
- struct box *c;
-
- for (c = box->children; c; c = c->next) {
- if ((c->type == BOX_BLOCK || c->type == BOX_TABLE ||
- c->type == BOX_INLINE_BLOCK) &&
- (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 && css_computed_position(c->style) ==
- CSS_POSITION_RELATIVE) {
- if (!layout_position_absolute(c, c, 0, 0, content))
- return false;
+ /* bottom */
+ type = css_computed_bottom(box->style, &value, &unit);
+ if (type == CSS_BOTTOM_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ *bottom = FPCT_OF_INT_TOINT(value,
+ containing_block->height);
} else {
- int px, py;
- 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
- * to parent */
- struct box *p;
- px = c->x;
- py = c->y;
- for (p = box->parent; p && !p->float_children;
- p = p->parent) {
- px -= p->x;
- py -= p->y;
- }
- } else {
- /* Not a float, so box x/y coords are relative
- * to parent */
- px = c->x;
- py = c->y;
- }
- if (!layout_position_absolute(c, containing_block,
- cx + px, cy + py, content))
- return false;
+ *bottom = FIXTOINT(nscss_len2px(value, unit,
+ box->style));
}
+ } else {
+ *bottom = AUTO;
}
-
- return true;
}
@@ -4620,8 +3090,9 @@ bool layout_position_absolute(struct box *box,
* \param content memory pool for any new boxes
* \return true on success, false on memory exhaustion
*/
-
-bool layout_absolute(struct box *box, struct box *containing_block,
+static bool
+layout_absolute(struct box *box,
+ struct box *containing_block,
int cx, int cy,
html_content *content)
{
@@ -4857,7 +3328,7 @@ bool layout_absolute(struct box *box, struct box *containing_block,
if (!layout_block_context(box, -1, content))
return false;
} else if (box->type == BOX_TABLE) {
- /* layout_table also expects the containing block to be
+ /* layout_table also expects the containing block to be
* stored in the float_container field */
box->float_container = containing_block;
/* \todo layout_table considers margins etc. again */
@@ -4979,84 +3450,1565 @@ bool layout_absolute(struct box *box, struct box *containing_block,
/**
- * Compute box offsets for a relatively or absolutely positioned box with
- * respect to a box.
- *
- * \param box box to compute offsets for
- * \param containing_block box to compute percentages with respect to
- * \param top updated to top offset, or AUTO
- * \param right updated to right offset, or AUTO
- * \param bottom updated to bottom offset, or AUTO
- * \param left updated to left offset, or AUTO
+ * Recursively layout and position absolutely positioned boxes.
*
- * See CSS 2.1 9.3.2. containing_block must have width and height.
+ * \param box tree of boxes to layout
+ * \param containing_block current containing block
+ * \param cx position of box relative to containing_block
+ * \param cy position of box relative to containing_block
+ * \param content memory pool for any new boxes
+ * \return true on success, false on memory exhaustion
*/
+static bool
+layout_position_absolute(struct box *box,
+ struct box *containing_block,
+ int cx, int cy,
+ html_content *content)
+{
+ struct box *c;
-void layout_compute_offsets(struct box *box,
- struct box *containing_block,
- int *top, int *right, int *bottom, int *left)
+ for (c = box->children; c; c = c->next) {
+ if ((c->type == BOX_BLOCK || c->type == BOX_TABLE ||
+ c->type == BOX_INLINE_BLOCK) &&
+ (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 && 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 && (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
+ * to parent */
+ struct box *p;
+ px = c->x;
+ py = c->y;
+ for (p = box->parent; p && !p->float_children;
+ p = p->parent) {
+ px -= p->x;
+ py -= p->y;
+ }
+ } else {
+ /* Not a float, so box x/y coords are relative
+ * to parent */
+ px = c->x;
+ py = c->y;
+ }
+ if (!layout_position_absolute(c, containing_block,
+ cx + px, cy + py, content))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Compute a box's relative offset as per CSS 2.1 9.4.3
+ *
+ * \param box Box to compute relative offsets for.
+ * \param x Receives relative offset in x.
+ * \param y Receives relative offset in y.
+ */
+static void layout_compute_relative_offset(struct box *box, int *x, int *y)
{
- uint32_t type;
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
+ int left, right, top, bottom;
+ struct box *containing_block;
- assert(containing_block->width != UNKNOWN_WIDTH &&
- containing_block->width != AUTO &&
- containing_block->height != AUTO);
+ assert(box && box->parent && box->style &&
+ css_computed_position(box->style) ==
+ CSS_POSITION_RELATIVE);
- /* left */
- type = css_computed_left(box->style, &value, &unit);
- if (type == CSS_LEFT_SET) {
- if (unit == CSS_UNIT_PCT) {
- *left = FPCT_OF_INT_TOINT(value,
- containing_block->width);
+ 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;
+ }
+
+ layout_compute_offsets(box, containing_block,
+ &top, &right, &bottom, &left);
+
+ if (left == AUTO && right == AUTO)
+ left = right = 0;
+ else if (left == AUTO)
+ /* left is auto => computed = -right */
+ left = -right;
+ else if (right == AUTO)
+ /* right is auto => computed = -left */
+ right = -left;
+ else {
+ /* over constrained => examine direction property
+ * of containing block */
+ if (containing_block->style &&
+ css_computed_direction(
+ containing_block->style) ==
+ CSS_DIRECTION_RTL) {
+ /* right wins */
+ left = -right;
} else {
- *left = FIXTOINT(nscss_len2px(value, unit, box->style));
+ /* assume LTR in all other cases */
+ right = -left;
}
+ }
+
+ assert(left == -right);
+
+ if (top == AUTO && bottom == AUTO) {
+ top = bottom = 0;
+ } else if (top == AUTO) {
+ top = -bottom;
} else {
- *left = AUTO;
+ /* bottom is AUTO, or neither are AUTO */
+ bottom = -top;
}
- /* right */
- type = css_computed_right(box->style, &value, &unit);
- if (type == CSS_RIGHT_SET) {
- if (unit == CSS_UNIT_PCT) {
- *right = FPCT_OF_INT_TOINT(value,
- containing_block->width);
+#ifdef LAYOUT_DEBUG
+ LOG("left %i, right %i, top %i, bottom %i", left, right, top, bottom);
+#endif
+
+ *x = left;
+ *y = top;
+}
+
+
+/**
+ * Adjust positions of relatively positioned boxes.
+ *
+ * \param root box to adjust the position of
+ * \param fp box which forms the block formatting context for children of
+ * "root" which are floats
+ * \param fx x offset due to intervening relatively positioned boxes
+ * between current box, "root", and the block formatting context
+ * box, "fp", for float children of "root"
+ * \param fy y offset due to intervening relatively positioned boxes
+ * between current box, "root", and the block formatting context
+ * box, "fp", for float children of "root"
+ */
+static void
+layout_position_relative(struct box *root, struct box *fp, int fx, int fy)
+{
+ struct box *box; /* for children of "root" */
+ struct box *fn; /* for block formatting context box for children of
+ * "box" */
+ struct box *fc; /* for float children of the block formatting context,
+ * "fp" */
+ int x, y; /* for the offsets resulting from any relative
+ * positioning on the current block */
+ int fnx, fny; /* for affsets which apply to flat children of "box" */
+
+ /**\todo ensure containing box is large enough after moving boxes */
+
+ assert(root);
+
+ /* Normal children */
+ for (box = root->children; box; box = box->next) {
+
+ if (box->type == BOX_TEXT)
+ continue;
+
+ /* If relatively positioned, get offsets */
+ if (box->style && css_computed_position(box->style) ==
+ CSS_POSITION_RELATIVE)
+ layout_compute_relative_offset(box, &x, &y);
+ else
+ x = y = 0;
+
+ /* Adjust float coordinates.
+ * (note float x and y are relative to their block formatting
+ * context box and not their parent) */
+ 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 */
+ for (fc = fp->float_children; fc; fc = fc->next_float) {
+ if (box == fc->children) {
+ /* Box is floated in the block
+ * formatting context block, fp.
+ * Apply float offsets. */
+ box->x += fx;
+ box->y += fy;
+ fx = fy = 0;
+ }
+ }
+ }
+
+ if (box->float_children) {
+ fn = box;
+ fnx = fny = 0;
} else {
- *right = FIXTOINT(nscss_len2px(value, unit,
- box->style));
+ fn = fp;
+ fnx = fx + x;
+ fny = fy + y;
}
+
+ /* recurse first */
+ layout_position_relative(box, fn, fnx, fny);
+
+ /* Ignore things we're not interested in. */
+ if (!box->style || (box->style &&
+ css_computed_position(box->style) !=
+ CSS_POSITION_RELATIVE))
+ continue;
+
+ box->x += x;
+ box->y += y;
+
+ /* Handle INLINEs - their "children" are in fact
+ * the sibling boxes between the INLINE and
+ * INLINE_END boxes */
+ if (box->type == BOX_INLINE && box->inline_end) {
+ struct box *b;
+ for (b = box->next; b && b != box->inline_end;
+ b = b->next) {
+ b->x += x;
+ b->y += y;
+ }
+ }
+ }
+}
+
+
+/* exported function documented in render/layout.h */
+bool layout_document(html_content *content, int width, int height)
+{
+ bool ret;
+ struct box *doc = content->layout;
+ const struct gui_layout_table *font_func = content->font_func;
+
+ layout_minmax_block(doc, font_func);
+
+ layout_block_find_dimensions(width, height, 0, 0, doc);
+ 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;
+
+ ret = layout_block_context(doc, height, content);
+
+ /* make <html> and <body> fill available height */
+ if (doc->y + doc->padding[TOP] + doc->height + doc->padding[BOTTOM] +
+ doc->border[BOTTOM].width + doc->margin[BOTTOM] <
+ height) {
+ doc->height = height - (doc->y + doc->padding[TOP] +
+ 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].width +
+ doc->children->padding[TOP] +
+ doc->children->padding[BOTTOM] +
+ doc->children->border[BOTTOM].width +
+ doc->children->margin[BOTTOM]);
+ }
+
+ layout_lists(doc, font_func);
+ layout_position_absolute(doc, doc, 0, 0, content);
+ layout_position_relative(doc, doc, 0, 0);
+
+ layout_calculate_descendant_bboxes(doc);
+
+ return ret;
+}
+
+
+/**
+ * Insert a float into a container.
+ *
+ * \param cont block formatting context block, used to contain float
+ * \param b box to add to float
+ *
+ * This sorts floats in order of descending bottom edges.
+ */
+static void add_float_to_container(struct box *cont, struct box *b)
+{
+ struct box *box = cont->float_children;
+ int b_bottom = b->y + b->height;
+
+ assert(b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT);
+
+ if (box == NULL) {
+ /* No other float children */
+ b->next_float = NULL;
+ cont->float_children = b;
+ return;
+ } else if (b_bottom >= box->y + box->height) {
+ /* Goes at start of list */
+ b->next_float = cont->float_children;
+ cont->float_children = b;
} else {
- *right = AUTO;
+ struct box *prev = NULL;
+ while (box != NULL && b_bottom < box->y + box->height) {
+ prev = box;
+ box = box->next_float;
+ }
+ if (prev != NULL) {
+ b->next_float = prev->next_float;
+ prev->next_float = b;
+ }
}
+}
- /* top */
- type = css_computed_top(box->style, &value, &unit);
- if (type == CSS_TOP_SET) {
- if (unit == CSS_UNIT_PCT) {
- *top = FPCT_OF_INT_TOINT(value,
- containing_block->height);
+
+/**
+ * Split a text box.
+ *
+ * \param content memory pool for any new boxes
+ * \param fstyle style for text in text box
+ * \param split_box box with text to split
+ * \param new_length new length for text in split_box, after splitting
+ * \param new_width new width for text in split_box, after splitting
+ * \return true on success, false on memory exhaustion
+ *
+ * A new box is created and inserted into the box tree after split_box,
+ * containing the text after new_length excluding the initial space character.
+ */
+static bool
+layout_text_box_split(html_content *content,
+ plot_font_style_t *fstyle,
+ struct box *split_box,
+ size_t new_length,
+ int new_width)
+{
+ int space_width = split_box->space;
+ struct box *c2;
+ const struct gui_layout_table *font_func = content->font_func;
+ bool space = (split_box->text[new_length] == ' ');
+ int used_length = new_length + (space ? 1 : 0);
+
+ if ((space && space_width == 0) || space_width == UNKNOWN_WIDTH) {
+ /* We're need to add a space, and we don't know how big
+ * it's to be, OR we have a space of unknown width anyway;
+ * Calculate space width */
+ font_func->width(fstyle, " ", 1, &space_width);
+ }
+
+ if (split_box->space == UNKNOWN_WIDTH)
+ split_box->space = space_width;
+ if (!space)
+ space_width = 0;
+
+ /* Create clone of split_box, c2 */
+ c2 = talloc_memdup(content->bctx, split_box, sizeof *c2);
+ if (!c2)
+ return false;
+ c2->flags |= CLONE;
+
+ /* Set remaining text in c2 */
+ c2->text += used_length;
+
+ /* Set c2 according to the remaining text */
+ c2->width -= new_width + space_width;
+ c2->flags &= ~MEASURED; /* width has been estimated */
+ c2->length = split_box->length - used_length;
+
+ /* Update split_box for its reduced text */
+ split_box->width = new_width;
+ split_box->flags |= MEASURED;
+ split_box->length = new_length;
+ split_box->space = space_width;
+
+ /* Insert c2 into box list */
+ c2->next = split_box->next;
+ split_box->next = c2;
+ c2->prev = split_box;
+ if (c2->next)
+ c2->next->prev = c2;
+ else
+ c2->parent->last = c2;
+#ifdef LAYOUT_DEBUG
+ LOG("split_box %p len: %u \"%.*s\"", split_box, split_box->length, split_box->length, split_box->text);
+ LOG(" new_box %p len: %u \"%.*s\"", c2, c2->length, c2->length, c2->text);
+#endif
+ return true;
+}
+
+
+/**
+ * Compute dimensions of box, margins, paddings, and borders for a floating
+ * element using shrink-to-fit. Also used for inline-blocks.
+ *
+ * \param available_width Max width available in pixels
+ * \param style Box's style
+ * \param box Box for which to find dimensions
+ * Box margins, borders, paddings, width and
+ * height are updated.
+ */
+static void
+layout_float_find_dimensions(int available_width,
+ const css_computed_style *style,
+ struct box *box)
+{
+ int width, height, max_width, min_width, max_height, min_height;
+ int *margin = box->margin;
+ int *padding = box->padding;
+ struct box_border *border = box->border;
+ enum css_overflow_e overflow_x = css_computed_overflow_x(style);
+ enum css_overflow_e overflow_y = css_computed_overflow_y(style);
+ int scrollbar_width_x =
+ (overflow_x == CSS_OVERFLOW_SCROLL ||
+ overflow_x == CSS_OVERFLOW_AUTO) ?
+ SCROLLBAR_WIDTH : 0;
+ int scrollbar_width_y =
+ (overflow_y == CSS_OVERFLOW_SCROLL ||
+ overflow_y == CSS_OVERFLOW_AUTO) ?
+ SCROLLBAR_WIDTH : 0;
+
+ layout_find_dimensions(available_width, -1, box, style, &width, &height,
+ &max_width, &min_width, &max_height, &min_height,
+ margin, padding, border);
+
+ if (margin[LEFT] == AUTO)
+ margin[LEFT] = 0;
+ if (margin[RIGHT] == AUTO)
+ margin[RIGHT] = 0;
+
+ if (box->gadget == NULL) {
+ padding[RIGHT] += scrollbar_width_y;
+ padding[BOTTOM] += scrollbar_width_x;
+ }
+
+ if (box->object && !(box->flags & REPLACE_DIM) &&
+ content_get_type(box->object) != CONTENT_HTML) {
+ /* Floating replaced element, with intrinsic width or height.
+ * See 10.3.6 and 10.6.2 */
+ layout_get_object_dimensions(box, &width, &height,
+ min_width, max_width, min_height, max_height);
+ } else if (box->gadget && (box->gadget->type == GADGET_TEXTBOX ||
+ box->gadget->type == GADGET_PASSWORD ||
+ box->gadget->type == GADGET_FILE ||
+ box->gadget->type == GADGET_TEXTAREA)) {
+ 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);
+
+ if (box->gadget->type == GADGET_TEXTBOX ||
+ box->gadget->type == GADGET_PASSWORD ||
+ box->gadget->type == GADGET_FILE) {
+ if (width == AUTO) {
+ size = INTTOFIX(10);
+ width = FIXTOINT(nscss_len2px(size, unit,
+ box->style));
+ }
+ if (box->gadget->type == GADGET_FILE &&
+ height == AUTO) {
+ size = FLTTOFIX(1.5);
+ height = FIXTOINT(nscss_len2px(size, unit,
+ box->style));
+ }
+ }
+ if (box->gadget->type == GADGET_TEXTAREA) {
+ if (width == AUTO) {
+ size = INTTOFIX(10);
+ width = FIXTOINT(nscss_len2px(size, unit,
+ box->style));
+ }
+ if (height == AUTO) {
+ 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 includes margin, borders and padding */
+ if (width == available_width) {
+ width -= box->margin[LEFT] + box->border[LEFT].width +
+ box->padding[LEFT] +
+ box->padding[RIGHT] +
+ box->border[RIGHT].width +
+ box->margin[RIGHT];
} else {
- *top = FIXTOINT(nscss_len2px(value, unit, box->style));
+ /* width was obtained from a min_width or max_width
+ * value, so need to use the same method for calculating
+ * mbp as was used in layout_minmax_block() */
+ int fixed = 0;
+ float frac = 0;
+ calculate_mbp_width(box->style, LEFT, true, true, true,
+ &fixed, &frac);
+ calculate_mbp_width(box->style, RIGHT, true, true, true,
+ &fixed, &frac);
+ if (fixed < 0)
+ fixed = 0;
+
+ width -= fixed;
}
+
+ if (max_width >= 0 && width > max_width) width = max_width;
+ if (min_width > 0 && width < min_width) width = min_width;
+
} else {
- *top = AUTO;
+ if (max_width >= 0 && width > max_width) width = max_width;
+ if (min_width > 0 && width < min_width) width = min_width;
+ width -= scrollbar_width_y;
}
- /* bottom */
- type = css_computed_bottom(box->style, &value, &unit);
- if (type == CSS_BOTTOM_SET) {
- if (unit == CSS_UNIT_PCT) {
- *bottom = FPCT_OF_INT_TOINT(value,
- containing_block->height);
- } else {
- *bottom = FIXTOINT(nscss_len2px(value, unit,
- box->style));
+ box->width = width;
+ box->height = height;
+
+ if (margin[TOP] == AUTO)
+ margin[TOP] = 0;
+ if (margin[BOTTOM] == AUTO)
+ margin[BOTTOM] = 0;
+}
+
+
+/**
+ * Layout the contents of a float or inline block.
+ *
+ * \param b float or inline block box
+ * \param width available width
+ * \param content memory pool for any new boxes
+ * \return true on success, false on memory exhaustion
+ */
+static bool layout_float(struct box *b, int width, html_content *content)
+{
+ assert(b->type == BOX_TABLE || b->type == BOX_BLOCK ||
+ b->type == BOX_INLINE_BLOCK);
+ layout_float_find_dimensions(width, b->style, b);
+ if (b->type == BOX_TABLE) {
+ if (!layout_table(b, width, content))
+ return false;
+ if (b->margin[LEFT] == AUTO)
+ b->margin[LEFT] = 0;
+ if (b->margin[RIGHT] == AUTO)
+ b->margin[RIGHT] = 0;
+ if (b->margin[TOP] == AUTO)
+ b->margin[TOP] = 0;
+ if (b->margin[BOTTOM] == AUTO)
+ b->margin[BOTTOM] = 0;
+ } else
+ return layout_block_context(b, -1, content);
+ return true;
+}
+
+
+/**
+ * Position a float in the first available space.
+ *
+ * \param c float box to position
+ * \param width available width
+ * \param cx x coordinate relative to cont to place float right of
+ * \param y y coordinate relative to cont to place float below
+ * \param cont ancestor box which defines horizontal space, for floats
+ */
+static void
+place_float_below(struct box *c, int width, int cx, int y, struct box *cont)
+{
+ int x0, x1, yy;
+ struct box *left;
+ struct box *right;
+
+ yy = y > cont->cached_place_below_level ?
+ y : cont->cached_place_below_level;
+
+#ifdef LAYOUT_DEBUG
+ LOG("c %p, width %i, cx %i, y %i, cont %p", c, width, cx, y, cont);
+#endif
+
+ do {
+ y = yy;
+ x0 = cx;
+ x1 = cx + width;
+ find_sides(cont->float_children, y, y + c->height, &x0, &x1,
+ &left, &right);
+ if (left != 0 && right != 0) {
+ yy = (left->y + left->height <
+ right->y + right->height ?
+ left->y + left->height :
+ right->y + right->height);
+ } else if (left == 0 && right != 0) {
+ yy = right->y + right->height;
+ } else if (left != 0 && right == 0) {
+ yy = left->y + left->height;
}
+ } while ((left != 0 || right != 0) && (c->width > x1 - x0));
+
+ if (c->type == BOX_FLOAT_LEFT) {
+ c->x = x0;
} else {
- *bottom = AUTO;
+ c->x = x1 - c->width;
}
+ c->y = y;
+ cont->cached_place_below_level = y;
+}
+
+
+/**
+ * Position a line of boxes in inline formatting context.
+ *
+ * \param first box at start of line
+ * \param width available width on input, updated with actual width on output
+ * (may be incorrect if the line gets split?)
+ * \param y coordinate of top of line, updated on exit to bottom
+ * \param cx coordinate of left of line relative to cont
+ * \param cy coordinate of top of line relative to cont
+ * \param cont ancestor box which defines horizontal space, for floats
+ * \param indent apply any first-line indent
+ * \param has_text_children at least one TEXT in the inline_container
+ * \param next_box updated to first box for next line, or 0 at end
+ * \param content memory pool for any new boxes
+ * \return true on success, false on memory exhaustion
+ */
+static bool
+layout_line(struct box *first,
+ int *width,
+ int *y,
+ int cx,
+ int cy,
+ struct box *cont,
+ bool indent,
+ bool has_text_children,
+ html_content *content,
+ struct box **next_box)
+{
+ int height, used_height;
+ int x0 = 0;
+ int x1 = *width;
+ int x, h, x_previous;
+ int fy = cy;
+ struct box *left;
+ struct box *right;
+ struct box *b;
+ struct box *split_box = 0;
+ struct box *d;
+ struct box *br_box = 0;
+ bool move_y = false;
+ bool place_below = false;
+ int space_before = 0, space_after = 0;
+ unsigned int inline_count = 0;
+ unsigned int i;
+ const struct gui_layout_table *font_func = content->font_func;
+ plot_font_style_t fstyle;
+
+#ifdef LAYOUT_DEBUG
+ LOG("first %p, first->text '%.*s', width %i, y %i, cx %i, cy %i", first, (int)first->length, first->text, *width, *y, cx, cy);
+#endif
+
+ /* find sides at top of line */
+ x0 += cx;
+ x1 += cx;
+ find_sides(cont->float_children, cy, cy, &x0, &x1, &left, &right);
+ x0 -= cx;
+ x1 -= cx;
+
+ if (indent)
+ x0 += layout_text_indent(first->parent->parent->style, *width);
+
+ if (x1 < x0)
+ x1 = x0;
+
+ /* get minimum line height from containing block.
+ * this is the line-height if there are text children and also in the
+ * case of an initially empty text input */
+ if (has_text_children || first->parent->parent->gadget)
+ used_height = height =
+ line_height(first->parent->parent->style);
+ else
+ /* inline containers with no text are usually for layout and
+ * look better with no minimum line-height */
+ used_height = height = 0;
+
+ /* pass 1: find height of line assuming sides at top of line: loop
+ * body executed at least once
+ * keep in sync with the loop in layout_minmax_line() */
+#ifdef LAYOUT_DEBUG
+ LOG("x0 %i, x1 %i, x1 - x0 %i", x0, x1, x1 - x0);
+#endif
+
+ for (x = 0, b = first; x <= x1 - x0 && b != 0; b = b->next) {
+ int min_width, max_width, min_height, max_height;
+
+ assert(b->type == BOX_INLINE || b->type == BOX_INLINE_BLOCK ||
+ b->type == BOX_FLOAT_LEFT ||
+ b->type == BOX_FLOAT_RIGHT ||
+ b->type == BOX_BR || b->type == BOX_TEXT ||
+ b->type == BOX_INLINE_END);
+
+#ifdef LAYOUT_DEBUG
+ LOG("pass 1: b %p, x %i", b, x);
+#endif
+
+ if (b->type == BOX_BR)
+ break;
+
+ if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT)
+ continue;
+ if (b->type == BOX_INLINE_BLOCK &&
+ (css_computed_position(b->style) ==
+ CSS_POSITION_ABSOLUTE ||
+ css_computed_position(b->style) ==
+ CSS_POSITION_FIXED))
+ continue;
+
+ assert(b->style != NULL);
+ font_plot_style_from_css(b->style, &fstyle);
+
+ x += space_after;
+
+ if (b->type == BOX_INLINE_BLOCK) {
+ if (b->max_width != UNKNOWN_WIDTH)
+ if (!layout_float(b, *width, content))
+ return false;
+ 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].width +
+ b->padding[LEFT] + b->width +
+ b->padding[RIGHT] +
+ b->border[RIGHT].width +
+ b->margin[RIGHT];
+ space_after = 0;
+ continue;
+ }
+
+ if (b->type == BOX_INLINE) {
+ /* calculate borders, margins, and padding */
+ layout_find_dimensions(*width, -1, b, b->style, 0, 0,
+ 0, 0, 0, 0, b->margin, b->padding,
+ b->border);
+ for (i = 0; i != 4; i++)
+ if (b->margin[i] == AUTO)
+ b->margin[i] = 0;
+ x += b->margin[LEFT] + b->border[LEFT].width +
+ b->padding[LEFT];
+ if (b->inline_end) {
+ b->inline_end->margin[RIGHT] = b->margin[RIGHT];
+ b->inline_end->padding[RIGHT] =
+ b->padding[RIGHT];
+ b->inline_end->border[RIGHT] =
+ b->border[RIGHT];
+ } else {
+ x += b->padding[RIGHT] +
+ b->border[RIGHT].width +
+ b->margin[RIGHT];
+ }
+ } else if (b->type == BOX_INLINE_END) {
+ b->width = 0;
+ if (b->space == UNKNOWN_WIDTH) {
+ font_func->width(&fstyle, " ", 1, &b->space);
+ /** \todo handle errors */
+ }
+ space_after = b->space;
+
+ x += b->padding[RIGHT] + b->border[RIGHT].width +
+ b->margin[RIGHT];
+ continue;
+ }
+
+ if (!b->object && !(b->flags & IFRAME) && !b->gadget &&
+ !(b->flags & REPLACE_DIM)) {
+ /* inline non-replaced, 10.3.1 and 10.6.1 */
+ b->height = line_height(b->style ? b->style :
+ b->parent->parent->style);
+ if (height < b->height)
+ height = b->height;
+
+ if (!b->text) {
+ b->width = 0;
+ space_after = 0;
+ continue;
+ }
+
+ if (b->width == UNKNOWN_WIDTH) {
+ /** \todo handle errors */
+
+ /* If it's a select element, we must use the
+ * width of the widest option text */
+ if (b->parent->parent->gadget &&
+ b->parent->parent->gadget->type
+ == GADGET_SELECT) {
+ int opt_maxwidth = 0;
+ struct form_option *o;
+
+ for (o = b->parent->parent->gadget->
+ data.select.items; o;
+ o = o->next) {
+ int opt_width;
+ font_func->width(&fstyle,
+ o->text,
+ strlen(o->text),
+ &opt_width);
+
+ if (opt_maxwidth < opt_width)
+ opt_maxwidth =opt_width;
+ }
+ b->width = opt_maxwidth;
+ if (nsoption_bool(core_select_menu))
+ b->width += SCROLLBAR_WIDTH;
+ } else {
+ font_func->width(&fstyle, b->text,
+ b->length, &b->width);
+ b->flags |= MEASURED;
+ }
+ }
+
+ /* If the current text has not been measured (i.e. its
+ * width was estimated after splitting), and it fits on
+ * the line, measure it properly, so next box is placed
+ * correctly. */
+ if (b->text && (x + b->width < x1 - x0) &&
+ !(b->flags & MEASURED) &&
+ b->next) {
+ font_func->width(&fstyle, b->text,
+ b->length, &b->width);
+ b->flags |= MEASURED;
+ }
+
+ x += b->width;
+ if (b->space == UNKNOWN_WIDTH) {
+ font_func->width(&fstyle, " ", 1, &b->space);
+ /** \todo handle errors */
+ }
+ space_after = b->space;
+ continue;
+ }
+
+ space_after = 0;
+
+ /* inline replaced, 10.3.2 and 10.6.2 */
+ assert(b->style);
+
+ layout_find_dimensions(*width, -1, b, b->style,
+ &b->width, &b->height, &max_width, &min_width,
+ &max_height, &min_height, NULL, NULL, NULL);
+
+ if (b->object && !(b->flags & REPLACE_DIM)) {
+ layout_get_object_dimensions(b, &b->width, &b->height,
+ min_width, max_width,
+ min_height, max_height);
+ } else if (b->flags & IFRAME) {
+ /* TODO: should we look at the content dimensions? */
+ if (b->width == AUTO)
+ b->width = 400;
+ if (b->height == AUTO)
+ b->height = 300;
+
+ /* We reformat the iframe browser window to new
+ * dimensions in pass 2 */
+ } else {
+ /* form control with no object */
+ if (b->width == AUTO)
+ b->width = FIXTOINT(nscss_len2px(INTTOFIX(1),
+ CSS_UNIT_EM, b->style));
+ if (b->height == AUTO)
+ b->height = FIXTOINT(nscss_len2px(INTTOFIX(1),
+ CSS_UNIT_EM, b->style));
+ }
+
+ /* Reformat object to new box size */
+ if (b->object && content_get_type(b->object) == CONTENT_HTML &&
+ b->width !=
+ content_get_available_width(b->object)) {
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
+ enum css_height_e htype = css_computed_height(b->style,
+ &value, &unit);
+
+ content_reformat(b->object, false, b->width, b->height);
+
+ if (htype == CSS_HEIGHT_AUTO)
+ b->height = content_get_height(b->object);
+ }
+
+ if (height < b->height)
+ height = b->height;
+
+ x += b->width;
+ }
+
+ /* find new sides using this height */
+ x0 = cx;
+ x1 = cx + *width;
+ find_sides(cont->float_children, cy, cy + height, &x0, &x1,
+ &left, &right);
+ x0 -= cx;
+ x1 -= cx;
+
+ if (indent)
+ x0 += layout_text_indent(first->parent->parent->style, *width);
+
+ if (x1 < x0)
+ x1 = x0;
+
+ space_after = space_before = 0;
+
+ /* pass 2: place boxes in line: loop body executed at least once */
+#ifdef LAYOUT_DEBUG
+ LOG("x0 %i, x1 %i, x1 - x0 %i", x0, x1, x1 - x0);
+#endif
+
+ for (x = x_previous = 0, b = first; x <= x1 - x0 && b; b = b->next) {
+#ifdef LAYOUT_DEBUG
+ LOG("pass 2: b %p, x %i", b, x);
+#endif
+
+ if (b->type == BOX_INLINE_BLOCK &&
+ (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 ||
+ b->type == BOX_INLINE_BLOCK ||
+ b->type == BOX_TEXT ||
+ b->type == BOX_INLINE_END) {
+ assert(b->width != UNKNOWN_WIDTH);
+
+ x_previous = x;
+ x += space_after;
+ b->x = x;
+
+ if ((b->type == BOX_INLINE && !b->inline_end) ||
+ b->type == BOX_INLINE_BLOCK) {
+ b->x += b->margin[LEFT] + b->border[LEFT].width;
+ x = b->x + b->padding[LEFT] + b->width +
+ b->padding[RIGHT] +
+ b->border[RIGHT].width +
+ b->margin[RIGHT];
+ } else if (b->type == BOX_INLINE) {
+ b->x += b->margin[LEFT] + b->border[LEFT].width;
+ x = b->x + b->padding[LEFT] + b->width;
+ } else if (b->type == BOX_INLINE_END) {
+ b->height = b->inline_end->height;
+ x += b->padding[RIGHT] +
+ b->border[RIGHT].width +
+ b->margin[RIGHT];
+ } else {
+ x += b->width;
+ }
+
+ space_before = space_after;
+ if (b->object || b->flags & REPLACE_DIM ||
+ b->flags & IFRAME)
+ space_after = 0;
+ else if (b->text || b->type == BOX_INLINE_END) {
+ if (b->space == UNKNOWN_WIDTH) {
+ font_plot_style_from_css(b->style,
+ &fstyle);
+ /** \todo handle errors */
+ font_func->width(&fstyle, " ", 1,
+ &b->space);
+ }
+ space_after = b->space;
+ } else {
+ space_after = 0;
+ }
+ split_box = b;
+ move_y = true;
+ inline_count++;
+ } else if (b->type == BOX_BR) {
+ b->x = x;
+ b->width = 0;
+ br_box = b;
+ b = b->next;
+ split_box = 0;
+ move_y = true;
+ break;
+
+ } else {
+ /* float */
+#ifdef LAYOUT_DEBUG
+ LOG("float %p", b);
+#endif
+
+ d = b->children;
+ d->float_children = 0;
+ d->cached_place_below_level = 0;
+ b->float_container = d->float_container = cont;
+
+ if (!layout_float(d, *width, content))
+ return false;
+
+#ifdef LAYOUT_DEBUG
+ LOG("%p : %d %d", d, d->margin[TOP], d->border[TOP].width);
+#endif
+
+ 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].width +
+ d->margin[RIGHT];
+ b->height = d->margin[TOP] + d->border[TOP].width +
+ d->padding[TOP] + d->height +
+ d->padding[BOTTOM] +
+ d->border[BOTTOM].width +
+ d->margin[BOTTOM];
+
+ if (b->width > (x1 - x0) - x)
+ place_below = true;
+ 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 &&
+ cy >= cont->cached_place_below_level) {
+ /* + not cleared or,
+ * cleared and there are no floats to clear
+ * + fits without needing to be placed below or,
+ * this line is empty with no floats
+ * + current y, cy, is below the clear level
+ *
+ * Float affects current line */
+ if (b->type == BOX_FLOAT_LEFT) {
+ b->x = cx + x0;
+ if (b->width > 0)
+ x0 += b->width;
+ left = b;
+ } else {
+ b->x = cx + x1 - b->width;
+ if (b->width > 0)
+ x1 -= b->width;
+ right = b;
+ }
+ b->y = cy;
+ } else {
+ /* cleared or doesn't fit on line */
+ /* place below into next available space */
+ int fcy = (cy > cont->clear_level) ? cy :
+ cont->clear_level;
+ fcy = (fcy > cont->cached_place_below_level) ?
+ fcy :
+ cont->cached_place_below_level;
+ fy = (fy > fcy) ? fy : fcy;
+ fy = (fy == cy) ? fy + height : fy;
+
+ place_float_below(b, *width, cx, fy, cont);
+ fy = b->y;
+ if (d->style && (
+ (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)))) {
+ /* to be cleared below existing
+ * floats */
+ if (b->type == BOX_FLOAT_LEFT)
+ b->x = cx;
+ else
+ b->x = cx + *width - b->width;
+
+ fcy = layout_clear(cont->float_children,
+ css_computed_clear(d->style));
+ if (fcy > cont->clear_level)
+ cont->clear_level = fcy;
+ if (b->y < fcy)
+ b->y = fcy;
+ }
+ if (b->type == BOX_FLOAT_LEFT)
+ left = b;
+ else
+ right = b;
+ }
+ add_float_to_container(cont, b);
+
+ split_box = 0;
+ }
+ }
+
+ if (x1 - x0 < x && split_box) {
+ /* the last box went over the end */
+ size_t split = 0;
+ int w;
+ bool no_wrap = css_computed_white_space(
+ split_box->style) == CSS_WHITE_SPACE_NOWRAP ||
+ css_computed_white_space(
+ split_box->style) == CSS_WHITE_SPACE_PRE;
+
+ x = x_previous;
+
+ if (!no_wrap &&
+ (split_box->type == BOX_INLINE ||
+ split_box->type == BOX_TEXT) &&
+ !split_box->object &&
+ !(split_box->flags & REPLACE_DIM) &&
+ !(split_box->flags & IFRAME) &&
+ !split_box->gadget && split_box->text) {
+
+ font_plot_style_from_css(split_box->style, &fstyle);
+ /** \todo handle errors */
+ font_func->split(&fstyle,
+ split_box->text,
+ split_box->length,
+ x1 - x0 - x - space_before,
+ &split,
+ &w);
+ }
+
+ /* split == 0 implies that text can't be split */
+
+ if (split == 0)
+ w = split_box->width;
+
+#ifdef LAYOUT_DEBUG
+ LOG("splitting: split_box %p \"%.*s\", spilt %zu, w %i, ""left %p, right %p, inline_count %u", split_box, (int)split_box->length, split_box->text, split, w, left, right, inline_count);
+#endif
+
+ if ((split == 0 || x1 - x0 <= x + space_before + w) &&
+ !left && !right && inline_count == 1) {
+ /* first word of box doesn't fit, but no floats and
+ * first box on line so force in */
+ if (split == 0 || split == split_box->length) {
+ /* only one word in this box, or not text
+ * or white-space:nowrap */
+ b = split_box->next;
+ } else {
+ /* cut off first word for this line */
+ if (!layout_text_box_split(content, &fstyle,
+ split_box, split, w))
+ return false;
+ b = split_box->next;
+ }
+ x += space_before + w;
+#ifdef LAYOUT_DEBUG
+ LOG("forcing");
+#endif
+ } else if ((split == 0 || x1 - x0 <= x + space_before + w) &&
+ inline_count == 1) {
+ /* first word of first box doesn't fit, but a float is
+ * taking some of the width so move below it */
+ assert(left || right);
+ used_height = 0;
+ if (left) {
+#ifdef LAYOUT_DEBUG
+ LOG("cy %i, left->y %i, left->height %i", cy, left->y, left->height);
+#endif
+ used_height = left->y + left->height - cy + 1;
+#ifdef LAYOUT_DEBUG
+ LOG("used_height %i", used_height);
+#endif
+ }
+ if (right && used_height <
+ right->y + right->height - cy + 1)
+ used_height = right->y + right->height - cy + 1;
+
+ if (used_height < 0)
+ used_height = 0;
+
+ b = split_box;
+#ifdef LAYOUT_DEBUG
+ LOG("moving below float");
+#endif
+ } else if (split == 0 || x1 - x0 <= x + space_before + w) {
+ /* first word of box doesn't fit so leave box for next
+ * line */
+ b = split_box;
+#ifdef LAYOUT_DEBUG
+ LOG("leaving for next line");
+#endif
+ } else {
+ /* fit as many words as possible */
+ assert(split != 0);
+#ifdef LAYOUT_DEBUG
+ LOG("'%.*s' %i %zu %i", (int)split_box->length, split_box->text, x1 - x0, split, w);
+#endif
+ if (split != split_box->length) {
+ if (!layout_text_box_split(content, &fstyle,
+ split_box, split, w))
+ return false;
+ b = split_box->next;
+ }
+ x += space_before + w;
+#ifdef LAYOUT_DEBUG
+ LOG("fitting words");
+#endif
+ }
+ move_y = true;
+ }
+
+ /* set positions */
+ switch (css_computed_text_align(first->parent->parent->style)) {
+ case CSS_TEXT_ALIGN_RIGHT:
+ case CSS_TEXT_ALIGN_LIBCSS_RIGHT:
+ x0 = x1 - x;
+ break;
+ case CSS_TEXT_ALIGN_CENTER:
+ case CSS_TEXT_ALIGN_LIBCSS_CENTER:
+ x0 = (x0 + (x1 - x)) / 2;
+ break;
+ case CSS_TEXT_ALIGN_LEFT:
+ case CSS_TEXT_ALIGN_LIBCSS_LEFT:
+ case CSS_TEXT_ALIGN_JUSTIFY:
+ /* leave on left */
+ break;
+ case CSS_TEXT_ALIGN_DEFAULT:
+ /* None; consider text direction */
+ switch (css_computed_direction(first->parent->parent->style)) {
+ case CSS_DIRECTION_LTR:
+ /* leave on left */
+ break;
+ case CSS_DIRECTION_RTL:
+ x0 = x1 - x;
+ break;
+ }
+ break;
+ }
+
+ for (d = first; d != b; d = d->next) {
+ d->flags &= ~NEW_LINE;
+
+ if (d->type == BOX_INLINE_BLOCK &&
+ (css_computed_position(d->style) ==
+ CSS_POSITION_ABSOLUTE ||
+ css_computed_position(d->style) ==
+ CSS_POSITION_FIXED)) {
+ /* positioned inline-blocks:
+ * set static position (x,y) only, rest of positioning
+ * is handled later */
+ d->x += x0;
+ d->y = *y;
+ continue;
+ } else if ((d->type == BOX_INLINE &&
+ ((d->object || d->gadget) == false) &&
+ !(d->flags & IFRAME) &&
+ !(d->flags & REPLACE_DIM)) ||
+ d->type == BOX_BR ||
+ d->type == BOX_TEXT ||
+ d->type == BOX_INLINE_END) {
+ /* regular (non-replaced) inlines */
+ d->x += x0;
+ d->y = *y - d->padding[TOP];
+
+ if (d->type == BOX_TEXT && d->height > used_height) {
+ /* text */
+ used_height = d->height;
+ }
+ } else if ((d->type == BOX_INLINE) ||
+ d->type == BOX_INLINE_BLOCK) {
+ /* replaced inlines and inline-blocks */
+ d->x += x0;
+ d->y = *y + d->border[TOP].width + d->margin[TOP];
+ 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;
+ }
+ }
+
+ first->flags |= NEW_LINE;
+
+ assert(b != first || (move_y && 0 < used_height && (left || right)));
+
+ /* handle vertical-align by adjusting box y values */
+ /** \todo proper vertical alignment handling */
+ for (d = first; d != b; d = d->next) {
+ if ((d->type == BOX_INLINE && d->inline_end) ||
+ d->type == BOX_BR ||
+ d->type == BOX_TEXT ||
+ d->type == BOX_INLINE_END) {
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
+ switch (css_computed_vertical_align(d->style, &value,
+ &unit)) {
+ case CSS_VERTICAL_ALIGN_SUPER:
+ case CSS_VERTICAL_ALIGN_TOP:
+ case CSS_VERTICAL_ALIGN_TEXT_TOP:
+ /* already at top */
+ break;
+ case CSS_VERTICAL_ALIGN_SUB:
+ case CSS_VERTICAL_ALIGN_BOTTOM:
+ case CSS_VERTICAL_ALIGN_TEXT_BOTTOM:
+ d->y += used_height - d->height;
+ break;
+ default:
+ case CSS_VERTICAL_ALIGN_BASELINE:
+ d->y += 0.75 * (used_height - d->height);
+ break;
+ }
+ }
+ }
+
+ /* handle clearance for br */
+ if (br_box && css_computed_clear(br_box->style) != CSS_CLEAR_NONE) {
+ int clear_y = layout_clear(cont->float_children,
+ css_computed_clear(br_box->style));
+ if (used_height < clear_y - cy)
+ used_height = clear_y - cy;
+ }
+
+ if (move_y)
+ *y += used_height;
+ *next_box = b;
+ *width = x; /* return actual width */
+ return true;
+}
+
+
+/* exported function documented in render/layout.h */
+bool layout_inline_container(struct box *inline_container, int width,
+ struct box *cont, int cx, int cy, html_content *content)
+{
+ bool first_line = true;
+ bool has_text_children;
+ struct box *c, *next;
+ int y = 0;
+ int curwidth,maxwidth = width;
+
+ assert(inline_container->type == BOX_INLINE_CONTAINER);
+
+#ifdef LAYOUT_DEBUG
+ LOG("inline_container %p, width %i, cont %p, cx %i, cy %i", inline_container, width, cont, cx, cy);
+#endif
+
+ has_text_children = false;
+ for (c = inline_container->children; c; c = c->next) {
+ bool is_pre = false;
+
+ if (c->style) {
+ enum css_white_space_e 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->flags & REPLACE_DIM) &&
+ !(c->flags & IFRAME) &&
+ c->text && (c->length || is_pre)) ||
+ c->type == BOX_BR)
+ has_text_children = true;
+ }
+
+ /** \todo fix wrapping so that a box with horizontal scrollbar will
+ * shrink back to 'width' if no word is wider than 'width' (Or just set
+ * curwidth = width and have the multiword lines wrap to the min width)
+ */
+ for (c = inline_container->children; c; ) {
+#ifdef LAYOUT_DEBUG
+ LOG("c %p", c);
+#endif
+ curwidth = inline_container->width;
+ if (!layout_line(c, &curwidth, &y, cx, cy + y, cont, first_line,
+ has_text_children, content, &next))
+ return false;
+ maxwidth = max(maxwidth,curwidth);
+ c = next;
+ first_line = false;
+ }
+
+ inline_container->width = maxwidth;
+ inline_container->height = y;
+
+ return true;
+}
+
+
+/* exported function documented in render/layout.h */
+void
+layout_minmax_table(struct box *table, const struct gui_layout_table *font_func)
+{
+ unsigned int i, j;
+ int border_spacing_h = 0;
+ int table_min = 0, table_max = 0;
+ int extra_fixed = 0;
+ float extra_frac = 0;
+ struct column *col = table->col;
+ struct box *row_group, *row, *cell;
+ enum css_width_e 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)
+ return;
+
+ /* start with 0 except for fixed-width columns */
+ for (i = 0; i != table->columns; i++) {
+ if (col[i].type == COLUMN_WIDTH_FIXED)
+ col[i].min = col[i].max = col[i].width;
+ else
+ col[i].min = col[i].max = 0;
+ }
+
+ /* border-spacing is used in the separated borders model */
+ 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)
+ for (row = row_group->children; row; row = row->next)
+ for (cell = row->children; cell; cell = cell->next) {
+ assert(cell->type == BOX_TABLE_CELL);
+ assert(cell->style);
+ /** TODO: Handle colspan="0" correctly.
+ * It's currently converted to 1 in box normaisation */
+ assert(cell->columns != 0);
+
+ if (cell->columns != 1)
+ continue;
+
+ layout_minmax_block(cell, font_func);
+ i = cell->start_column;
+
+ if (col[i].positioned)
+ continue;
+
+ /* update column min, max widths using cell widths */
+ if (col[i].min < cell->min_width)
+ col[i].min = cell->min_width;
+ if (col[i].max < cell->max_width)
+ col[i].max = cell->max_width;
+ }
+
+ /* 2nd pass: cells which span multiple columns */
+ 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) {
+ unsigned int flexible_columns = 0;
+ int min = 0, max = 0, fixed_width = 0, extra;
+
+ if (cell->columns == 1)
+ continue;
+
+ layout_minmax_block(cell, font_func);
+ i = cell->start_column;
+
+ /* find min width so far of spanned columns, and count
+ * number of non-fixed spanned columns and total fixed width */
+ for (j = 0; j != cell->columns; j++) {
+ min += col[i + j].min;
+ if (col[i + j].type == COLUMN_WIDTH_FIXED)
+ fixed_width += col[i + j].width;
+ else
+ flexible_columns++;
+ }
+ min += (cell->columns - 1) * border_spacing_h;
+
+ /* distribute extra min to spanned columns */
+ if (min < cell->min_width) {
+ if (flexible_columns == 0) {
+ extra = 1 + (cell->min_width - min) /
+ cell->columns;
+ for (j = 0; j != cell->columns; j++) {
+ col[i + j].min += extra;
+ if (col[i + j].max < col[i + j].min)
+ col[i + j].max = col[i + j].min;
+ }
+ } else {
+ extra = 1 + (cell->min_width - min) /
+ flexible_columns;
+ for (j = 0; j != cell->columns; j++) {
+ if (col[i + j].type !=
+ COLUMN_WIDTH_FIXED) {
+ col[i + j].min += extra;
+ if (col[i + j].max <
+ col[i + j].min)
+ col[i + j].max =
+ col[i + j].min;
+ }
+ }
+ }
+ }
+
+ /* find max width so far of spanned columns */
+ for (j = 0; j != cell->columns; j++)
+ max += col[i + j].max;
+ max += (cell->columns - 1) * border_spacing_h;
+
+ /* distribute extra max to spanned columns */
+ if (max < cell->max_width && flexible_columns) {
+ extra = 1 + (cell->max_width - max) / flexible_columns;
+ for (j = 0; j != cell->columns; j++)
+ if (col[i + j].type != COLUMN_WIDTH_FIXED)
+ col[i + j].max += extra;
+ }
+ }
+
+ for (i = 0; i != table->columns; i++) {
+ if (col[i].max < col[i].min) {
+ box_dump(stderr, table, 0, true);
+ assert(0);
+ }
+ table_min += col[i].min;
+ table_max += col[i].max;
+ }
+
+ /* fixed width takes priority, unless it is too narrow */
+ 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)
+ table_max = width;
+ }
+
+ /* add margins, border, padding to min, max widths */
+ calculate_mbp_width(table->style, LEFT, true, true, true,
+ &extra_fixed, &extra_frac);
+ calculate_mbp_width(table->style, RIGHT, true, true, true,
+ &extra_fixed, &extra_frac);
+ if (extra_fixed < 0)
+ extra_fixed = 0;
+ if (extra_frac < 0)
+ extra_frac = 0;
+ if (1.0 <= extra_frac)
+ extra_frac = 0.9;
+ table->min_width = (table_min + extra_fixed) / (1.0 - extra_frac);
+ table->max_width = (table_max + extra_fixed) / (1.0 - extra_frac);
+ table->min_width += (table->columns + 1) * border_spacing_h;
+ table->max_width += (table->columns + 1) * border_spacing_h;
+
+ assert(0 <= table->min_width && table->min_width <= table->max_width);
}
@@ -5069,9 +5021,10 @@ void layout_compute_offsets(struct box *box,
* \param desc_x1 updated to right of box's bbox
* \param desc_y1 updated to bottom of box's bbox
*/
-
-static void layout_get_box_bbox(struct box *box, int *desc_x0, int *desc_y0,
- int *desc_x1, int *desc_y1)
+static void
+layout_get_box_bbox(struct box *box,
+ int *desc_x0, int *desc_y0,
+ int *desc_x1, int *desc_y1)
{
*desc_x0 = -box->border[LEFT].width;
*desc_y0 = -box->border[TOP].width;
@@ -5106,9 +5059,11 @@ static void layout_get_box_bbox(struct box *box, int *desc_x0, int *desc_y0,
* \param off_x offset to apply to child->x coord to treat as child of box
* \param off_y offset to apply to child->y coord to treat as child of box
*/
-
-static void layout_update_descendant_bbox(struct box *box, struct box *child,
- int off_x, int off_y)
+static void
+layout_update_descendant_bbox(struct box *box,
+ struct box *child,
+ int off_x,
+ int off_y)
{
int child_desc_x0, child_desc_y0, child_desc_x1, child_desc_y1;
@@ -5161,13 +5116,7 @@ static void layout_update_descendant_bbox(struct box *box, struct box *child,
}
-/**
- * Recursively calculate the descendant_[xy][01] values for a laid-out box tree
- * and inform iframe browser windows of their size and position.
- *
- * \param box tree of boxes to update
- */
-
+/* exported function documented in render/layout.h */
void layout_calculate_descendant_bboxes(struct box *box)
{
struct box *child;
@@ -5255,4 +5204,3 @@ void layout_calculate_descendant_bboxes(struct box *box)
layout_update_descendant_bbox(box, child, 0, 0);
}
}
-
diff --git a/render/layout.h b/render/layout.h
index f4117e286..ff0da5a53 100644
--- a/render/layout.h
+++ b/render/layout.h
@@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/** \file
+/**
+ * \file
* HTML layout (interface).
*
* The main interface to the layout code is layout_document(), which takes a
@@ -29,11 +30,47 @@
struct box;
struct html_content;
+struct gui_layout_table;
+/**
+ * Calculate positions of boxes in a document.
+ *
+ * \param content content of type CONTENT_HTML
+ * \param width available width
+ * \param height available height
+ * \return true on success, false on memory exhaustion
+ */
bool layout_document(struct html_content *content, int width, int height);
-bool layout_inline_container(struct box *box, int width,
- struct box *cont, int cx, int cy, struct html_content *content);
+
+/**
+ * Layout lines of text or inline boxes with floats.
+ *
+ * \param inline_container inline container box
+ * \param width horizontal space available
+ * \param cont ancestor box which defines horizontal space, for floats
+ * \param cx box position relative to cont
+ * \param cy box position relative to cont
+ * \param content memory pool for any new boxes
+ * \return true on success, false on memory exhaustion
+ */
+bool layout_inline_container(struct box *box, int width, struct box *cont, int cx, int cy, struct html_content *content);
+
+/**
+ * Recursively calculate the descendant_[xy][01] values for a laid-out box tree
+ * and inform iframe browser windows of their size and position.
+ *
+ * \param box tree of boxes to update
+ */
void layout_calculate_descendant_bboxes(struct box *box);
-void layout_minmax_table(struct box *table,
- const struct font_functions *font_func);
+
+/**
+ * Calculate minimum and maximum width of a table.
+ *
+ * \param table box of type TABLE
+ * \param font_func Font functions
+ * \post table->min_width and table->max_width filled in,
+ * 0 <= table->min_width <= table->max_width
+ */
+void layout_minmax_table(struct box *table, const struct gui_layout_table *font_func);
+
#endif
diff --git a/render/textplain.c b/render/textplain.c
index c9f83f9ac..e999c352e 100644
--- a/render/textplain.c
+++ b/render/textplain.c
@@ -47,7 +47,8 @@
#include "desktop/search.h"
#include "desktop/selection.h"
#include "desktop/textinput.h"
-#include "desktop/font.h"
+#include "desktop/gui_layout.h"
+#include "desktop/gui_internal.h"
#include "render/search.h"
#include "render/textplain.h"
@@ -466,13 +467,20 @@ void textplain_reformat(struct content *c, int width, int height)
size_t columns = 80;
int character_width;
size_t line_start;
+ nserror res;
LOG("content %p w:%d h:%d", c, width, height);
/* compute available columns (assuming monospaced font) - use 8
- * characters for better accuracy */
- if (!nsfont.font_width(&textplain_style, "ABCDEFGH", 8, &character_width))
+ * characters for better accuracy
+ */
+ res = guit->layout->width(&textplain_style,
+ "ABCDEFGH", 8,
+ &character_width);
+ if (res != NSERROR_OK) {
return;
+ }
+
columns = (width - MARGIN - MARGIN) * 8 / character_width;
textplain_tab_width = (TAB_WIDTH * character_width) / 8;
@@ -933,9 +941,12 @@ bool textplain_redraw(struct content *c, struct content_redraw_data *data,
break;
/* locate end of string and align to next tab position */
- if (nsfont.font_width(&textplain_style, &text_d[offset],
- next_offset - offset, &width))
+ if (guit->layout->width(&textplain_style,
+ &text_d[offset],
+ next_offset - offset,
+ &width)) {
tx += (int)(width * data->scale);
+ }
ntx = x + ((1 + (tx - x) / tab_width) * tab_width);
@@ -1105,16 +1116,20 @@ size_t textplain_offset_from_coords(struct content *c, int x, int y, int dir)
while (next_offset < length && text[next_offset] != '\t')
next_offset = utf8_next(text, length, next_offset);
- if (next_offset < length)
- nsfont.font_width(&textplain_style, text, next_offset, &width);
+ if (next_offset < length) {
+ guit->layout->width(&textplain_style,
+ text,
+ next_offset,
+ &width);
+ }
if (x <= width) {
int pixel_offset;
size_t char_offset;
- nsfont.font_position_in_string(&textplain_style,
- text, next_offset, x,
- &char_offset, &pixel_offset);
+ guit->layout->position(&textplain_style,
+ text, next_offset, x,
+ &char_offset, &pixel_offset);
idx += char_offset;
break;
@@ -1192,10 +1207,12 @@ int textplain_coord_from_offset(const char *text, size_t offset, size_t length)
size_t next_offset = 0;
int tx;
- while (next_offset < offset && text[next_offset] != '\t')
+ while (next_offset < offset && text[next_offset] != '\t') {
next_offset = utf8_next(text, length, next_offset);
+ }
+
+ guit->layout->width(&textplain_style, text, next_offset, &tx);
- nsfont.font_width(&textplain_style, text, next_offset, &tx);
x += tx;
if (next_offset >= offset)