Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2005 Richard Wilson <info@tinct.net>
  3.  * Copyright 2006 James Bursa <bursa@users.sourceforge.net>
  4.  * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
  5.  * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
  6.  *
  7.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  8.  *
  9.  * NetSurf is free software; you can redistribute it and/or modify
  10.  * it under the terms of the GNU General Public License as published by
  11.  * the Free Software Foundation; version 2 of the License.
  12.  *
  13.  * NetSurf is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  20.  */
  21.  
  22. /** \file
  23.  * HTML layout (implementation).
  24.  *
  25.  * Layout is carried out in two stages:
  26.  *
  27.  * 1. + calculation of minimum / maximum box widths, and
  28.  *    + determination of whether block level boxes will have >zero height
  29.  *
  30.  * 2. + layout (position and dimensions)
  31.  *
  32.  * In most cases the functions for the two stages are a corresponding pair
  33.  * layout_minmax_X() and layout_X().
  34.  */
  35.  
  36. #include <assert.h>
  37. #include <limits.h>
  38. #include <stdbool.h>
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <math.h>
  43. #include <dom/dom.h>
  44. #include "css/css.h"
  45. #include "css/utils.h"
  46. #include "content/content_protected.h"
  47. #include "desktop/options.h"
  48. #include "desktop/scrollbar.h"
  49. #include "render/box.h"
  50. #include "render/font.h"
  51. #include "render/form.h"
  52. #include "render/html_internal.h"
  53. #include "render/layout.h"
  54. #include "render/table.h"
  55. #include "utils/log.h"
  56. #include "utils/talloc.h"
  57. #include "utils/utils.h"
  58.  
  59.  
  60. /* Define to enable layout debugging */
  61. #undef LAYOUT_DEBUG
  62.  
  63. #define AUTO INT_MIN
  64.  
  65. /* Fixed point value percentage of an integer, to an integer */
  66. #define FPCT_OF_INT_TOINT(a, b) FIXTOINT(FMUL(FDIV(a, F_100), INTTOFIX(b)))
  67.  
  68.  
  69. static bool layout_block_context(struct box *block, int viewport_height,
  70.                 html_content *content);
  71. static void layout_minmax_block(struct box *block,
  72.                 const struct font_functions *font_func);
  73. static struct box* layout_next_margin_block(struct box *box, struct box *block,
  74.                 int viewport_height, int *max_pos_margin, int *max_neg_margin);
  75. static bool layout_block_object(struct box *block);
  76. static void layout_get_object_dimensions(struct box *box,
  77.                 int *width, int *height, int min_width, int max_width);
  78. static void layout_block_find_dimensions(int available_width,
  79.                 int viewport_height, int lm, int rm,
  80.                 struct box *box);
  81. static bool layout_apply_minmax_height(struct box *box, struct box *container);
  82. static void layout_block_add_scrollbar(struct box *box, int which);
  83. static int layout_solve_width(struct box *box, int available_width, int width,
  84.                 int lm, int rm, int max_width, int min_width);
  85. static void layout_float_find_dimensions(int available_width,
  86.                 const css_computed_style *style, struct box *box);
  87. static void layout_find_dimensions(int available_width, int viewport_height,
  88.                 struct box *box, const css_computed_style *style,
  89.                 int *width, int *height, int *max_width, int *min_width,
  90.                 int margin[4], int padding[4], struct box_border border[4]);
  91. static void layout_tweak_form_dimensions(struct box *box, bool percentage,
  92.                 int available_width, bool setwidth, int *dimension);
  93. static int layout_clear(struct box *fl, enum css_clear_e clear);
  94. static void find_sides(struct box *fl, int y0, int y1,
  95.                 int *x0, int *x1, struct box **left, struct box **right);
  96. static void layout_minmax_inline_container(struct box *inline_container,
  97.                 bool *has_height, const struct font_functions *font_func);
  98. static int line_height(const css_computed_style *style);
  99. static bool layout_line(struct box *first, int *width, int *y,
  100.                 int cx, int cy, struct box *cont, bool indent,
  101.                 bool has_text_children,
  102.                 html_content *content, struct box **next_box);
  103. static struct box *layout_minmax_line(struct box *first, int *min, int *max,
  104.                 bool first_line, bool *line_has_height,
  105.                 const struct font_functions *font_func);
  106. static int layout_text_indent(const css_computed_style *style, int width);
  107. static bool layout_float(struct box *b, int width, html_content *content);
  108. static void place_float_below(struct box *c, int width, int cx, int y,
  109.                 struct box *cont);
  110. static bool layout_table(struct box *box, int available_width,
  111.                 html_content *content);
  112. static void layout_move_children(struct box *box, int x, int y);
  113. static void calculate_mbp_width(const css_computed_style *style,
  114.                 unsigned int side, bool margin, bool border, bool padding,
  115.                 int *fixed, float *frac);
  116. static void layout_lists(struct box *box,
  117.                 const struct font_functions *font_func);
  118. static void layout_position_relative(struct box *root, struct box *fp,
  119.                 int fx, int fy);
  120. static void layout_compute_relative_offset(struct box *box, int *x, int *y);
  121. static bool layout_position_absolute(struct box *box,
  122.                 struct box *containing_block,
  123.                 int cx, int cy,
  124.                 html_content *content);
  125. static bool layout_absolute(struct box *box, struct box *containing_block,
  126.                 int cx, int cy,
  127.                 html_content *content);
  128. static void layout_compute_offsets(struct box *box,
  129.                 struct box *containing_block,
  130.                 int *top, int *right, int *bottom, int *left);
  131.  
  132.  
  133. /**
  134.  * Calculate positions of boxes in a document.
  135.  *
  136.  * \param  doc       content of type CONTENT_HTML
  137.  * \param  width     available width
  138.  * \param  height    available height
  139.  * \return  true on success, false on memory exhaustion
  140.  */
  141.  
  142. bool layout_document(html_content *content, int width, int height)
  143. {
  144.         bool ret;
  145.         struct box *doc = content->layout;
  146.         const struct font_functions *font_func = content->font_func;
  147.  
  148.         layout_minmax_block(doc, font_func);
  149.  
  150.         layout_block_find_dimensions(width, height, 0, 0, doc);
  151.         doc->x = doc->margin[LEFT] + doc->border[LEFT].width;
  152.         doc->y = doc->margin[TOP] + doc->border[TOP].width;
  153.         width -= doc->margin[LEFT] + doc->border[LEFT].width +
  154.                         doc->padding[LEFT] + doc->padding[RIGHT] +
  155.                         doc->border[RIGHT].width + doc->margin[RIGHT];
  156.         if (width < 0)
  157.                 width = 0;
  158.         doc->width = width;
  159.  
  160.         ret = layout_block_context(doc, height, content);
  161.  
  162.         /* make <html> and <body> fill available height */
  163.         if (doc->y + doc->padding[TOP] + doc->height + doc->padding[BOTTOM] +
  164.                         doc->border[BOTTOM].width + doc->margin[BOTTOM] <
  165.                         height) {
  166.                 doc->height = height - (doc->y + doc->padding[TOP] +
  167.                                 doc->padding[BOTTOM] +
  168.                                 doc->border[BOTTOM].width +
  169.                                 doc->margin[BOTTOM]);
  170.                 if (doc->children)
  171.                         doc->children->height = doc->height -
  172.                                         (doc->children->margin[TOP] +
  173.                                          doc->children->border[TOP].width +
  174.                                          doc->children->padding[TOP] +
  175.                                          doc->children->padding[BOTTOM] +
  176.                                          doc->children->border[BOTTOM].width +
  177.                                          doc->children->margin[BOTTOM]);
  178.         }
  179.  
  180.         layout_lists(doc, font_func);
  181.         layout_position_absolute(doc, doc, 0, 0, content);
  182.         layout_position_relative(doc, doc, 0, 0);
  183.  
  184.         layout_calculate_descendant_bboxes(doc);
  185.  
  186.         return ret;
  187. }
  188.  
  189.  
  190. /**
  191.  * Layout a block formatting context.
  192.  *
  193.  * \param  block            BLOCK, INLINE_BLOCK, or TABLE_CELL to layout
  194.  * \param  viewport_height  Height of viewport in pixels or -ve if unknown
  195.  * \param  content          Memory pool for any new boxes
  196.  * \return  true on success, false on memory exhaustion
  197.  *
  198.  * This function carries out layout of a block and its children, as described
  199.  * in CSS 2.1 9.4.1.
  200.  */
  201.  
  202. bool layout_block_context(struct box *block, int viewport_height,
  203.                 html_content *content)
  204. {
  205.         struct box *box;
  206.         int cx, cy;  /**< current coordinates */
  207.         int max_pos_margin = 0;
  208.         int max_neg_margin = 0;
  209.         int y = 0;
  210.         int lm, rm;
  211.         struct box *margin_collapse = NULL;
  212.         bool in_margin = false;
  213.         css_fixed gadget_size;
  214.         css_unit gadget_unit; /* Checkbox / radio buttons */
  215.  
  216.         assert(block->type == BOX_BLOCK ||
  217.                         block->type == BOX_INLINE_BLOCK ||
  218.                         block->type == BOX_TABLE_CELL);
  219.         assert(block->width != UNKNOWN_WIDTH);
  220.         assert(block->width != AUTO);
  221.  
  222.         block->float_children = NULL;
  223.         block->clear_level = 0;
  224.  
  225.         /* special case if the block contains an object */
  226.         if (block->object) {
  227.                 int temp_width = block->width;
  228.                 if (!layout_block_object(block))
  229.                         return false;
  230.                 layout_get_object_dimensions(block, &temp_width,
  231.                                 &block->height, INT_MIN, INT_MAX);
  232.                 return true;
  233.         } else if (block->flags & REPLACE_DIM) {
  234.                 return true;
  235.         }
  236.  
  237.         /* special case if the block contains an radio button or checkbox */
  238.         if (block->gadget && (block->gadget->type == GADGET_RADIO ||
  239.                         block->gadget->type == GADGET_CHECKBOX)) {
  240.                 /* form checkbox or radio button
  241.                  * if width or height is AUTO, set it to 1em */
  242.                 gadget_unit = CSS_UNIT_EM;
  243.                 gadget_size = INTTOFIX(1);
  244.                 if (block->height == AUTO)
  245.                         block->height = FIXTOINT(nscss_len2px(gadget_size,
  246.                                         gadget_unit, block->style));
  247.         }
  248.  
  249.         box = block->children;
  250.         /* set current coordinates to top-left of the block */
  251.         cx = 0;
  252.         y = cy = block->padding[TOP];
  253.         if (box)
  254.                 box->y = block->padding[TOP];
  255.  
  256.         /* Step through the descendants of the block in depth-first order, but
  257.          * not into the children of boxes which aren't blocks. For example, if
  258.          * the tree passed to this function looks like this (box->type shown):
  259.          *
  260.          *  block -> BOX_BLOCK
  261.          *             BOX_BLOCK * (1)
  262.          *               BOX_INLINE_CONTAINER * (2)
  263.          *                 BOX_INLINE
  264.          *                 BOX_TEXT
  265.          *                 ...
  266.          *             BOX_BLOCK * (3)
  267.          *               BOX_TABLE * (4)
  268.          *                 BOX_TABLE_ROW
  269.          *                   BOX_TABLE_CELL
  270.          *                     ...
  271.          *                   BOX_TABLE_CELL
  272.          *                     ...
  273.          *               BOX_BLOCK * (5)
  274.          *                 BOX_INLINE_CONTAINER * (6)
  275.          *                   BOX_TEXT
  276.          *                   ...
  277.          * then the while loop will visit each box marked with *, setting box
  278.          * to each in the order shown. */
  279.         while (box) {
  280.                 assert(box->type == BOX_BLOCK || box->type == BOX_TABLE ||
  281.                                 box->type == BOX_INLINE_CONTAINER);
  282.  
  283.                 /* Tables are laid out before being positioned, because the
  284.                  * position depends on the width which is calculated in
  285.                  * table layout. Blocks and inline containers are positioned
  286.                  * before being laid out, because width is not dependent on
  287.                  * content, and the position is required during layout for
  288.                  * correct handling of floats.
  289.                  */
  290.  
  291.                 if (box->style &&
  292.                                 (css_computed_position(box->style) ==
  293.                                         CSS_POSITION_ABSOLUTE ||
  294.                                  css_computed_position(box->style) ==
  295.                                         CSS_POSITION_FIXED)) {
  296.                         box->x = box->parent->padding[LEFT];
  297.                         /* absolute positioned; this element will establish
  298.                          * its own block context when it gets laid out later,
  299.                          * so no need to look at its children now. */
  300.                         goto advance_to_next_box;
  301.                 }
  302.  
  303.                 /* If we don't know which box the current margin collapses
  304.                  * through to, find out.  Update the pos/neg margin values. */
  305.                 if (margin_collapse == NULL) {
  306.                         margin_collapse = layout_next_margin_block(box, block,
  307.                                         viewport_height,
  308.                                         &max_pos_margin, &max_neg_margin);
  309.                         /* We have a margin that has not yet been applied. */
  310.                         in_margin = true;
  311.                 }
  312.  
  313.                 /* Clearance. */
  314.                 y = 0;
  315.                 if (box->style && css_computed_clear(box->style) !=
  316.                                 CSS_CLEAR_NONE)
  317.                         y = layout_clear(block->float_children,
  318.                                         css_computed_clear(box->style));
  319.  
  320.                 /* Blocks establishing a block formatting context get minimum
  321.                  * left and right margins to avoid any floats. */
  322.                 lm = rm = 0;
  323.  
  324.                 if (box->type == BOX_BLOCK || box->flags & IFRAME) {
  325.                         if (!box->object && !(box->flags & IFRAME) &&
  326.                                         !(box->flags & REPLACE_DIM) &&
  327.                                         box->style &&
  328.                                         css_computed_overflow(box->style) !=
  329.                                         CSS_OVERFLOW_VISIBLE) {
  330.                                 /* box establishes new block formatting context
  331.                                  * so available width may be diminished due to
  332.                                  * floats. */
  333.                                 int x0, x1, top;
  334.                                 struct box *left, *right;
  335.                                 top = cy + max_pos_margin - max_neg_margin;
  336.                                 top = (top > y) ? top : y;
  337.                                 x0 = cx;
  338.                                 x1 = cx + box->parent->width -
  339.                                                 box->parent->padding[LEFT] -
  340.                                                 box->parent->padding[RIGHT];
  341.                                 find_sides(block->float_children, top, top,
  342.                                                 &x0, &x1, &left, &right);
  343.                                 /* calculate min required left & right margins
  344.                                  * needed to avoid floats */
  345.                                 lm = x0 - cx;
  346.                                 rm = cx + box->parent->width -
  347.                                                 box->parent->padding[LEFT] -
  348.                                                 box->parent->padding[RIGHT] -
  349.                                                 x1;
  350.                         }
  351.                         layout_block_find_dimensions(box->parent->width,
  352.                                         viewport_height, lm, rm, box);
  353.                         if (box->type == BOX_BLOCK && !(box->flags & IFRAME)) {
  354.                                 layout_block_add_scrollbar(box, RIGHT);
  355.                                 layout_block_add_scrollbar(box, BOTTOM);
  356.                         }
  357.                 } else if (box->type == BOX_TABLE) {
  358.                         if (box->style != NULL) {
  359.                                 enum css_width_e wtype;
  360.                                 css_fixed width = 0;
  361.                                 css_unit unit = CSS_UNIT_PX;
  362.  
  363.                                 wtype = css_computed_width(box->style, &width,
  364.                                                 &unit);
  365.  
  366.                                 if (wtype == CSS_WIDTH_AUTO) {
  367.                                         /* max available width may be
  368.                                          * diminished due to floats. */
  369.                                         int x0, x1, top;
  370.                                         struct box *left, *right;
  371.                                         top = cy + max_pos_margin -
  372.                                                         max_neg_margin;
  373.                                         top = (top > y) ? top : y;
  374.                                         x0 = cx;
  375.                                         x1 = cx + box->parent->width -
  376.                                                 box->parent->padding[LEFT] -
  377.                                                 box->parent->padding[RIGHT];
  378.                                         find_sides(block->float_children,
  379.                                                 top, top, &x0, &x1,
  380.                                                 &left, &right);
  381.                                         /* calculate min required left & right
  382.                                          * margins needed to avoid floats */
  383.                                         lm = x0 - cx;
  384.                                         rm = cx + box->parent->width -
  385.                                                 box->parent->padding[LEFT] -
  386.                                                 box->parent->padding[RIGHT] -
  387.                                                 x1;
  388.                                 }
  389.                         }
  390.                         if (!layout_table(box, box->parent->width - lm - rm,
  391.                                         content))
  392.                                 return false;
  393.                         layout_solve_width(box, box->parent->width, box->width,
  394.                                         lm, rm, -1, -1);
  395.                 }
  396.  
  397.                 /* Position box: horizontal. */
  398.                 box->x = box->parent->padding[LEFT] + box->margin[LEFT] +
  399.                                 box->border[LEFT].width;
  400.                 cx += box->x;
  401.  
  402.                 /* Position box: vertical. */
  403.                 if (box->border[TOP].width) {
  404.                         box->y += box->border[TOP].width;
  405.                         cy += box->border[TOP].width;
  406.                 }
  407.  
  408.                 /* Vertical margin */
  409.                 if (((box->type == BOX_BLOCK &&
  410.                                 (box->flags & HAS_HEIGHT)) ||
  411.                                 box->type == BOX_TABLE ||
  412.                                 (box->type == BOX_INLINE_CONTAINER &&
  413.                                 box != box->parent->children) ||
  414.                                 margin_collapse == box) &&
  415.                                 in_margin == true) {
  416.                         /* Margin goes above this box. */
  417.                         cy += max_pos_margin - max_neg_margin;
  418.                         box->y += max_pos_margin - max_neg_margin;
  419.  
  420.                         /* Current margin has been applied. */
  421.                         in_margin = false;
  422.                         max_pos_margin = max_neg_margin = 0;
  423.                 }
  424.  
  425.                 /* Handle clearance */
  426.                 if (box->type != BOX_INLINE_CONTAINER &&
  427.                                 (y > 0) && (cy < y)) {
  428.                         /* box clears something*/
  429.                         box->y += y - cy;
  430.                         cy = y;
  431.                 }
  432.  
  433.                 /* Unless the box has an overflow style of visible, the box
  434.                  * establishes a new block context. */
  435.                 if (box->type == BOX_BLOCK && box->style &&
  436.                                 css_computed_overflow(box->style) !=
  437.                                 CSS_OVERFLOW_VISIBLE) {
  438.  
  439.                         layout_block_context(box, viewport_height, content);
  440.  
  441.                         cy += box->padding[TOP];
  442.  
  443.                         if (box->height == AUTO) {
  444.                                 box->height = 0;
  445.                                 layout_block_add_scrollbar(box, BOTTOM);
  446.                         }
  447.  
  448.                         cx -= box->x;
  449.                         cy += box->height + box->padding[BOTTOM] +
  450.                                         box->border[BOTTOM].width;
  451.                         y = box->y + box->padding[TOP] + box->height +
  452.                                         box->padding[BOTTOM] +
  453.                                         box->border[BOTTOM].width;
  454.  
  455.                         /* Skip children, because they are done in the new
  456.                          * block context */
  457.                         goto advance_to_next_box;
  458.                 }
  459.  
  460. #ifdef LAYOUT_DEBUG
  461.                 LOG(("box %p, cx %i, cy %i", box, cx, cy));
  462. #endif
  463.  
  464.                 /* Layout (except tables). */
  465.                 if (box->object) {
  466.                         if (!layout_block_object(box))
  467.                                 return false;
  468.  
  469.                 } else if (box->type == BOX_INLINE_CONTAINER) {
  470.                         box->width = box->parent->width;
  471.                         if (!layout_inline_container(box, box->width, block,
  472.                                         cx, cy, content))
  473.                                 return false;
  474.  
  475.                 } else if (box->type == BOX_TABLE) {
  476.                         /* Move down to avoid floats if necessary. */
  477.                         int x0, x1;
  478.                         struct box *left, *right;
  479.                         y = cy;
  480.                         while (1) {
  481.                                 enum css_width_e wtype;
  482.                                 css_fixed width = 0;
  483.                                 css_unit unit = CSS_UNIT_PX;
  484.  
  485.                                 wtype = css_computed_width(box->style,
  486.                                                 &width, &unit);
  487.  
  488.                                 x0 = cx;
  489.                                 x1 = cx + box->parent->width;
  490.                                 find_sides(block->float_children, y,
  491.                                                 y + box->height,
  492.                                                 &x0, &x1, &left, &right);
  493.                                 if (wtype == CSS_WIDTH_AUTO)
  494.                                         break;
  495.                                 if (box->width <= x1 - x0)
  496.                                         break;
  497.                                 if (!left && !right)
  498.                                         break;
  499.                                 else if (!left)
  500.                                         y = right->y + right->height + 1;
  501.                                 else if (!right)
  502.                                         y = left->y + left->height + 1;
  503.                                 else if (left->y + left->height <
  504.                                                 right->y + right->height)
  505.                                         y = left->y + left->height + 1;
  506.                                 else
  507.                                         y = right->y + right->height + 1;
  508.                         }
  509.                         box->x += x0 - cx;
  510.                         cx = x0;
  511.                         box->y += y - cy;
  512.                         cy = y;
  513.                 }
  514.  
  515.                 /* Advance to next box. */
  516.                 if (box->type == BOX_BLOCK && !box->object && !(box->iframe) &&
  517.                                 box->children) {
  518.                         /* Down into children. */
  519.  
  520.                         if (box == margin_collapse) {
  521.                                 /* Current margin collapsed though to this box.
  522.                                  * Unset margin_collapse. */
  523.                                 margin_collapse = NULL;
  524.                         }
  525.  
  526.                         y = box->padding[TOP];
  527.                         box = box->children;
  528.                         box->y = y;
  529.                         cy += y;
  530.                         continue;
  531.                 } else if (box->type == BOX_BLOCK || box->object ||
  532.                                 box->flags & IFRAME)
  533.                         cy += box->padding[TOP];
  534.  
  535.                 if (box->type == BOX_BLOCK && box->height == AUTO) {
  536.                         box->height = 0;
  537.                         layout_block_add_scrollbar(box, BOTTOM);
  538.                 }
  539.  
  540.                 cy += box->height + box->padding[BOTTOM] +
  541.                                 box->border[BOTTOM].width;
  542.                 cx -= box->x;
  543.                 y = box->y + box->padding[TOP] + box->height +
  544.                                 box->padding[BOTTOM] +
  545.                                 box->border[BOTTOM].width;
  546.  
  547.         advance_to_next_box:
  548.                 if (!box->next) {
  549.                         /* No more siblings:
  550.                          * up to first ancestor with a sibling. */
  551.  
  552.                         do {
  553.                                 if (box == margin_collapse) {
  554.                                         /* Current margin collapsed though to
  555.                                          * this box.  Unset margin_collapse. */
  556.                                         margin_collapse = NULL;
  557.                                 }
  558.  
  559.                                 /* Apply bottom margin */
  560.                                 if (max_pos_margin < box->margin[BOTTOM])
  561.                                         max_pos_margin = box->margin[BOTTOM];
  562.                                 else if (max_neg_margin < -box->margin[BOTTOM])
  563.                                         max_neg_margin = -box->margin[BOTTOM];
  564.  
  565.                                 box = box->parent;
  566.                                 if (box == block)
  567.                                         break;
  568.  
  569.                                 /* Margin is invalidated if this is a box
  570.                                  * margins can't collapse through. */
  571.                                 if (box->type == BOX_BLOCK &&
  572.                                                 box->flags & MAKE_HEIGHT) {
  573.                                         margin_collapse = NULL;
  574.                                         in_margin = false;
  575.                                         max_pos_margin = max_neg_margin = 0;
  576.                                 }
  577.  
  578.                                 if (box->height == AUTO) {
  579.                                         box->height = y - box->padding[TOP];
  580.  
  581.                                         if (box->type == BOX_BLOCK)
  582.                                                 layout_block_add_scrollbar(box,
  583.                                                                 BOTTOM);
  584.                                 } else
  585.                                         cy += box->height -
  586.                                                         (y - box->padding[TOP]);
  587.  
  588.                                 /* Apply any min-height and max-height to
  589.                                  * boxes in normal flow */
  590.                                 if (box->style &&
  591.                                         css_computed_position(box->style) !=
  592.                                                 CSS_POSITION_ABSOLUTE &&
  593.                                                 layout_apply_minmax_height(box,
  594.                                                                 NULL)) {
  595.                                         /* Height altered */
  596.                                         /* Set current cy */
  597.                                         cy += box->height -
  598.                                                         (y - box->padding[TOP]);
  599.                                 }
  600.  
  601.                                 cy += box->padding[BOTTOM] +
  602.                                                 box->border[BOTTOM].width;
  603.                                 cx -= box->x;
  604.                                 y = box->y + box->padding[TOP] + box->height +
  605.                                                 box->padding[BOTTOM] +
  606.                                                 box->border[BOTTOM].width;
  607.  
  608.                         } while (box->next == NULL);
  609.                         if (box == block)
  610.                                 break;
  611.                 }
  612.  
  613.                 /* To next sibling. */
  614.  
  615.                 if (box == margin_collapse) {
  616.                         /* Current margin collapsed though to this box.
  617.                          * Unset margin_collapse. */
  618.                         margin_collapse = NULL;
  619.                 }
  620.  
  621.                 if (max_pos_margin < box->margin[BOTTOM])
  622.                         max_pos_margin = box->margin[BOTTOM];
  623.                 else if (max_neg_margin < -box->margin[BOTTOM])
  624.                         max_neg_margin = -box->margin[BOTTOM];
  625.  
  626.                 box = box->next;
  627.                 box->y = y;
  628.         }
  629.  
  630.         /* Account for bottom margin of last contained block */
  631.         cy += max_pos_margin - max_neg_margin;
  632.  
  633.         /* Increase height to contain any floats inside (CSS 2.1 10.6.7). */
  634.         for (box = block->float_children; box; box = box->next_float) {
  635.                 y = box->y + box->height + box->padding[BOTTOM] +
  636.                                 box->border[BOTTOM].width + box->margin[BOTTOM];
  637.                 if (cy < y)
  638.                         cy = y;
  639.         }
  640.  
  641.         if (block->height == AUTO) {
  642.                 block->height = cy - block->padding[TOP];
  643.                 if (block->type == BOX_BLOCK)
  644.                         layout_block_add_scrollbar(block, BOTTOM);
  645.         }
  646.  
  647.         if (block->style && css_computed_position(block->style) !=
  648.                         CSS_POSITION_ABSOLUTE) {
  649.                 /* Block is in normal flow */
  650.                 layout_apply_minmax_height(block, NULL);
  651.         }
  652.  
  653.         return true;
  654. }
  655.  
  656.  
  657. /**
  658.  * Calculate minimum and maximum width of a block.
  659.  *
  660.  * \param  block  box of type BLOCK, INLINE_BLOCK, or TABLE_CELL
  661.  * \post  block->min_width and block->max_width filled in,
  662.  *        0 <= block->min_width <= block->max_width
  663.  */
  664.  
  665. void layout_minmax_block(struct box *block,
  666.                 const struct font_functions *font_func)
  667. {
  668.         struct box *child;
  669.         int min = 0, max = 0;
  670.         int extra_fixed = 0;
  671.         float extra_frac = 0;
  672.         enum css_width_e wtype = CSS_WIDTH_AUTO;
  673.         css_fixed width = 0;
  674.         css_unit wunit = CSS_UNIT_PX;
  675.         enum css_height_e htype = CSS_HEIGHT_AUTO;
  676.         css_fixed height = 0;
  677.         css_unit hunit = CSS_UNIT_PX;
  678.         bool child_has_height = false;
  679.  
  680.         assert(block->type == BOX_BLOCK ||
  681.                         block->type == BOX_INLINE_BLOCK ||
  682.                         block->type == BOX_TABLE_CELL);
  683.  
  684.         /* check if the widths have already been calculated */
  685.         if (block->max_width != UNKNOWN_MAX_WIDTH)
  686.                 return;
  687.  
  688.         if (block->style != NULL) {
  689.                 wtype = css_computed_width(block->style, &width, &wunit);
  690.                 htype = css_computed_height(block->style, &height, &hunit);
  691.         }
  692.  
  693.         /* set whether the minimum width is of any interest for this box */
  694.         if (((block->parent && (block->parent->type == BOX_FLOAT_LEFT ||
  695.                         block->parent->type == BOX_FLOAT_RIGHT)) ||
  696.                         block->type == BOX_INLINE_BLOCK) &&
  697.                         wtype != CSS_WIDTH_SET) {
  698.                 /* box shrinks to fit; need minimum width */
  699.                 block->flags |= NEED_MIN;
  700.         } else if (block->type == BOX_TABLE_CELL) {
  701.                 /* box shrinks to fit; need minimum width */
  702.                 block->flags |= NEED_MIN;
  703.         } else if (block->parent && (block->parent->flags & NEED_MIN) &&
  704.                         wtype != CSS_WIDTH_SET) {
  705.                 /* box inside shrink-to-fit context; need minimum width */
  706.                 block->flags |= NEED_MIN;
  707.         }
  708.  
  709.         if (block->gadget && (block->gadget->type == GADGET_TEXTBOX ||
  710.                         block->gadget->type == GADGET_PASSWORD ||
  711.                         block->gadget->type == GADGET_FILE ||
  712.                         block->gadget->type == GADGET_TEXTAREA) &&
  713.                         block->style && wtype == CSS_WIDTH_AUTO) {
  714.                 css_fixed size = INTTOFIX(10);
  715.                 css_unit unit = CSS_UNIT_EM;
  716.  
  717.                 min = max = FIXTOINT(nscss_len2px(size, unit, block->style));
  718.  
  719.                 block->flags |= HAS_HEIGHT;
  720.         }
  721.  
  722.         if (block->gadget && (block->gadget->type == GADGET_RADIO ||
  723.                         block->gadget->type == GADGET_CHECKBOX) &&
  724.                         block->style && wtype == CSS_WIDTH_AUTO) {
  725.                 css_fixed size = INTTOFIX(1);
  726.                 css_unit unit = CSS_UNIT_EM;
  727.  
  728.                 /* form checkbox or radio button
  729.                  * if width is AUTO, set it to 1em */
  730.                 min = max = FIXTOINT(nscss_len2px(size, unit, block->style));
  731.  
  732.                 block->flags |= HAS_HEIGHT;
  733.         }
  734.  
  735.         if (block->object) {
  736.                 if (content_get_type(block->object) == CONTENT_HTML) {
  737.                         layout_minmax_block(html_get_box_tree(block->object),
  738.                                         font_func);
  739.                         min = html_get_box_tree(block->object)->min_width;
  740.                         max = html_get_box_tree(block->object)->max_width;
  741.                 } else {
  742.                         min = max = content_get_width(block->object);
  743.                 }
  744.  
  745.                 block->flags |= HAS_HEIGHT;
  746.         } else if (block->flags & IFRAME) {
  747.                 /** TODO: do we need to know the min/max width of the iframe's
  748.                  * content? */
  749.                 block->flags |= HAS_HEIGHT;
  750.         } else {
  751.                 /* recurse through children */
  752.                 for (child = block->children; child; child = child->next) {
  753.                         switch (child->type) {
  754.                         case BOX_BLOCK:
  755.                                 layout_minmax_block(child, font_func);
  756.                                 if (child->flags & HAS_HEIGHT)
  757.                                         child_has_height = true;
  758.                                 break;
  759.                         case BOX_INLINE_CONTAINER:
  760.                                 if (block->flags & NEED_MIN)
  761.                                         child->flags |= NEED_MIN;
  762.  
  763.                                 layout_minmax_inline_container(child,
  764.                                                 &child_has_height, font_func);
  765.                                 if (child_has_height &&
  766.                                                 child ==
  767.                                                 child->parent->children) {
  768.                                         block->flags |= MAKE_HEIGHT;
  769.                                 }
  770.                                 break;
  771.                         case BOX_TABLE:
  772.                                 layout_minmax_table(child, font_func);
  773.                                 /* todo: fix for zero height tables */
  774.                                 child_has_height = true;
  775.                                 child->flags |= MAKE_HEIGHT;
  776.                                 break;
  777.                         default:
  778.                                 assert(0);
  779.                         }
  780.                         assert(child->max_width != UNKNOWN_MAX_WIDTH);
  781.  
  782.                         if (child->style &&
  783.                                         (css_computed_position(child->style) ==
  784.                                                         CSS_POSITION_ABSOLUTE ||
  785.                                         css_computed_position(child->style) ==
  786.                                                         CSS_POSITION_FIXED)) {
  787.                                 /* This child is positioned out of normal flow,
  788.                                  * so it will have no affect on width */
  789.                                 continue;
  790.                         }
  791.  
  792.                         if (min < child->min_width)
  793.                                 min = child->min_width;
  794.                         if (max < child->max_width)
  795.                                 max = child->max_width;
  796.  
  797.                         if (child_has_height)
  798.                                 block->flags |= HAS_HEIGHT;
  799.                 }
  800.         }
  801.  
  802.         if (max < min) {
  803.                 box_dump(stderr, block, 0);
  804.                 assert(0);
  805.         }
  806.  
  807.         /* fixed width takes priority */
  808.         if (block->type != BOX_TABLE_CELL && wtype == CSS_WIDTH_SET &&
  809.                         wunit != CSS_UNIT_PCT) {
  810.                 min = max = FIXTOINT(nscss_len2px(width, wunit, block->style));
  811.         }
  812.  
  813.         if (htype == CSS_HEIGHT_SET && hunit != CSS_UNIT_PCT &&
  814.                         height > INTTOFIX(0)) {
  815.                 block->flags |= MAKE_HEIGHT;
  816.                 block->flags |= HAS_HEIGHT;
  817.         }
  818.  
  819.         /* add margins, border, padding to min, max widths */
  820.         /* Note: we don't know available width here so percentage margin
  821.          * and paddings are wrong. */
  822.         if (block->gadget && wtype == CSS_WIDTH_SET &&
  823.                         (block->gadget->type == GADGET_SUBMIT ||
  824.                         block->gadget->type == GADGET_RESET ||
  825.                         block->gadget->type == GADGET_BUTTON)) {
  826.                 /* some gadgets with specified width already include border and
  827.                  * padding, so just get margin */
  828.                 calculate_mbp_width(block->style, LEFT, true, false, false,
  829.                                 &extra_fixed, &extra_frac);
  830.                 calculate_mbp_width(block->style, RIGHT, true, false, false,
  831.                                 &extra_fixed, &extra_frac);
  832.         } else {
  833.                 calculate_mbp_width(block->style, LEFT, true, true, true,
  834.                                 &extra_fixed, &extra_frac);
  835.                 calculate_mbp_width(block->style, RIGHT, true, true, true,
  836.                                 &extra_fixed, &extra_frac);
  837.         }
  838.         if (extra_fixed < 0)
  839.                 extra_fixed = 0;
  840.         if (extra_frac < 0)
  841.                 extra_frac = 0;
  842.         if (1.0 <= extra_frac)
  843.                 extra_frac = 0.9;
  844.         if (block->style != NULL &&
  845.                         (css_computed_float(block->style) == CSS_FLOAT_LEFT ||
  846.                         css_computed_float(block->style) == CSS_FLOAT_RIGHT)) {
  847.                 /* floated boxs */
  848.                 block->min_width = min + extra_fixed;
  849.                 block->max_width = max + extra_fixed;
  850.         } else {
  851.                 /* not floated */
  852.                 block->min_width = (min + extra_fixed) / (1.0 - extra_frac);
  853.                 block->max_width = (max + extra_fixed) / (1.0 - extra_frac);
  854.         }
  855.  
  856.         assert(0 <= block->min_width && block->min_width <= block->max_width);
  857. }
  858.  
  859.  
  860. /**
  861.  * Find next block that current margin collapses to.
  862.  *
  863.  * \param  box    box to start tree-order search from (top margin is included)
  864.  * \param  block  box responsible for current block fromatting context
  865.  * \param  viewport_height  height of viewport in px
  866.  * \param  max_pos_margin  updated to to maximum positive margin encountered
  867.  * \param  max_neg_margin  updated to to maximum negative margin encountered
  868.  * \return  next box that current margin collapses to, or NULL if none.
  869.  */
  870.  
  871. struct box* layout_next_margin_block(struct box *box, struct box *block,
  872.                 int viewport_height, int *max_pos_margin, int *max_neg_margin)
  873. {
  874.         assert(block != NULL);
  875.  
  876.         while (box != NULL) {
  877.  
  878.                 if (box->type == BOX_INLINE_CONTAINER || (box->style &&
  879.                                 (css_computed_position(box->style) !=
  880.                                         CSS_POSITION_ABSOLUTE &&
  881.                                  css_computed_position(box->style) !=
  882.                                         CSS_POSITION_FIXED))) {
  883.                         /* Not positioned */
  884.  
  885.                         /* Get margins */
  886.                         if (box->style) {
  887.                                 layout_find_dimensions(box->parent->width,
  888.                                                 viewport_height, box,
  889.                                                 box->style,
  890.                                                 NULL, NULL, NULL, NULL,
  891.                                                 box->margin, box->padding,
  892.                                                 box->border);
  893.  
  894.                                 /* Apply top margin */
  895.                                 if (*max_pos_margin < box->margin[TOP])
  896.                                         *max_pos_margin = box->margin[TOP];
  897.                                 else if (*max_neg_margin < -box->margin[TOP])
  898.                                         *max_neg_margin = -box->margin[TOP];
  899.                         }
  900.  
  901.                         /* Check whether box is the box current margin collapses
  902.                          * to */
  903.                         if (box->flags & MAKE_HEIGHT ||
  904.                                         box->border[TOP].width ||
  905.                                         box->padding[TOP] ||
  906.                                         (box->style &&
  907.                                         css_computed_overflow(box->style) !=
  908.                                         CSS_OVERFLOW_VISIBLE) ||
  909.                                         (box->type == BOX_INLINE_CONTAINER &&
  910.                                         box != box->parent->children)) {
  911.                                 /* Collapse to this box; return it */
  912.                                 return box;
  913.                         }
  914.                 }
  915.  
  916.  
  917.                 /* Find next box */
  918.                 if (box->type == BOX_BLOCK && !box->object && box->children &&
  919.                                 box->style &&
  920.                                 css_computed_overflow(box->style) ==
  921.                                 CSS_OVERFLOW_VISIBLE) {
  922.                         /* Down into children. */
  923.                         box = box->children;
  924.                 } else {
  925.                         if (!box->next) {
  926.                                 /* No more siblings:
  927.                                  * Go up to first ancestor with a sibling. */
  928.                                 do {
  929.                                         /* Apply bottom margin */
  930.                                         if (*max_pos_margin <
  931.                                                         box->margin[BOTTOM])
  932.                                                 *max_pos_margin =
  933.                                                         box->margin[BOTTOM];
  934.                                         else if (*max_neg_margin <
  935.                                                         -box->margin[BOTTOM])
  936.                                                 *max_neg_margin =
  937.                                                         -box->margin[BOTTOM];
  938.  
  939.                                         box = box->parent;
  940.                                 } while (box != block && !box->next);
  941.  
  942.                                 if (box == block) {
  943.                                         /* Margins don't collapse with stuff
  944.                                          * outside the block formatting context
  945.                                          */
  946.                                         return block;
  947.                                 }
  948.                         }
  949.  
  950.                         /* Apply bottom margin */
  951.                         if (*max_pos_margin < box->margin[BOTTOM])
  952.                                 *max_pos_margin = box->margin[BOTTOM];
  953.                         else if (*max_neg_margin < -box->margin[BOTTOM])
  954.                                 *max_neg_margin = -box->margin[BOTTOM];
  955.  
  956.                         /* To next sibling. */
  957.                         box = box->next;
  958.  
  959.                         /* Get margins */
  960.                         if (box->style) {
  961.                                 layout_find_dimensions(box->parent->width,
  962.                                                 viewport_height, box,
  963.                                                 box->style,
  964.                                                 NULL, NULL, NULL, NULL,
  965.                                                 box->margin, box->padding,
  966.                                                 box->border);
  967.                         }
  968.                 }
  969.         }
  970.  
  971.         return NULL;
  972. }
  973.  
  974.  
  975. /**
  976.  * Layout a block which contains an object.
  977.  *
  978.  * \param  block  box of type BLOCK, INLINE_BLOCK, TABLE, or TABLE_CELL
  979.  * \return  true on success, false on memory exhaustion
  980.  */
  981.  
  982. bool layout_block_object(struct box *block)
  983. {
  984.         assert(block);
  985.         assert(block->type == BOX_BLOCK ||
  986.                         block->type == BOX_INLINE_BLOCK ||
  987.                         block->type == BOX_TABLE ||
  988.                         block->type == BOX_TABLE_CELL);
  989.         assert(block->object);
  990.  
  991. #ifdef LAYOUT_DEBUG
  992.         LOG(("block %p, object %s, width %i", block,
  993.                         hlcache_handle_get_url(block->object), block->width));
  994. #endif
  995.  
  996.         if (content_get_type(block->object) == CONTENT_HTML) {
  997.                 content_reformat(block->object, false, block->width, 1);
  998.         } else {
  999.                 /* Non-HTML objects */
  1000.                 /* this case handled already in
  1001.                  * layout_block_find_dimensions() */
  1002.         }
  1003.  
  1004.         return true;
  1005. }
  1006.  
  1007.  
  1008. /**
  1009.  * Compute the size of replaced boxes with auto dimensions, according to
  1010.  * content.
  1011.  *
  1012.  * \param  box     Box with object
  1013.  * \param  width   Width value in px or AUTO.  If AUTO, updated to value in px.
  1014.  * \param  height  Height value in px or AUTO. If AUTO, updated to value in px.
  1015.  * \param  min_width  Box's min width, as given by layout_find_dimensions.
  1016.  * \param  max_width  Box's max width, as given by layout_find_dimensions.
  1017.  *
  1018.  * See CSS 2.1 sections 10.3 and 10.6.
  1019.  */
  1020.  
  1021. void layout_get_object_dimensions(struct box *box, int *width, int *height,
  1022.                 int min_width, int max_width)
  1023. {
  1024.         assert(box->object != NULL);
  1025.         assert(width != NULL && height != NULL);
  1026.  
  1027.         if (*width == AUTO && *height == AUTO) {
  1028.                 /* No given dimensions */
  1029.  
  1030.                 bool scaled = false;
  1031.                 int intrinsic_width = content_get_width(box->object);
  1032.                 int intrinsic_height = content_get_height(box->object);
  1033.  
  1034.                 /* use intrinsic dimensions */
  1035.                 *width = intrinsic_width;
  1036.                 *height = intrinsic_height;
  1037.  
  1038.                 if (min_width >  0 && min_width > *width) {
  1039.                         *width = min_width;
  1040.                         scaled = true;
  1041.                 }
  1042.                 if (max_width >= 0 && max_width < *width) {
  1043.                         *width = max_width;
  1044.                         scaled = true;
  1045.                 }
  1046.  
  1047.                 if (scaled)
  1048.                         *height = (*width * intrinsic_height) /
  1049.                                         intrinsic_width;
  1050.  
  1051.         } else if (*width == AUTO) {
  1052.                 /* Have given height; width is calculated from the given height
  1053.                  * and ratio of intrinsic dimensions */
  1054.                 int intrinsic_width = content_get_width(box->object);
  1055.                 int intrinsic_height = content_get_height(box->object);
  1056.  
  1057.                 if (intrinsic_height != 0)
  1058.                         *width = (*height * intrinsic_width) /
  1059.                                         intrinsic_height;
  1060.                 else
  1061.                         *width = intrinsic_width;
  1062.  
  1063.                 if (min_width >  0 && min_width > *width)
  1064.                         *width = min_width;
  1065.                 if (max_width >= 0 && max_width < *width)
  1066.                         *width = max_width;
  1067.  
  1068.         } else if (*height == AUTO) {
  1069.                 /* Have given width; height is calculated from the given width
  1070.                  * and ratio of intrinsic dimensions */
  1071.                 int intrinsic_width = content_get_width(box->object);
  1072.                 int intrinsic_height = content_get_height(box->object);
  1073.  
  1074.                 if (intrinsic_width != 0)
  1075.                         *height = (*width * intrinsic_height) /
  1076.                                         intrinsic_width;
  1077.                 else
  1078.                         *height = intrinsic_height;
  1079.         }
  1080. }
  1081.  
  1082.  
  1083. /**
  1084.  * Compute dimensions of box, margins, paddings, and borders for a block-level
  1085.  * element.
  1086.  *
  1087.  * \param  available_width  Max width available in pixels
  1088.  * \param  viewport_height  Height of viewport in pixels or -ve if unknown
  1089.  * \param  lm               min left margin required to avoid floats in px.
  1090.  *                              zero if not applicable
  1091.  * \param  rm               min right margin required to avoid floats in px.
  1092.  *                              zero if not applicable
  1093.  * \param  box              box to find dimensions of. updated with new width,
  1094.  *                          height, margins, borders and paddings
  1095.  *
  1096.  * See CSS 2.1 10.3.3, 10.3.4, 10.6.2, and 10.6.3.
  1097.  */
  1098.  
  1099. void layout_block_find_dimensions(int available_width, int viewport_height,
  1100.                 int lm, int rm, struct box *box)
  1101. {
  1102.         int width, max_width, min_width;
  1103.         int height;
  1104.         int *margin = box->margin;
  1105.         int *padding = box->padding;
  1106.         struct box_border *border = box->border;
  1107.         const css_computed_style *style = box->style;
  1108.  
  1109.         layout_find_dimensions(available_width, viewport_height, box, style,
  1110.                         &width, &height, &max_width, &min_width,
  1111.                         margin, padding, border);
  1112.  
  1113.         if (box->object && !(box->flags & REPLACE_DIM) &&
  1114.                         content_get_type(box->object) != CONTENT_HTML) {
  1115.                 /* block-level replaced element, see 10.3.4 and 10.6.2 */
  1116.                 layout_get_object_dimensions(box, &width, &height,
  1117.                                 min_width, max_width);
  1118.         }
  1119.  
  1120.         box->width = layout_solve_width(box, available_width, width, lm, rm,
  1121.                         max_width, min_width);
  1122.         box->height = height;
  1123.  
  1124.         if (margin[TOP] == AUTO)
  1125.                 margin[TOP] = 0;
  1126.         if (margin[BOTTOM] == AUTO)
  1127.                 margin[BOTTOM] = 0;
  1128. }
  1129.  
  1130. /**
  1131.  * Manimpulate box height according to CSS min-height and max-height properties
  1132.  *
  1133.  * \param  box          block to modify with any min-height or max-height
  1134.  * \param  container    containing block for absolutely positioned elements, or
  1135.  *                      NULL for non absolutely positioned elements.
  1136.  * \return              whether the height has been changed
  1137.  */
  1138.  
  1139. bool layout_apply_minmax_height(struct box *box, struct box *container)
  1140. {
  1141.         int h;
  1142.         struct box *containing_block = NULL;
  1143.         bool updated = false;
  1144.  
  1145.         /* Find containing block for percentage heights */
  1146.         if (box->style != NULL && css_computed_position(box->style) ==
  1147.                         CSS_POSITION_ABSOLUTE) {
  1148.                 /* Box is absolutely positioned */
  1149.                 assert(container);
  1150.                 containing_block = container;
  1151.         } else if (box->float_container && box->style != NULL &&
  1152.                         (css_computed_float(box->style) == CSS_FLOAT_LEFT ||
  1153.                          css_computed_float(box->style) == CSS_FLOAT_RIGHT)) {
  1154.                 /* Box is a float */
  1155.                 assert(box->parent && box->parent->parent &&
  1156.                                 box->parent->parent->parent);
  1157.                 containing_block = box->parent->parent->parent;
  1158.         } else if (box->parent && box->parent->type != BOX_INLINE_CONTAINER) {
  1159.                 /* Box is a block level element */
  1160.                 containing_block = box->parent;
  1161.         } else if (box->parent && box->parent->type == BOX_INLINE_CONTAINER) {
  1162.                 /* Box is an inline block */
  1163.                 assert(box->parent->parent);
  1164.                 containing_block = box->parent->parent;
  1165.         }
  1166.  
  1167.         if (box->style) {
  1168.                 enum css_height_e htype = CSS_HEIGHT_AUTO;
  1169.                 css_fixed value = 0;
  1170.                 css_unit unit = CSS_UNIT_PX;
  1171.  
  1172.                 if (containing_block) {
  1173.                         htype = css_computed_height(containing_block->style,
  1174.                                         &value, &unit);
  1175.                 }
  1176.  
  1177.                 /* max-height */
  1178.                 if (css_computed_max_height(box->style, &value, &unit) ==
  1179.                                 CSS_MAX_HEIGHT_SET) {
  1180.                         if (unit == CSS_UNIT_PCT) {
  1181.                                 if (containing_block &&
  1182.                                         containing_block->height != AUTO &&
  1183.                                         (css_computed_position(box->style) ==
  1184.                                                         CSS_POSITION_ABSOLUTE ||
  1185.                                                 htype == CSS_HEIGHT_SET)) {
  1186.                                         /* Box is absolutely positioned or its
  1187.                                          * containing block has a valid
  1188.                                          * specified height. (CSS 2.1
  1189.                                          * Section 10.5) */
  1190.                                         h = FPCT_OF_INT_TOINT(value,
  1191.                                                 containing_block->height);
  1192.                                         if (h < box->height) {
  1193.                                                 box->height = h;
  1194.                                                 updated = true;
  1195.                                         }
  1196.                                 }
  1197.                         } else {
  1198.                                 h = FIXTOINT(nscss_len2px(value, unit,
  1199.                                                 box->style));
  1200.                                 if (h < box->height) {
  1201.                                         box->height = h;
  1202.                                         updated = true;
  1203.                                 }
  1204.                         }
  1205.                 }
  1206.  
  1207.                 /* min-height */
  1208.                 if (css_computed_min_height(box->style, &value, &unit) ==
  1209.                                 CSS_MIN_HEIGHT_SET) {
  1210.                         if (unit == CSS_UNIT_PCT) {
  1211.                                 if (containing_block &&
  1212.                                         containing_block->height != AUTO &&
  1213.                                         (css_computed_position(box->style) ==
  1214.                                                         CSS_POSITION_ABSOLUTE ||
  1215.                                                 htype == CSS_HEIGHT_SET)) {
  1216.                                         /* Box is absolutely positioned or its
  1217.                                          * containing block has a valid
  1218.                                          * specified height. (CSS 2.1
  1219.                                          * Section 10.5) */
  1220.                                         h = FPCT_OF_INT_TOINT(value,
  1221.                                                 containing_block->height);
  1222.                                         if (h > box->height) {
  1223.                                                 box->height = h;
  1224.                                                 updated = true;
  1225.                                         }
  1226.                                 }
  1227.                         } else {
  1228.                                 h = FIXTOINT(nscss_len2px(value, unit,
  1229.                                                 box->style));
  1230.                                 if (h > box->height) {
  1231.                                         box->height = h;
  1232.                                         updated = true;
  1233.                                 }
  1234.                         }
  1235.                 }
  1236.         }
  1237.         return updated;
  1238. }
  1239.  
  1240. /**
  1241.  * Manipulate a block's [RB]padding/height/width to accommodate scrollbars
  1242.  *
  1243.  * \param  box    Box to apply scrollbar space too. Must be BOX_BLOCK.
  1244.  * \param  which  Which scrollbar to make space for. Must be RIGHT or BOTTOM.
  1245.  */
  1246.  
  1247. void layout_block_add_scrollbar(struct box *box, int which)
  1248. {
  1249.         enum css_overflow_e overflow;
  1250.  
  1251.         assert(box->type == BOX_BLOCK && (which == RIGHT || which == BOTTOM));
  1252.  
  1253.         if (box->style == NULL)
  1254.                 return;
  1255.  
  1256.         overflow = css_computed_overflow(box->style);
  1257.  
  1258.         if (overflow == CSS_OVERFLOW_SCROLL || overflow == CSS_OVERFLOW_AUTO ||
  1259.                         (box->object && content_get_type(box->object) ==
  1260.                                         CONTENT_HTML)) {
  1261.                 /* make space for scrollbars, unless height/width are AUTO */
  1262.                 enum css_height_e htype;
  1263.                 css_fixed height = 0;
  1264.                 css_unit hunit = CSS_UNIT_PX;
  1265.                 htype = css_computed_height(box->style, &height, &hunit);
  1266.  
  1267.                 if (which == BOTTOM && box->height != AUTO &&
  1268.                                 (overflow == CSS_OVERFLOW_SCROLL ||
  1269.                                 box_hscrollbar_present(box))) {
  1270.                         box->padding[BOTTOM] += SCROLLBAR_WIDTH;
  1271.                 }
  1272.                 if (which == RIGHT && box->width != AUTO &&
  1273.                                 htype == CSS_HEIGHT_SET &&
  1274.                                 (overflow == CSS_OVERFLOW_SCROLL ||
  1275.                                 box_vscrollbar_present(box))) {
  1276.                         box->width -= SCROLLBAR_WIDTH;
  1277.                         box->padding[RIGHT] += SCROLLBAR_WIDTH;
  1278.                 }
  1279.         }
  1280. }
  1281.  
  1282. /**
  1283.  * Solve the width constraint as given in CSS 2.1 section 10.3.3.
  1284.  *
  1285.  * \param  box              Box to solve constraint for
  1286.  * \param  available_width  Max width available in pixels
  1287.  * \param  width            Current box width
  1288.  * \param  lm               Min left margin required to avoid floats in px.
  1289.  *                              zero if not applicable
  1290.  * \param  rm               Min right margin required to avoid floats in px.
  1291.  *                              zero if not applicable
  1292.  * \param  max_width        Box max-width ( -ve means no max-width to apply)
  1293.  * \param  min_width        Box min-width ( <=0 means no min-width to apply)
  1294.  * \return                  New box width
  1295.  *
  1296.  * \post \a box's left/right margins will be updated.
  1297.  */
  1298.  
  1299. int layout_solve_width(struct box *box, int available_width, int width,
  1300.                 int lm, int rm, int max_width, int min_width)
  1301. {
  1302.         bool auto_width = false;
  1303.  
  1304.         /* Increase specified left/right margins */
  1305.         if (box->margin[LEFT] != AUTO && box->margin[LEFT] < lm &&
  1306.                         box->margin[LEFT] >= 0)
  1307.                 box->margin[LEFT] = lm;
  1308.         if (box->margin[RIGHT] != AUTO && box->margin[RIGHT] < rm &&
  1309.                         box->margin[RIGHT] >= 0)
  1310.                 box->margin[RIGHT] = rm;
  1311.  
  1312.         /* Find width */
  1313.         if (width == AUTO) {
  1314.                 /* any other 'auto' become 0 or the minimum required values */
  1315.                 if (box->margin[LEFT] == AUTO)
  1316.                         box->margin[LEFT] = lm;
  1317.                 if (box->margin[RIGHT] == AUTO)
  1318.                         box->margin[RIGHT] = rm;
  1319.  
  1320.                 width = available_width -
  1321.                                 (box->margin[LEFT] + box->border[LEFT].width +
  1322.                                 box->padding[LEFT] + box->padding[RIGHT] +
  1323.                                 box->border[RIGHT].width + box->margin[RIGHT]);
  1324.                 width = width < 0 ? 0 : width;
  1325.                 auto_width = true;
  1326.         }
  1327.  
  1328.         if (max_width >= 0 && width > max_width) {
  1329.                 /* max-width is admissable and width exceeds max-width */
  1330.                 width = max_width;
  1331.                 auto_width = false;
  1332.         }
  1333.  
  1334.         if (min_width > 0 && width < min_width) {
  1335.                 /* min-width is admissable and width is less than max-width */
  1336.                 width = min_width;
  1337.                 auto_width = false;
  1338.         }
  1339.  
  1340.         /* Width was auto, and unconstrained by min/max width, so we're done */
  1341.         if (auto_width)
  1342.                 return width;
  1343.  
  1344.         /* Width was not auto, or was constrained by min/max width
  1345.          * Need to compute left/right margins */
  1346.  
  1347.         /* HTML alignment (only applies to over-constrained boxes) */
  1348.         if (box->margin[LEFT] != AUTO && box->margin[RIGHT] != AUTO &&
  1349.                         box->parent != NULL && box->parent->style != NULL) {
  1350.                 switch (css_computed_text_align(box->parent->style)) {
  1351.                 case CSS_TEXT_ALIGN_LIBCSS_RIGHT:
  1352.                         box->margin[LEFT] = AUTO;
  1353.                         box->margin[RIGHT] = 0;
  1354.                         break;
  1355.                 case CSS_TEXT_ALIGN_LIBCSS_CENTER:
  1356.                         box->margin[LEFT] = box->margin[RIGHT] = AUTO;
  1357.                         break;
  1358.                 case CSS_TEXT_ALIGN_LIBCSS_LEFT:
  1359.                         box->margin[LEFT] = 0;
  1360.                         box->margin[RIGHT] = AUTO;
  1361.                         break;
  1362.                 default:
  1363.                         /* Leave it alone; no HTML alignment */
  1364.                         break;
  1365.                 }
  1366.         }
  1367.  
  1368.         if (box->margin[LEFT] == AUTO && box->margin[RIGHT] == AUTO) {
  1369.                 /* make the margins equal, centering the element */
  1370.                 box->margin[LEFT] = box->margin[RIGHT] =
  1371.                                 (available_width - lm - rm -
  1372.                                 (box->border[LEFT].width + box->padding[LEFT] +
  1373.                                 width + box->padding[RIGHT] +
  1374.                                 box->border[RIGHT].width)) / 2;
  1375.  
  1376.                 if (box->margin[LEFT] < 0) {
  1377.                         box->margin[RIGHT] += box->margin[LEFT];
  1378.                         box->margin[LEFT] = 0;
  1379.                 }
  1380.  
  1381.                 box->margin[LEFT] += lm;
  1382.  
  1383.         } else if (box->margin[LEFT] == AUTO) {
  1384.                 box->margin[LEFT] = available_width - lm -
  1385.                                 (box->border[LEFT].width + box->padding[LEFT] +
  1386.                                 width + box->padding[RIGHT] +
  1387.                                 box->border[RIGHT].width + box->margin[RIGHT]);
  1388.                 box->margin[LEFT] = box->margin[LEFT] < lm
  1389.                                 ? lm : box->margin[LEFT];
  1390.         } else {
  1391.                 /* margin-right auto or "over-constrained" */
  1392.                 box->margin[RIGHT] = available_width - rm -
  1393.                                 (box->margin[LEFT] + box->border[LEFT].width +
  1394.                                  box->padding[LEFT] + width +
  1395.                                  box->padding[RIGHT] +
  1396.                                  box->border[RIGHT].width);
  1397.         }
  1398.  
  1399.         return width;
  1400. }
  1401.  
  1402.  
  1403. /**
  1404.  * Compute dimensions of box, margins, paddings, and borders for a floating
  1405.  * element using shrink-to-fit. Also used for inline-blocks.
  1406.  *
  1407.  * \param  available_width  Max width available in pixels
  1408.  * \param  style            Box's style
  1409.  * \param  box              Box for which to find dimensions
  1410.  *                              Box margins, borders, paddings, width and
  1411.  *                              height are updated.
  1412.  */
  1413.  
  1414. void layout_float_find_dimensions(int available_width,
  1415.                 const css_computed_style *style, struct box *box)
  1416. {
  1417.         int width, height, max_width, min_width;
  1418.         int *margin = box->margin;
  1419.         int *padding = box->padding;
  1420.         struct box_border *border = box->border;
  1421.         int scrollbar_width =
  1422.                         (css_computed_overflow(style) == CSS_OVERFLOW_SCROLL ||
  1423.                          css_computed_overflow(style) == CSS_OVERFLOW_AUTO) ?
  1424.                         SCROLLBAR_WIDTH : 0;
  1425.  
  1426.         layout_find_dimensions(available_width, -1, box, style, &width, &height,
  1427.                         &max_width, &min_width, margin, padding, border);
  1428.  
  1429.         if (margin[LEFT] == AUTO)
  1430.                 margin[LEFT] = 0;
  1431.         if (margin[RIGHT] == AUTO)
  1432.                 margin[RIGHT] = 0;
  1433.  
  1434.         padding[RIGHT] += scrollbar_width;
  1435.         padding[BOTTOM] += scrollbar_width;
  1436.  
  1437.         if (box->object && !(box->flags & REPLACE_DIM) &&
  1438.                         content_get_type(box->object) != CONTENT_HTML) {
  1439.                 /* Floating replaced element, with intrinsic width or height.
  1440.                  * See 10.3.6 and 10.6.2 */
  1441.                 layout_get_object_dimensions(box, &width, &height,
  1442.                                 min_width, max_width);
  1443.         } else if (box->gadget && (box->gadget->type == GADGET_TEXTBOX ||
  1444.                         box->gadget->type == GADGET_PASSWORD ||
  1445.                         box->gadget->type == GADGET_FILE ||
  1446.                         box->gadget->type == GADGET_TEXTAREA)) {
  1447.                 css_fixed size = 0;
  1448.                 css_unit unit = CSS_UNIT_EM;
  1449.  
  1450.                 /* Give sensible dimensions to gadgets, with auto width/height,
  1451.                  * that don't shrink to fit contained text. */
  1452.                 assert(box->style);
  1453.  
  1454.                 if (box->gadget->type == GADGET_TEXTBOX ||
  1455.                                 box->gadget->type == GADGET_PASSWORD ||
  1456.                                 box->gadget->type == GADGET_FILE) {
  1457.                         if (width == AUTO) {
  1458.                                 size = INTTOFIX(10);
  1459.                                 width = FIXTOINT(nscss_len2px(size, unit,
  1460.                                                 box->style));
  1461.                         }
  1462.                         if (box->gadget->type == GADGET_FILE &&
  1463.                                         height == AUTO) {
  1464.                                 size = FLTTOFIX(1.5);
  1465.                                 height = FIXTOINT(nscss_len2px(size, unit,
  1466.                                                 box->style));
  1467.                         }
  1468.                 }
  1469.                 if (box->gadget->type == GADGET_TEXTAREA) {
  1470.                         if (width == AUTO) {
  1471.                                 size = INTTOFIX(10);
  1472.                                 width = FIXTOINT(nscss_len2px(size, unit,
  1473.                                                 box->style));
  1474.                         } else {
  1475.                                 width -= scrollbar_width;
  1476.                         }
  1477.                         if (height == AUTO) {
  1478.                                 size = INTTOFIX(4);
  1479.                                 height = FIXTOINT(nscss_len2px(size, unit,
  1480.                                                 box->style));
  1481.                         }
  1482.                 }
  1483.         } else if (width == AUTO) {
  1484.                 /* CSS 2.1 section 10.3.5 */
  1485.                 width = min(max(box->min_width, available_width),
  1486.                                 box->max_width);
  1487.  
  1488.                 /* width includes margin, borders and padding */
  1489.                 if (width == available_width) {
  1490.                         width -= box->margin[LEFT] + box->border[LEFT].width +
  1491.                                         box->padding[LEFT] +
  1492.                                         box->padding[RIGHT] +
  1493.                                         box->border[RIGHT].width +
  1494.                                         box->margin[RIGHT];
  1495.                 } else {
  1496.                         /* width was obtained from a min_width or max_width
  1497.                          * value, so need to use the same method for calculating
  1498.                          * mbp as was used in layout_minmax_block() */
  1499.                         int fixed = 0;
  1500.                         float frac = 0;
  1501.                         calculate_mbp_width(box->style, LEFT, true, true, true,
  1502.                                         &fixed, &frac);
  1503.                         calculate_mbp_width(box->style, RIGHT, true, true, true,
  1504.                                         &fixed, &frac);
  1505.                         if (fixed < 0)
  1506.                                 fixed = 0;
  1507.  
  1508.                         width -= fixed;
  1509.                 }
  1510.  
  1511.                 if (max_width >= 0 && width > max_width) width = max_width;
  1512.                 if (min_width >  0 && width < min_width) width = min_width;
  1513.  
  1514.         } else {
  1515.                 if (max_width >= 0 && width > max_width) width = max_width;
  1516.                 if (min_width >  0 && width < min_width) width = min_width;
  1517.                 width -= scrollbar_width;
  1518.         }
  1519.  
  1520.         box->width = width;
  1521.         box->height = height;
  1522.  
  1523.         if (margin[TOP] == AUTO)
  1524.                 margin[TOP] = 0;
  1525.         if (margin[BOTTOM] == AUTO)
  1526.                 margin[BOTTOM] = 0;
  1527. }
  1528.  
  1529.  
  1530. /**
  1531.  * Calculate width, height, and thickness of margins, paddings, and borders.
  1532.  *
  1533.  * \param  available_width  width of containing block
  1534.  * \param  viewport_height  height of viewport in pixels or -ve if unknown
  1535.  * \param  box              current box
  1536.  * \param  style            style giving width, height, margins, paddings,
  1537.  *                          and borders
  1538.  * \param  width            updated to width, may be NULL
  1539.  * \param  height           updated to height, may be NULL
  1540.  * \param  max_width        updated to max-width, may be NULL
  1541.  * \param  min_width        updated to min-width, may be NULL
  1542.  * \param  margin[4]        filled with margins, may be NULL
  1543.  * \param  padding[4]       filled with paddings, may be NULL
  1544.  * \param  border[4]        filled with border widths, may be NULL
  1545.  */
  1546.  
  1547. void layout_find_dimensions(int available_width, int viewport_height,
  1548.                 struct box *box, const css_computed_style *style,
  1549.                 int *width, int *height, int *max_width, int *min_width,
  1550.                 int margin[4], int padding[4], struct box_border border[4])
  1551. {
  1552.         struct box *containing_block = NULL;
  1553.         unsigned int i;
  1554.         bool percentage;
  1555.  
  1556.         if (width) {
  1557.                 enum css_width_e wtype;
  1558.                 css_fixed value = 0;
  1559.                 css_unit unit = CSS_UNIT_PX;
  1560.  
  1561.                 wtype = css_computed_width(style, &value, &unit);
  1562.  
  1563.                 if (wtype == CSS_WIDTH_SET) {
  1564.                         if (unit == CSS_UNIT_PCT) {
  1565.                                 *width = FPCT_OF_INT_TOINT(
  1566.                                                 value, available_width);
  1567.                         } else {
  1568.                                 *width = FIXTOINT(nscss_len2px(value, unit,
  1569.                                                 style));
  1570.                         }
  1571.                 } else {
  1572.                         *width = AUTO;
  1573.                 }
  1574.  
  1575.                 /* specified gadget widths include borders and padding in some
  1576.                  * cases */
  1577.                 if (box->gadget && *width != AUTO) {
  1578.                         percentage = unit == CSS_UNIT_PCT;
  1579.  
  1580.                         layout_tweak_form_dimensions(box, percentage,
  1581.                                         available_width, true, width);
  1582.                 }
  1583.         }
  1584.  
  1585.         if (height) {
  1586.                 enum css_height_e htype;
  1587.                 css_fixed value = 0;
  1588.                 css_unit unit = CSS_UNIT_PX;
  1589.  
  1590.                 htype = css_computed_height(style, &value, &unit);
  1591.  
  1592.                 if (htype == CSS_HEIGHT_SET) {
  1593.                         if (unit == CSS_UNIT_PCT) {
  1594.                                 enum css_height_e cbhtype;
  1595.  
  1596.                                 if (css_computed_position(box->style) ==
  1597.                                                 CSS_POSITION_ABSOLUTE &&
  1598.                                                 box->parent) {
  1599.                                         /* Box is absolutely positioned */
  1600.                                         assert(box->float_container);
  1601.                                         containing_block = box->float_container;
  1602.                                 } else if (box->float_container &&
  1603.                                         css_computed_position(box->style) !=
  1604.                                                 CSS_POSITION_ABSOLUTE &&
  1605.                                         (css_computed_float(box->style) ==
  1606.                                                 CSS_FLOAT_LEFT ||
  1607.                                          css_computed_float(box->style) ==
  1608.                                                 CSS_FLOAT_RIGHT)) {
  1609.                                         /* Box is a float */
  1610.                                         assert(box->parent &&
  1611.                                                 box->parent->parent &&
  1612.                                                 box->parent->parent->parent);
  1613.  
  1614.                                         containing_block =
  1615.                                                 box->parent->parent->parent;
  1616.                                 } else if (box->parent && box->parent->type !=
  1617.                                                 BOX_INLINE_CONTAINER) {
  1618.                                         /* Box is a block level element */
  1619.                                         containing_block = box->parent;
  1620.                                 } else if (box->parent && box->parent->type ==
  1621.                                                 BOX_INLINE_CONTAINER) {
  1622.                                         /* Box is an inline block */
  1623.                                         assert(box->parent->parent);
  1624.                                         containing_block = box->parent->parent;
  1625.                                 }
  1626.  
  1627.                                 if (containing_block) {
  1628.                                         css_fixed f = 0;
  1629.                                         css_unit u = CSS_UNIT_PX;
  1630.  
  1631.                                         cbhtype = css_computed_height(
  1632.                                                         containing_block->style,
  1633.                                                         &f, &u);
  1634.                                 }
  1635.  
  1636.                                 if (containing_block &&
  1637.                                         containing_block->height != AUTO &&
  1638.                                         (css_computed_position(box->style) ==
  1639.                                                         CSS_POSITION_ABSOLUTE ||
  1640.                                                 cbhtype == CSS_HEIGHT_SET)) {
  1641.                                         /* Box is absolutely positioned or its
  1642.                                          * containing block has a valid
  1643.                                          * specified height.
  1644.                                          * (CSS 2.1 Section 10.5) */
  1645.                                         *height = FPCT_OF_INT_TOINT(value,
  1646.                                                 containing_block->height);
  1647.                                 } else if ((!box->parent ||
  1648.                                                 !box->parent->parent) &&
  1649.                                                 viewport_height >= 0) {
  1650.                                         /* If root element or it's child
  1651.                                          * (HTML or BODY) */
  1652.                                         *height = FPCT_OF_INT_TOINT(value,
  1653.                                                         viewport_height);
  1654.                                 } else {
  1655.                                         /* precentage height not permissible
  1656.                                          * treat height as auto */
  1657.                                         *height = AUTO;
  1658.                                 }
  1659.                         } else {
  1660.                                 *height = FIXTOINT(nscss_len2px(value, unit,
  1661.                                                 style));
  1662.                         }
  1663.                 } else {
  1664.                         *height = AUTO;
  1665.                 }
  1666.  
  1667.                 /* specified gadget heights include borders and padding in
  1668.                  * some cases */
  1669.                 if (box->gadget && *height != AUTO) {
  1670.                         percentage = unit == CSS_UNIT_PCT;
  1671.  
  1672.                         layout_tweak_form_dimensions(box, percentage,
  1673.                                         available_width, false, height);
  1674.                 }
  1675.         }
  1676.  
  1677.         if (max_width) {
  1678.                 enum css_max_width_e type;
  1679.                 css_fixed value = 0;
  1680.                 css_unit unit = CSS_UNIT_PX;
  1681.  
  1682.                 type = css_computed_max_width(style, &value, &unit);
  1683.  
  1684.                 if (type == CSS_MAX_WIDTH_SET) {
  1685.                         if (unit == CSS_UNIT_PCT) {
  1686.                                 *max_width = FPCT_OF_INT_TOINT(value,
  1687.                                                 available_width);
  1688.                         } else {
  1689.                                 *max_width = FIXTOINT(nscss_len2px(value, unit,
  1690.                                                 style));
  1691.                         }
  1692.                 } else {
  1693.                         /* Inadmissible */
  1694.                         *max_width = -1;
  1695.                 }
  1696.  
  1697.                 /* specified gadget widths include borders and padding in some
  1698.                  * cases */
  1699.                 if (box->gadget && *max_width != -1) {
  1700.                         percentage = unit == CSS_UNIT_PCT;
  1701.  
  1702.                         layout_tweak_form_dimensions(box, percentage,
  1703.                                         available_width, true, max_width);
  1704.                 }
  1705.         }
  1706.  
  1707.         if (min_width) {
  1708.                 enum css_min_width_e type;
  1709.                 css_fixed value = 0;
  1710.                 css_unit unit = CSS_UNIT_PX;
  1711.  
  1712.                 type = css_computed_min_width(style, &value, &unit);
  1713.  
  1714.                 if (type == CSS_MIN_WIDTH_SET) {
  1715.                         if (unit == CSS_UNIT_PCT) {
  1716.                                 *min_width = FPCT_OF_INT_TOINT(value,
  1717.                                                 available_width);
  1718.                         } else {
  1719.                                 *min_width = FIXTOINT(nscss_len2px(value, unit,
  1720.                                                 style));
  1721.                         }
  1722.                 } else {
  1723.                         /* Inadmissible */
  1724.                         *min_width = 0;
  1725.                 }
  1726.  
  1727.                 /* specified gadget widths include borders and padding in some
  1728.                  * cases */
  1729.                 if (box->gadget && *min_width != 0) {
  1730.                         percentage = unit == CSS_UNIT_PCT;
  1731.  
  1732.                         layout_tweak_form_dimensions(box, percentage,
  1733.                                         available_width, true, min_width);
  1734.                 }
  1735.         }
  1736.  
  1737.         for (i = 0; i != 4; i++) {
  1738.                 if (margin) {
  1739.                         enum css_margin_e type = CSS_MARGIN_AUTO;
  1740.                         css_fixed value = 0;
  1741.                         css_unit unit = CSS_UNIT_PX;
  1742.  
  1743.                         switch (i) {
  1744.                         case TOP:
  1745.                                 type = css_computed_margin_top(style,
  1746.                                                 &value, &unit);
  1747.                                 break;
  1748.                         case RIGHT:
  1749.                                 type = css_computed_margin_right(style,
  1750.                                                 &value, &unit);
  1751.                                 break;
  1752.                         case BOTTOM:
  1753.                                 type = css_computed_margin_bottom(style,
  1754.                                                 &value, &unit);
  1755.                                 break;
  1756.                         case LEFT:
  1757.                                 type = css_computed_margin_left(style,
  1758.                                                 &value, &unit);
  1759.                                 break;
  1760.                         }
  1761.  
  1762.                         if (type == CSS_MARGIN_SET) {
  1763.                                 if (unit == CSS_UNIT_PCT) {
  1764.                                         margin[i] = FPCT_OF_INT_TOINT(value,
  1765.                                                         available_width);
  1766.                                 } else {
  1767.                                         margin[i] = FIXTOINT(nscss_len2px(value,
  1768.                                                         unit, style));
  1769.                                 }
  1770.                         } else {
  1771.                                 margin[i] = AUTO;
  1772.                         }
  1773.                 }
  1774.  
  1775.                 if (padding) {
  1776.                         css_fixed value = 0;
  1777.                         css_unit unit = CSS_UNIT_PX;
  1778.  
  1779.                         switch (i) {
  1780.                         case TOP:
  1781.                                 css_computed_padding_top(style, &value, &unit);
  1782.                                 break;
  1783.                         case RIGHT:
  1784.                                 css_computed_padding_right(style, &value,
  1785.                                                 &unit);
  1786.                                 break;
  1787.                         case BOTTOM:
  1788.                                 css_computed_padding_bottom(style, &value,
  1789.                                                 &unit);
  1790.                                 break;
  1791.                         case LEFT:
  1792.                                 css_computed_padding_left(style, &value, &unit);
  1793.                                 break;
  1794.                         }
  1795.  
  1796.                         if (unit == CSS_UNIT_PCT) {
  1797.                                 padding[i] = FPCT_OF_INT_TOINT(value,
  1798.                                                 available_width);
  1799.                         } else {
  1800.                                 padding[i] = FIXTOINT(nscss_len2px(value, unit,
  1801.                                                 style));
  1802.                         }
  1803.                 }
  1804.  
  1805.                 /* Table cell borders are populated in table.c */
  1806.                 if (border && box->type != BOX_TABLE_CELL) {
  1807.                         enum css_border_style_e bstyle = CSS_BORDER_STYLE_NONE;
  1808.                         css_color color = 0;
  1809.                         css_fixed value = 0;
  1810.                         css_unit unit = CSS_UNIT_PX;
  1811.  
  1812.                         switch (i) {
  1813.                         case TOP:
  1814.                                 css_computed_border_top_width(style, &value,
  1815.                                                 &unit);
  1816.                                 bstyle = css_computed_border_top_style(style);
  1817.                                 css_computed_border_top_color(style, &color);
  1818.                                 break;
  1819.                         case RIGHT:
  1820.                                 css_computed_border_right_width(style, &value,
  1821.                                                 &unit);
  1822.                                 bstyle = css_computed_border_right_style(style);
  1823.                                 css_computed_border_right_color(style, &color);
  1824.                                 break;
  1825.                         case BOTTOM:
  1826.                                 css_computed_border_bottom_width(style, &value,
  1827.                                                 &unit);
  1828.                                 bstyle = css_computed_border_bottom_style(
  1829.                                                 style);
  1830.                                 css_computed_border_bottom_color(style, &color);
  1831.                                 break;
  1832.                         case LEFT:
  1833.                                 css_computed_border_left_width(style, &value,
  1834.                                                 &unit);
  1835.                                 bstyle = css_computed_border_left_style(style);
  1836.                                 css_computed_border_left_color(style, &color);
  1837.                                 break;
  1838.                         }
  1839.  
  1840.                         border[i].style = bstyle;
  1841.                         border[i].c = color;
  1842.  
  1843.                         if (bstyle == CSS_BORDER_STYLE_HIDDEN ||
  1844.                                         bstyle == CSS_BORDER_STYLE_NONE)
  1845.                                 /* spec unclear: following Mozilla */
  1846.                                 border[i].width = 0;
  1847.                         else
  1848.                                 border[i].width = FIXTOINT(nscss_len2px(value,
  1849.                                                 unit, style));
  1850.  
  1851.                         /* Special case for border-collapse: make all borders
  1852.                          * on table/table-row-group/table-row zero width. */
  1853.                         if (css_computed_border_collapse(style) ==
  1854.                                         CSS_BORDER_COLLAPSE_COLLAPSE &&
  1855.                                         (box->type == BOX_TABLE ||
  1856.                                          box->type == BOX_TABLE_ROW_GROUP ||
  1857.                                          box->type == BOX_TABLE_ROW))
  1858.                                 border[i].width = 0;
  1859.                 }
  1860.         }
  1861. }
  1862.  
  1863.  
  1864. /**
  1865.  * Under some circumstances, specified dimensions for form elements include
  1866.  * borders and padding.
  1867.  *
  1868.  * \param  box              gadget to adjust dimensions of
  1869.  * \param  percentage       whether the gadget has its dimension specified as a
  1870.  *                              percentage
  1871.  * \param  available_width  width of containing block
  1872.  * \param  setwidth         set true if the dimension to be tweaked is a width,
  1873.  *                              else set false for a height
  1874.  * \param  dimension        current value for given width/height dimension.
  1875.  *                              updated to new value after consideration of
  1876.  *                              gadget properties.
  1877.  */
  1878.  
  1879. void layout_tweak_form_dimensions(struct box *box, bool percentage,
  1880.                 int available_width, bool setwidth, int *dimension)
  1881. {
  1882.         int fixed = 0;
  1883.         float frac = 0;
  1884.  
  1885.         assert(box && box->gadget);
  1886.  
  1887.         /* specified gadget widths include borders and padding in some
  1888.          * cases */
  1889.         if (percentage || box->gadget->type == GADGET_SUBMIT ||
  1890.                         box->gadget->type == GADGET_RESET ||
  1891.                         box->gadget->type == GADGET_BUTTON) {
  1892.                 calculate_mbp_width(box->style, setwidth ? LEFT : TOP,
  1893.                                 false, true, true, &fixed, &frac);
  1894.                 calculate_mbp_width(box->style, setwidth ? RIGHT : BOTTOM,
  1895.                                 false, true, true, &fixed, &frac);
  1896.                 *dimension -= frac * available_width + fixed;
  1897.                 *dimension = *dimension > 0 ? *dimension : 0;
  1898.         }
  1899. }
  1900.  
  1901.  
  1902. /**
  1903.  * Find y coordinate which clears all floats on left and/or right.
  1904.  *
  1905.  * \param  fl     first float in float list
  1906.  * \param  clear  type of clear
  1907.  * \return  y coordinate relative to ancestor box for floats
  1908.  */
  1909.  
  1910. int layout_clear(struct box *fl, enum css_clear_e clear)
  1911. {
  1912.         int y = 0;
  1913.         for (; fl; fl = fl->next_float) {
  1914.                 if ((clear == CSS_CLEAR_LEFT || clear == CSS_CLEAR_BOTH) &&
  1915.                                 fl->type == BOX_FLOAT_LEFT)
  1916.                         if (y < fl->y + fl->height)
  1917.                                 y = fl->y + fl->height;
  1918.                 if ((clear == CSS_CLEAR_RIGHT || clear == CSS_CLEAR_BOTH) &&
  1919.                                 fl->type == BOX_FLOAT_RIGHT)
  1920.                         if (y < fl->y + fl->height)
  1921.                                 y = fl->y + fl->height;
  1922.         }
  1923.         return y;
  1924. }
  1925.  
  1926.  
  1927. /**
  1928.  * Find left and right edges in a vertical range.
  1929.  *
  1930.  * \param  fl     first float in float list
  1931.  * \param  y0     start of y range to search
  1932.  * \param  y1     end of y range to search
  1933.  * \param  x0     start left edge, updated to available left edge
  1934.  * \param  x1     start right edge, updated to available right edge
  1935.  * \param  left   returns float on left if present
  1936.  * \param  right  returns float on right if present
  1937.  */
  1938.  
  1939. void find_sides(struct box *fl, int y0, int y1,
  1940.                 int *x0, int *x1, struct box **left, struct box **right)
  1941. {
  1942.         int fy0, fy1, fx0, fx1;
  1943.  
  1944. #ifdef LAYOUT_DEBUG
  1945.         LOG(("y0 %i, y1 %i, x0 %i, x1 %i", y0, y1, *x0, *x1));
  1946. #endif
  1947.  
  1948.         *left = *right = 0;
  1949.         for (; fl; fl = fl->next_float) {
  1950.                 fy0 = fl->y;
  1951.                 fy1 = fl->y + fl->height;
  1952.                 if (y0 < fy1 && fy0 <= y1) {
  1953.                         if (fl->type == BOX_FLOAT_LEFT) {
  1954.                                 fx1 = fl->x + fl->width;
  1955.                                 if (*x0 < fx1) {
  1956.                                         *x0 = fx1;
  1957.                                         *left = fl;
  1958.                                 }
  1959.                         } else if (fl->type == BOX_FLOAT_RIGHT) {
  1960.                                 fx0 = fl->x;
  1961.                                 if (fx0 < *x1) {
  1962.                                         *x1 = fx0;
  1963.                                         *right = fl;
  1964.                                 }
  1965.                         }
  1966.                 }
  1967.         }
  1968.  
  1969. #ifdef LAYOUT_DEBUG
  1970.         LOG(("x0 %i, x1 %i, left %p, right %p", *x0, *x1, *left, *right));
  1971. #endif
  1972. }
  1973.  
  1974.  
  1975. /**
  1976.  * Layout lines of text or inline boxes with floats.
  1977.  *
  1978.  * \param  box    inline container
  1979.  * \param  width  horizontal space available
  1980.  * \param  cont   ancestor box which defines horizontal space, for floats
  1981.  * \param  cx     box position relative to cont
  1982.  * \param  cy     box position relative to cont
  1983.  * \param  content  memory pool for any new boxes
  1984.  * \return  true on success, false on memory exhaustion
  1985.  */
  1986.  
  1987. bool layout_inline_container(struct box *inline_container, int width,
  1988.                 struct box *cont, int cx, int cy, html_content *content)
  1989. {
  1990.         bool first_line = true;
  1991.         bool has_text_children;
  1992.         struct box *c, *next;
  1993.         int y = 0;
  1994.         int curwidth,maxwidth = width;
  1995.  
  1996.         assert(inline_container->type == BOX_INLINE_CONTAINER);
  1997.  
  1998. #ifdef LAYOUT_DEBUG
  1999.         LOG(("inline_container %p, width %i, cont %p, cx %i, cy %i",
  2000.                         inline_container, width, cont, cx, cy));
  2001. #endif
  2002.  
  2003.         has_text_children = false;
  2004.         for (c = inline_container->children; c; c = c->next) {
  2005.                 bool is_pre = false;
  2006.  
  2007.                 if (c->style) {
  2008.                         enum css_white_space_e whitespace;
  2009.  
  2010.                         whitespace = css_computed_white_space(c->style);
  2011.  
  2012.                         is_pre = (whitespace == CSS_WHITE_SPACE_PRE ||
  2013.                                 whitespace == CSS_WHITE_SPACE_PRE_LINE ||
  2014.                                 whitespace == CSS_WHITE_SPACE_PRE_WRAP);
  2015.                 }
  2016.  
  2017.                 if ((!c->object && !(c->flags & REPLACE_DIM) &&
  2018.                                 !(c->flags & IFRAME) &&
  2019.                                 c->text && (c->length || is_pre)) ||
  2020.                                 c->type == BOX_BR)
  2021.                         has_text_children = true;
  2022.         }
  2023.  
  2024.         /** \todo fix wrapping so that a box with horizontal scrollbar will
  2025.          * shrink back to 'width' if no word is wider than 'width' (Or just set
  2026.          * curwidth = width and have the multiword lines wrap to the min width)
  2027.          */
  2028.         for (c = inline_container->children; c; ) {
  2029. #ifdef LAYOUT_DEBUG
  2030.                 LOG(("c %p", c));
  2031. #endif
  2032.                 curwidth = inline_container->width;
  2033.                 if (!layout_line(c, &curwidth, &y, cx, cy + y, cont, first_line,
  2034.                                 has_text_children, content, &next))
  2035.                         return false;
  2036.                 maxwidth = max(maxwidth,curwidth);
  2037.                 c = next;
  2038.                 first_line = false;
  2039.         }
  2040.  
  2041.         inline_container->width = maxwidth;
  2042.         inline_container->height = y;
  2043.  
  2044.         return true;
  2045. }
  2046.  
  2047.  
  2048. /**
  2049.  * Calculate minimum and maximum width of an inline container.
  2050.  *
  2051.  * \param  inline_container  box of type INLINE_CONTAINER
  2052.  * \post  inline_container->min_width and inline_container->max_width filled in,
  2053.  *        0 <= inline_container->min_width <= inline_container->max_width
  2054.  */
  2055.  
  2056. void layout_minmax_inline_container(struct box *inline_container,
  2057.                 bool *has_height, const struct font_functions *font_func)
  2058. {
  2059.         struct box *child;
  2060.         int line_min = 0, line_max = 0;
  2061.         int min = 0, max = 0;
  2062.         bool first_line = true;
  2063.         bool line_has_height;
  2064.  
  2065.         assert(inline_container->type == BOX_INLINE_CONTAINER);
  2066.  
  2067.         /* check if the widths have already been calculated */
  2068.         if (inline_container->max_width != UNKNOWN_MAX_WIDTH)
  2069.                 return;
  2070.  
  2071.         *has_height = false;
  2072.  
  2073.         for (child = inline_container->children; child; ) {
  2074.                 child = layout_minmax_line(child, &line_min, &line_max,
  2075.                                 first_line, &line_has_height, font_func);
  2076.                 if (min < line_min)
  2077.                         min = line_min;
  2078.                 if (max < line_max)
  2079.                         max = line_max;
  2080.                 first_line = false;
  2081.                 *has_height |= line_has_height;
  2082.         }
  2083.  
  2084.         inline_container->min_width = min;
  2085.         inline_container->max_width = max;
  2086.  
  2087.         assert(0 <= inline_container->min_width &&
  2088.                         inline_container->min_width <=
  2089.                         inline_container->max_width);
  2090. }
  2091.  
  2092.  
  2093. /**
  2094.  * Calculate line height from a style.
  2095.  */
  2096.  
  2097. int line_height(const css_computed_style *style)
  2098. {
  2099.         enum css_line_height_e lhtype;
  2100.         css_fixed lhvalue = 0;
  2101.         css_unit lhunit = CSS_UNIT_PX;
  2102.         css_fixed line_height;
  2103.  
  2104.         assert(style);
  2105.  
  2106.         lhtype = css_computed_line_height(style, &lhvalue, &lhunit);
  2107.         if (lhtype == CSS_LINE_HEIGHT_NORMAL) {
  2108.                 /* Normal => use a constant of 1.3 * font-size */
  2109.                 lhvalue = FLTTOFIX(1.3);
  2110.                 lhtype = CSS_LINE_HEIGHT_NUMBER;
  2111.         }
  2112.  
  2113.         if (lhtype == CSS_LINE_HEIGHT_NUMBER ||
  2114.                         lhunit == CSS_UNIT_PCT) {
  2115.                 line_height = nscss_len2px(lhvalue, CSS_UNIT_EM, style);
  2116.  
  2117.                 if (lhtype != CSS_LINE_HEIGHT_NUMBER)
  2118.                         line_height = FDIV(line_height, F_100);
  2119.         } else {
  2120.                 assert(lhunit != CSS_UNIT_PCT);
  2121.  
  2122.                 line_height = nscss_len2px(lhvalue, lhunit, style);
  2123.         }
  2124.  
  2125.         return FIXTOINT(line_height);
  2126. }
  2127.  
  2128.  
  2129. /**
  2130.  * Split a text box.
  2131.  *
  2132.  * \param  content     memory pool for any new boxes
  2133.  * \param  fstyle      style for text in text box
  2134.  * \param  split_box   box with text to split
  2135.  * \param  new_length  new length for text in split_box, after splitting
  2136.  * \param  new_width   new width for text in split_box, after splitting
  2137.  * \return  true on success, false on memory exhaustion
  2138.  *
  2139.  * A new box is created and inserted into the box tree after split_box,
  2140.  * containing the text after new_length excluding the initial space character.
  2141.  */
  2142.  
  2143. static bool layout_text_box_split(html_content *content,
  2144.                 plot_font_style_t *fstyle, struct box *split_box,
  2145.                 size_t new_length, int new_width)
  2146. {
  2147.         int space_width = split_box->space;
  2148.         struct box *c2;
  2149.         const struct font_functions *font_func = content->font_func;
  2150.  
  2151.         if (space_width == 0) {
  2152.                 /* Currently split_box has no space. */
  2153.                 /* Get the space width because the split_box will need it */
  2154.                 /* Don't set it in split_box yet, or it will get cloned. */
  2155.                 font_func->font_width(fstyle, " ", 1, &space_width);
  2156.         } else if (space_width == UNKNOWN_WIDTH) {
  2157.                 /* Split_box has a space but its width is unknown. */
  2158.                 /* Get the space width because the split_box will need it */
  2159.                 /* Set it in split_box, so it gets cloned. */
  2160.                 font_func->font_width(fstyle, " ", 1, &space_width);
  2161.                 split_box->space = space_width;
  2162.         }
  2163.  
  2164.         /* Create clone of split_box, c2 */
  2165.         c2 = talloc_memdup(content->bctx, split_box, sizeof *c2);
  2166.         if (!c2)
  2167.                 return false;
  2168.         c2->flags |= CLONE;
  2169.  
  2170.         /* Set remaining text in c2 */
  2171.         if (split_box->parent->parent->gadget != NULL) {
  2172.                 /* Inside a form text input / textarea, special case */
  2173.                 /* TODO: Move text inputs to core textarea widget and remove
  2174.                  *       this */
  2175.                 c2->text = talloc_strndup(content->bctx,
  2176.                                 split_box->text + new_length + 1,
  2177.                                 split_box->length - (new_length + 1));
  2178.                 if (!c2->text)
  2179.                         return false;
  2180.         } else {
  2181.                 c2->text += new_length + 1;
  2182.         }
  2183.  
  2184.         /* Set c2 according to the remaining text */
  2185.         c2->width -= new_width + space_width;
  2186.         c2->flags &= ~MEASURED; /* width has been estimated */
  2187.         c2->length = split_box->length - (new_length + 1);
  2188.  
  2189.         /* Update split_box for its reduced text */
  2190.         split_box->width = new_width;
  2191.         split_box->flags |= MEASURED;
  2192.         split_box->length = new_length;
  2193.         split_box->space = space_width;
  2194.  
  2195.         /* Insert c2 into box list */
  2196.         c2->next = split_box->next;
  2197.         split_box->next = c2;
  2198.         c2->prev = split_box;
  2199.         if (c2->next)
  2200.                 c2->next->prev = c2;
  2201.         else
  2202.                 c2->parent->last = c2;
  2203.  
  2204.         return true;
  2205. }
  2206.  
  2207.  
  2208. /**
  2209.  * Position a line of boxes in inline formatting context.
  2210.  *
  2211.  * \param  first   box at start of line
  2212.  * \param  width   available width on input, updated with actual width on output
  2213.  *                 (may be incorrect if the line gets split?)
  2214.  * \param  y       coordinate of top of line, updated on exit to bottom
  2215.  * \param  cx      coordinate of left of line relative to cont
  2216.  * \param  cy      coordinate of top of line relative to cont
  2217.  * \param  cont    ancestor box which defines horizontal space, for floats
  2218.  * \param  indent  apply any first-line indent
  2219.  * \param  has_text_children  at least one TEXT in the inline_container
  2220.  * \param  next_box  updated to first box for next line, or 0 at end
  2221.  * \param  content  memory pool for any new boxes
  2222.  * \return  true on success, false on memory exhaustion
  2223.  */
  2224.  
  2225. bool layout_line(struct box *first, int *width, int *y,
  2226.                 int cx, int cy, struct box *cont, bool indent,
  2227.                 bool has_text_children,
  2228.                 html_content *content, struct box **next_box)
  2229. {
  2230.         int height, used_height;
  2231.         int x0 = 0;
  2232.         int x1 = *width;
  2233.         int x, h, x_previous;
  2234.         int fy = cy;
  2235.         struct box *left;
  2236.         struct box *right;
  2237.         struct box *b;
  2238.         struct box *split_box = 0;
  2239.         struct box *d;
  2240.         struct box *br_box = 0;
  2241.         bool move_y = false;
  2242.         bool place_below = false;
  2243.         int space_before = 0, space_after = 0;
  2244.         unsigned int inline_count = 0;
  2245.         unsigned int i;
  2246.         const struct font_functions *font_func = content->font_func;
  2247.         plot_font_style_t fstyle;
  2248.  
  2249. #ifdef LAYOUT_DEBUG
  2250.         LOG(("first %p, first->text '%.*s', width %i, y %i, cx %i, cy %i",
  2251.                         first, (int) first->length, first->text, *width,
  2252.                         *y, cx, cy));
  2253. #endif
  2254.  
  2255.         /* find sides at top of line */
  2256.         x0 += cx;
  2257.         x1 += cx;
  2258.         find_sides(cont->float_children, cy, cy, &x0, &x1, &left, &right);
  2259.         x0 -= cx;
  2260.         x1 -= cx;
  2261.  
  2262.         if (indent)
  2263.                 x0 += layout_text_indent(first->parent->parent->style, *width);
  2264.  
  2265.         if (x1 < x0)
  2266.                 x1 = x0;
  2267.  
  2268.         /* get minimum line height from containing block.
  2269.          * this is the line-height if there are text children and also in the
  2270.          * case of an initially empty text input */
  2271.         if (has_text_children || first->parent->parent->gadget)
  2272.                 used_height = height =
  2273.                                 line_height(first->parent->parent->style);
  2274.         else
  2275.                 /* inline containers with no text are usually for layout and
  2276.                  * look better with no minimum line-height */
  2277.                 used_height = height = 0;
  2278.  
  2279.         /* pass 1: find height of line assuming sides at top of line: loop
  2280.          * body executed at least once
  2281.          * keep in sync with the loop in layout_minmax_line() */
  2282. #ifdef LAYOUT_DEBUG
  2283.         LOG(("x0 %i, x1 %i, x1 - x0 %i", x0, x1, x1 - x0));
  2284. #endif
  2285.  
  2286.         for (x = 0, b = first; x <= x1 - x0 && b != 0; b = b->next) {
  2287.                 int min_width, max_width;
  2288.  
  2289.                 assert(b->type == BOX_INLINE || b->type == BOX_INLINE_BLOCK ||
  2290.                                 b->type == BOX_FLOAT_LEFT ||
  2291.                                 b->type == BOX_FLOAT_RIGHT ||
  2292.                                 b->type == BOX_BR || b->type == BOX_TEXT ||
  2293.                                 b->type == BOX_INLINE_END);
  2294.  
  2295. #ifdef LAYOUT_DEBUG
  2296.                 LOG(("pass 1: b %p, x %i", b, x));
  2297. #endif
  2298.  
  2299.                 if (b->type == BOX_BR)
  2300.                         break;
  2301.  
  2302.                 if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT)
  2303.                         continue;
  2304.                 if (b->type == BOX_INLINE_BLOCK &&
  2305.                                 (css_computed_position(b->style) ==
  2306.                                                 CSS_POSITION_ABSOLUTE ||
  2307.                                  css_computed_position(b->style) ==
  2308.                                                 CSS_POSITION_FIXED))
  2309.                         continue;
  2310.  
  2311.                 assert(b->style != NULL);
  2312.                 font_plot_style_from_css(b->style, &fstyle);
  2313.  
  2314.                 x += space_after;
  2315.  
  2316.                 if (b->type == BOX_INLINE_BLOCK) {
  2317.                         if (b->max_width != UNKNOWN_WIDTH)
  2318.                                 if (!layout_float(b, *width, content))
  2319.                                         return false;
  2320.                         h = b->border[TOP].width + b->padding[TOP] + b->height +
  2321.                                         b->padding[BOTTOM] +
  2322.                                         b->border[BOTTOM].width;
  2323.                         if (height < h)
  2324.                                 height = h;
  2325.                         x += b->margin[LEFT] + b->border[LEFT].width +
  2326.                                         b->padding[LEFT] + b->width +
  2327.                                         b->padding[RIGHT] +
  2328.                                         b->border[RIGHT].width +
  2329.                                         b->margin[RIGHT];
  2330.                         space_after = 0;
  2331.                         continue;
  2332.                 }
  2333.  
  2334.                 if (b->type == BOX_INLINE) {
  2335.                         /* calculate borders, margins, and padding */
  2336.                         layout_find_dimensions(*width, -1, b, b->style, 0, 0,
  2337.                                         0, 0, b->margin, b->padding, b->border);
  2338.                         for (i = 0; i != 4; i++)
  2339.                                 if (b->margin[i] == AUTO)
  2340.                                         b->margin[i] = 0;
  2341.                         x += b->margin[LEFT] + b->border[LEFT].width +
  2342.                                         b->padding[LEFT];
  2343.                         if (b->inline_end) {
  2344.                                 b->inline_end->margin[RIGHT] = b->margin[RIGHT];
  2345.                                 b->inline_end->padding[RIGHT] =
  2346.                                                 b->padding[RIGHT];
  2347.                                 b->inline_end->border[RIGHT] =
  2348.                                                 b->border[RIGHT];
  2349.                         } else {
  2350.                                 x += b->padding[RIGHT] +
  2351.                                                 b->border[RIGHT].width +
  2352.                                                 b->margin[RIGHT];
  2353.                         }
  2354.                 } else if (b->type == BOX_INLINE_END) {
  2355.                         b->width = 0;
  2356.                         if (b->space == UNKNOWN_WIDTH) {
  2357.                                 font_func->font_width(&fstyle, " ", 1,
  2358.                                                 &b->space);
  2359.                                 /** \todo handle errors */
  2360.                         }
  2361.                         space_after = b->space;
  2362.  
  2363.                         x += b->padding[RIGHT] + b->border[RIGHT].width +
  2364.                                         b->margin[RIGHT];
  2365.                         continue;
  2366.                 }
  2367.  
  2368.                 if (!b->object && !(b->flags & IFRAME) && !b->gadget &&
  2369.                                 !(b->flags & REPLACE_DIM)) {
  2370.                         /* inline non-replaced, 10.3.1 and 10.6.1 */
  2371.                         b->height = line_height(b->style ? b->style :
  2372.                                         b->parent->parent->style);
  2373.                         if (height < b->height)
  2374.                                 height = b->height;
  2375.  
  2376.                         if (!b->text) {
  2377.                                 b->width = 0;
  2378.                                 space_after = 0;
  2379.                                 continue;
  2380.                         }
  2381.  
  2382.                         if (b->width == UNKNOWN_WIDTH) {
  2383.                                 /** \todo handle errors */
  2384.  
  2385.                                 /* If it's a select element, we must use the
  2386.                                  * width of the widest option text */
  2387.                                 if (b->parent->parent->gadget &&
  2388.                                                 b->parent->parent->gadget->type
  2389.                                                 == GADGET_SELECT) {
  2390.                                         int opt_maxwidth = 0;
  2391.                                         struct form_option *o;
  2392.  
  2393.                                         for (o = b->parent->parent->gadget->
  2394.                                                         data.select.items; o;
  2395.                                                         o = o->next) {
  2396.                                                 int opt_width;
  2397.                                                 font_func->font_width(&fstyle,
  2398.                                                                 o->text,
  2399.                                                                 strlen(o->text),
  2400.                                                                 &opt_width);
  2401.  
  2402.                                                 if (opt_maxwidth < opt_width)
  2403.                                                         opt_maxwidth =opt_width;
  2404.                                         }
  2405.                                         b->width = opt_maxwidth;
  2406.                                         if (nsoption_bool(core_select_menu))
  2407.                                                 b->width += SCROLLBAR_WIDTH;
  2408.                                 } else {
  2409.                                         font_func->font_width(&fstyle, b->text,
  2410.                                                         b->length, &b->width);
  2411.                                         b->flags |= MEASURED;
  2412.                                 }
  2413.                         }
  2414.  
  2415.                         /* If the current text has not been measured (i.e. its
  2416.                          * width was estimated after splitting), and it fits on
  2417.                          * the line, measure it properly, so next box is placed
  2418.                          * correctly. */
  2419.                         if (b->text && (x + b->width < x1 - x0) &&
  2420.                                         !(b->flags & MEASURED) &&
  2421.                                         b->next) {
  2422.                                 font_func->font_width(&fstyle, b->text,
  2423.                                                 b->length, &b->width);
  2424.                                 b->flags |= MEASURED;
  2425.                         }
  2426.  
  2427.                         x += b->width;
  2428.                         if (b->space == UNKNOWN_WIDTH) {
  2429.                                 font_func->font_width(&fstyle, " ", 1,
  2430.                                                 &b->space);
  2431.                                 /** \todo handle errors */
  2432.                         }
  2433.                         space_after = b->space;
  2434.                         continue;
  2435.                 }
  2436.  
  2437.                 space_after = 0;
  2438.  
  2439.                 /* inline replaced, 10.3.2 and 10.6.2 */
  2440.                 assert(b->style);
  2441.  
  2442.                 layout_find_dimensions(*width, -1, b, b->style,
  2443.                                 &b->width, &b->height, &max_width, &min_width,
  2444.                                 NULL, NULL, NULL);
  2445.  
  2446.                 if (b->object && !(b->flags & REPLACE_DIM)) {
  2447.                         layout_get_object_dimensions(b, &b->width, &b->height,
  2448.                                         min_width, max_width);
  2449.                 } else if (b->flags & IFRAME) {
  2450.                         /* TODO: should we look at the content dimensions? */
  2451.                         if (b->width == AUTO)
  2452.                                 b->width = 400;
  2453.                         if (b->height == AUTO)
  2454.                                 b->height = 300;
  2455.  
  2456.                         /* We reformat the iframe browser window to new
  2457.                          * dimensions in pass 2 */
  2458.                 } else {
  2459.                         /* form control with no object */
  2460.                         if (b->width == AUTO)
  2461.                                 b->width = FIXTOINT(nscss_len2px(INTTOFIX(1),
  2462.                                                 CSS_UNIT_EM, b->style));
  2463.                         if (b->height == AUTO)
  2464.                                 b->height = FIXTOINT(nscss_len2px(INTTOFIX(1),
  2465.                                                 CSS_UNIT_EM, b->style));
  2466.                 }
  2467.  
  2468.                 /* Reformat object to new box size */
  2469.                 if (b->object && content_get_type(b->object) == CONTENT_HTML &&
  2470.                                 b->width !=
  2471.                                 content_get_available_width(b->object)) {
  2472.                         css_fixed value = 0;
  2473.                         css_unit unit = CSS_UNIT_PX;
  2474.                         enum css_height_e htype = css_computed_height(b->style,
  2475.                                         &value, &unit);
  2476.  
  2477.                         content_reformat(b->object, false, b->width, b->height);
  2478.  
  2479.                         if (htype == CSS_HEIGHT_AUTO)
  2480.                                 b->height = content_get_height(b->object);
  2481.                 }
  2482.  
  2483.                 if (height < b->height)
  2484.                         height = b->height;
  2485.  
  2486.                 x += b->width;
  2487.         }
  2488.  
  2489.         /* find new sides using this height */
  2490.         x0 = cx;
  2491.         x1 = cx + *width;
  2492.         find_sides(cont->float_children, cy, cy + height, &x0, &x1,
  2493.                         &left, &right);
  2494.         x0 -= cx;
  2495.         x1 -= cx;
  2496.  
  2497.         if (indent)
  2498.                 x0 += layout_text_indent(first->parent->parent->style, *width);
  2499.  
  2500.         if (x1 < x0)
  2501.                 x1 = x0;
  2502.  
  2503.         space_after = space_before = 0;
  2504.  
  2505.         /* pass 2: place boxes in line: loop body executed at least once */
  2506. #ifdef LAYOUT_DEBUG
  2507.         LOG(("x0 %i, x1 %i, x1 - x0 %i", x0, x1, x1 - x0));
  2508. #endif
  2509.  
  2510.         for (x = x_previous = 0, b = first; x <= x1 - x0 && b; b = b->next) {
  2511. #ifdef LAYOUT_DEBUG
  2512.                 LOG(("pass 2: b %p, x %i", b, x));
  2513. #endif
  2514.  
  2515.                 if (b->type == BOX_INLINE_BLOCK &&
  2516.                                 (css_computed_position(b->style) ==
  2517.                                                 CSS_POSITION_ABSOLUTE ||
  2518.                                  css_computed_position(b->style) ==
  2519.                                                 CSS_POSITION_FIXED)) {
  2520.                         b->x = x + space_after;
  2521.  
  2522.                 } else if (b->type == BOX_INLINE ||
  2523.                                 b->type == BOX_INLINE_BLOCK ||
  2524.                                 b->type == BOX_TEXT ||
  2525.                                 b->type == BOX_INLINE_END) {
  2526.                         assert(b->width != UNKNOWN_WIDTH);
  2527.  
  2528.                         x_previous = x;
  2529.                         x += space_after;
  2530.                         b->x = x;
  2531.  
  2532.                         if ((b->type == BOX_INLINE && !b->inline_end) ||
  2533.                                         b->type == BOX_INLINE_BLOCK) {
  2534.                                 b->x += b->margin[LEFT] + b->border[LEFT].width;
  2535.                                 x = b->x + b->padding[LEFT] + b->width +
  2536.                                                 b->padding[RIGHT] +
  2537.                                                 b->border[RIGHT].width +
  2538.                                                 b->margin[RIGHT];
  2539.                         } else if (b->type == BOX_INLINE) {
  2540.                                 b->x += b->margin[LEFT] + b->border[LEFT].width;
  2541.                                 x = b->x + b->padding[LEFT] + b->width;
  2542.                         } else if (b->type == BOX_INLINE_END) {
  2543.                                 b->height = b->inline_end->height;
  2544.                                 x += b->padding[RIGHT] +
  2545.                                                 b->border[RIGHT].width +
  2546.                                                 b->margin[RIGHT];
  2547.                         } else {
  2548.                                 x += b->width;
  2549.                         }
  2550.  
  2551.                         space_before = space_after;
  2552.                         if (b->object || b->flags & REPLACE_DIM ||
  2553.                                         b->flags & IFRAME)
  2554.                                 space_after = 0;
  2555.                         else if (b->text || b->type == BOX_INLINE_END) {
  2556.                                 if (b->space == UNKNOWN_WIDTH) {
  2557.                                         font_plot_style_from_css(b->style,
  2558.                                                         &fstyle);
  2559.                                         /** \todo handle errors */
  2560.                                         font_func->font_width(&fstyle, " ", 1,
  2561.                                                         &b->space);
  2562.                                 }
  2563.                                 space_after = b->space;
  2564.                         } else {
  2565.                                 space_after = 0;
  2566.                         }
  2567.                         split_box = b;
  2568.                         move_y = true;
  2569.                         inline_count++;
  2570.                 } else if (b->type == BOX_BR) {
  2571.                         b->x = x;
  2572.                         b->width = 0;
  2573.                         br_box = b;
  2574.                         b = b->next;
  2575.                         split_box = 0;
  2576.                         move_y = true;
  2577.                         break;
  2578.  
  2579.                 } else {
  2580.                         /* float */
  2581. #ifdef LAYOUT_DEBUG
  2582.                         LOG(("float %p", b));
  2583. #endif
  2584.  
  2585.                         d = b->children;
  2586.                         d->float_children = 0;
  2587.                         b->float_container = d->float_container = cont;
  2588.  
  2589.                         if (!layout_float(d, *width, content))
  2590.                                 return false;
  2591.  
  2592. #ifdef LAYOUT_DEBUG
  2593.                         LOG(("%p : %d %d", d, d->margin[TOP],
  2594.                                         d->border[TOP].width));
  2595. #endif
  2596.  
  2597.                         d->x = d->margin[LEFT] + d->border[LEFT].width;
  2598.                         d->y = d->margin[TOP] + d->border[TOP].width;
  2599.                         b->width = d->margin[LEFT] + d->border[LEFT].width +
  2600.                                         d->padding[LEFT] + d->width +
  2601.                                         d->padding[RIGHT] +
  2602.                                         d->border[RIGHT].width +
  2603.                                         d->margin[RIGHT];
  2604.                         b->height = d->margin[TOP] + d->border[TOP].width +
  2605.                                         d->padding[TOP] + d->height +
  2606.                                         d->padding[BOTTOM] +
  2607.                                         d->border[BOTTOM].width +
  2608.                                         d->margin[BOTTOM];
  2609.  
  2610.                         if (b->width > (x1 - x0) - x)
  2611.                                 place_below = true;
  2612.                         if (d->style && (css_computed_clear(d->style) ==
  2613.                                                 CSS_CLEAR_NONE ||
  2614.                                         (css_computed_clear(d->style) ==
  2615.                                                 CSS_CLEAR_LEFT && left == 0) ||
  2616.                                         (css_computed_clear(d->style) ==
  2617.                                                 CSS_CLEAR_RIGHT &&
  2618.                                                 right == 0) ||
  2619.                                         (css_computed_clear(d->style) ==
  2620.                                                 CSS_CLEAR_BOTH &&
  2621.                                                 left == 0 && right == 0)) &&
  2622.                                         (!place_below ||
  2623.                                         (left == 0 && right == 0 && x == 0)) &&
  2624.                                         cy >= cont->clear_level) {
  2625.                                 /* + not cleared or,
  2626.                                  *   cleared and there are no floats to clear
  2627.                                  * + fits without needing to be placed below or,
  2628.                                  *   this line is empty with no floats
  2629.                                  * + current y, cy, is below the clear level
  2630.                                  *
  2631.                                  * Float affects current line */
  2632.                                 if (b->type == BOX_FLOAT_LEFT) {
  2633.                                         b->x = cx + x0;
  2634.                                         if (b->width > 0)
  2635.                                                 x0 += b->width;
  2636.                                         left = b;
  2637.                                 } else {
  2638.                                         b->x = cx + x1 - b->width;
  2639.                                         if (b->width > 0)
  2640.                                                 x1 -= b->width;
  2641.                                         right = b;
  2642.                                 }
  2643.                                 b->y = cy;
  2644.                         } else {
  2645.                                 /* cleared or doesn't fit on line */
  2646.                                 /* place below into next available space */
  2647.                                 int fcy = (cy > cont->clear_level) ? cy :
  2648.                                                 cont->clear_level;
  2649.                                 fy = (fy > fcy) ? fy : fcy;
  2650.                                 fy = (fy == cy) ? fy + height : fy;
  2651.  
  2652.                                 place_float_below(b, *width, cx, fy, cont);
  2653.                                 fy = b->y;
  2654.                                 if (d->style && (
  2655.                                                 (css_computed_clear(d->style) ==
  2656.                                                 CSS_CLEAR_LEFT && left != 0) ||
  2657.                                                 (css_computed_clear(d->style) ==
  2658.                                                 CSS_CLEAR_RIGHT &&
  2659.                                                 right != 0) ||
  2660.                                                 (css_computed_clear(d->style) ==
  2661.                                                 CSS_CLEAR_BOTH &&
  2662.                                                 (left != 0 || right != 0)))) {
  2663.                                         /* to be cleared below existing
  2664.                                          * floats */
  2665.                                         if (b->type == BOX_FLOAT_LEFT)
  2666.                                                 b->x = cx;
  2667.                                         else
  2668.                                                 b->x = cx + *width - b->width;
  2669.  
  2670.                                         fcy = layout_clear(cont->float_children,
  2671.                                                 css_computed_clear(d->style));
  2672.                                         if (fcy > cont->clear_level)
  2673.                                                 cont->clear_level = fcy;
  2674.                                         if (b->y < fcy)
  2675.                                                 b->y = fcy;
  2676.                                 }
  2677.                                 if (b->type == BOX_FLOAT_LEFT)
  2678.                                         left = b;
  2679.                                 else
  2680.                                         right = b;
  2681.                         }
  2682.                         if (cont->float_children == b) {
  2683. #ifdef LAYOUT_DEBUG
  2684.                                 LOG(("float %p already placed", b));
  2685. #endif
  2686.  
  2687.                                 box_dump(stderr, cont, 0);
  2688.                                 assert(0);
  2689.                         }
  2690.                         b->next_float = cont->float_children;
  2691.                         cont->float_children = b;
  2692.  
  2693.                         split_box = 0;
  2694.                 }
  2695.         }
  2696.  
  2697.         if (x1 - x0 < x && split_box) {
  2698.                 /* the last box went over the end */
  2699.                 unsigned int i;
  2700.                 size_t space = 0;
  2701.                 int w;
  2702.                 bool no_wrap = css_computed_white_space(
  2703.                                 split_box->style) == CSS_WHITE_SPACE_NOWRAP ||
  2704.                                 css_computed_white_space(
  2705.                                 split_box->style) == CSS_WHITE_SPACE_PRE;
  2706.  
  2707.                 x = x_previous;
  2708.  
  2709.                 if ((split_box->type == BOX_INLINE ||
  2710.                                 split_box->type == BOX_TEXT) &&
  2711.                                 !split_box->object &&
  2712.                                 !(split_box->flags & REPLACE_DIM) &&
  2713.                                 !(split_box->flags & IFRAME) &&
  2714.                                 !split_box->gadget && split_box->text) {
  2715.                         /* skip leading spaces, otherwise code gets fooled into
  2716.                          * thinking it's all one long word */
  2717.                         for (i = 0; i != split_box->length &&
  2718.                                         split_box->text[i] == ' '; i++)
  2719.                                 ;
  2720.                         /* find end of word */
  2721.                         for (; i != split_box->length &&
  2722.                                         split_box->text[i] != ' '; i++)
  2723.                                 ;
  2724.                         if (i != split_box->length)
  2725.                                 space = i;
  2726.                 }
  2727.  
  2728.                 /* space != 0 implies split_box->text != 0 */
  2729.  
  2730.                 if (space == 0 || no_wrap)
  2731.                         w = split_box->width;
  2732.                 else {
  2733.                         font_plot_style_from_css(split_box->style, &fstyle);
  2734.                         /** \todo handle errors */
  2735.                         font_func->font_width(&fstyle, split_box->text,
  2736.                                         space, &w);
  2737.                 }
  2738.  
  2739. #ifdef LAYOUT_DEBUG
  2740.                 LOG(("splitting: split_box %p \"%.*s\", space %zu, w %i, "
  2741.                                 "left %p, right %p, inline_count %u",
  2742.                                 split_box, (int) split_box->length,
  2743.                                 split_box->text, space, w,
  2744.                                 left, right, inline_count));
  2745. #endif
  2746.  
  2747.                 if ((space == 0 || x1 - x0 <= x + space_before + w) &&
  2748.                                 !left && !right && inline_count == 1) {
  2749.                         /* first word of box doesn't fit, but no floats and
  2750.                          * first box on line so force in */
  2751.                         if (space == 0 || no_wrap) {
  2752.                                 /* only one word in this box, or not text
  2753.                                  * or white-space:nowrap */
  2754.                                 b = split_box->next;
  2755.                         } else {
  2756.                                 /* cut off first word for this line */
  2757.                                 if (!layout_text_box_split(content, &fstyle,
  2758.                                                 split_box, space, w))
  2759.                                         return false;
  2760.                                 b = split_box->next;
  2761.                         }
  2762.                         x += space_before + w;
  2763. #ifdef LAYOUT_DEBUG
  2764.                         LOG(("forcing"));
  2765. #endif
  2766.                 } else if ((space == 0 || x1 - x0 <= x + space_before + w) &&
  2767.                                 inline_count == 1) {
  2768.                         /* first word of first box doesn't fit, but a float is
  2769.                          * taking some of the width so move below it */
  2770.                         assert(left || right);
  2771.                         used_height = 0;
  2772.                         if (left) {
  2773. #ifdef LAYOUT_DEBUG
  2774.                                 LOG(("cy %i, left->y %i, left->height %i",
  2775.                                                 cy, left->y, left->height));
  2776. #endif
  2777.                                 used_height = left->y + left->height - cy + 1;
  2778. #ifdef LAYOUT_DEBUG
  2779.                                 LOG(("used_height %i", used_height));
  2780. #endif
  2781.                         }
  2782.                         if (right && used_height <
  2783.                                         right->y + right->height - cy + 1)
  2784.                                 used_height = right->y + right->height - cy + 1;
  2785.  
  2786.                         if (used_height < 0)
  2787.                                 used_height = 0;
  2788.  
  2789.                         b = split_box;
  2790. #ifdef LAYOUT_DEBUG
  2791.                         LOG(("moving below float"));
  2792. #endif
  2793.                 } else if (space == 0 || x1 - x0 <= x + space_before + w) {
  2794.                         /* first word of box doesn't fit so leave box for next
  2795.                          * line */
  2796.                         b = split_box;
  2797. #ifdef LAYOUT_DEBUG
  2798.                         LOG(("leaving for next line"));
  2799. #endif
  2800.                 } else {
  2801.                         /* fit as many words as possible */
  2802.                         assert(space != 0);
  2803.                         font_plot_style_from_css(split_box->style, &fstyle);
  2804.                         /** \todo handle errors */
  2805.                         font_func->font_split(&fstyle,
  2806.                                         split_box->text, split_box->length,
  2807.                                         x1 - x0 - x - space_before, &space, &w);
  2808. #ifdef LAYOUT_DEBUG
  2809.                         LOG(("'%.*s' %i %zu %i", (int) split_box->length,
  2810.                                         split_box->text, x1 - x0, space, w));
  2811. #endif
  2812.                         if (space == 0)
  2813.                                 space = 1;
  2814.                         if (space != split_box->length) {
  2815.                                 if (!layout_text_box_split(content, &fstyle,
  2816.                                                 split_box, space, w))
  2817.                                         return false;
  2818.                                 b = split_box->next;
  2819.                         }
  2820.                         x += space_before + w;
  2821. #ifdef LAYOUT_DEBUG
  2822.                         LOG(("fitting words"));
  2823. #endif
  2824.                 }
  2825.                 move_y = true;
  2826.         }
  2827.  
  2828.         /* set positions */
  2829.         switch (css_computed_text_align(first->parent->parent->style)) {
  2830.         case CSS_TEXT_ALIGN_RIGHT:
  2831.         case CSS_TEXT_ALIGN_LIBCSS_RIGHT:
  2832.                 x0 = x1 - x;
  2833.                 break;
  2834.         case CSS_TEXT_ALIGN_CENTER:
  2835.         case CSS_TEXT_ALIGN_LIBCSS_CENTER:
  2836.                 x0 = (x0 + (x1 - x)) / 2;
  2837.                 break;
  2838.         case CSS_TEXT_ALIGN_LEFT:
  2839.         case CSS_TEXT_ALIGN_LIBCSS_LEFT:
  2840.         case CSS_TEXT_ALIGN_JUSTIFY:
  2841.                 /* leave on left */
  2842.                 break;
  2843.         case CSS_TEXT_ALIGN_DEFAULT:
  2844.                 /* None; consider text direction */
  2845.                 switch (css_computed_direction(first->parent->parent->style)) {
  2846.                 case CSS_DIRECTION_LTR:
  2847.                         /* leave on left */
  2848.                         break;
  2849.                 case CSS_DIRECTION_RTL:
  2850.                         x0 = x1 - x;
  2851.                         break;
  2852.                 }
  2853.                 break;
  2854.         }
  2855.  
  2856.         for (d = first; d != b; d = d->next) {
  2857.                 d->flags &= ~NEW_LINE;
  2858.  
  2859.                 if (d->type == BOX_INLINE_BLOCK &&
  2860.                                 (css_computed_position(d->style) ==
  2861.                                                 CSS_POSITION_ABSOLUTE ||
  2862.                                  css_computed_position(d->style) ==
  2863.                                                 CSS_POSITION_FIXED)) {
  2864.                         /* positioned inline-blocks:
  2865.                          * set static position (x,y) only, rest of positioning
  2866.                          * is handled later */
  2867.                         d->x += x0;
  2868.                         d->y = *y;
  2869.                         continue;
  2870.                 } else if ((d->type == BOX_INLINE &&
  2871.                                 ((d->object || d->gadget) == false) &&
  2872.                                 !(d->flags & IFRAME) &&
  2873.                                 !(d->flags & REPLACE_DIM)) ||
  2874.                                 d->type == BOX_BR ||
  2875.                                 d->type == BOX_TEXT ||
  2876.                                 d->type == BOX_INLINE_END) {
  2877.                         /* regular (non-replaced) inlines */
  2878.                         d->x += x0;
  2879.                         d->y = *y - d->padding[TOP];
  2880.  
  2881.                         if (d->type == BOX_TEXT && d->height > used_height) {
  2882.                                 /* text */
  2883.                                 used_height = d->height;
  2884.                         }
  2885.                 } else if ((d->type == BOX_INLINE) ||
  2886.                                 d->type == BOX_INLINE_BLOCK) {
  2887.                         /* replaced inlines and inline-blocks */
  2888.                         d->x += x0;
  2889.                         d->y = *y + d->border[TOP].width + d->margin[TOP];
  2890.                         h = d->margin[TOP] + d->border[TOP].width +
  2891.                                         d->padding[TOP] + d->height +
  2892.                                         d->padding[BOTTOM] +
  2893.                                         d->border[BOTTOM].width +
  2894.                                         d->margin[BOTTOM];
  2895.                         if (used_height < h)
  2896.                                 used_height = h;
  2897.                 }
  2898.         }
  2899.  
  2900.         first->flags |= NEW_LINE;
  2901.  
  2902.         assert(b != first || (move_y && 0 < used_height && (left || right)));
  2903.  
  2904.         /* handle vertical-align by adjusting box y values */
  2905.         /** \todo  proper vertical alignment handling */
  2906.         for (d = first; d != b; d = d->next) {
  2907.                 if ((d->type == BOX_INLINE && d->inline_end) ||
  2908.                                 d->type == BOX_BR ||
  2909.                                 d->type == BOX_TEXT ||
  2910.                                 d->type == BOX_INLINE_END) {
  2911.                         css_fixed value = 0;
  2912.                         css_unit unit = CSS_UNIT_PX;
  2913.                         switch (css_computed_vertical_align(d->style, &value,
  2914.                                         &unit)) {
  2915.                         case CSS_VERTICAL_ALIGN_SUPER:
  2916.                         case CSS_VERTICAL_ALIGN_TOP:
  2917.                         case CSS_VERTICAL_ALIGN_TEXT_TOP:
  2918.                                 /* already at top */
  2919.                                 break;
  2920.                         case CSS_VERTICAL_ALIGN_SUB:
  2921.                         case CSS_VERTICAL_ALIGN_BOTTOM:
  2922.                         case CSS_VERTICAL_ALIGN_TEXT_BOTTOM:
  2923.                                 d->y += used_height - d->height;
  2924.                                 break;
  2925.                         default:
  2926.                         case CSS_VERTICAL_ALIGN_BASELINE:
  2927.                                 d->y += 0.75 * (used_height - d->height);
  2928.                                 break;
  2929.                         }
  2930.                 }
  2931.         }
  2932.  
  2933.         /* handle clearance for br */
  2934.         if (br_box && css_computed_clear(br_box->style) != CSS_CLEAR_NONE) {
  2935.                 int clear_y = layout_clear(cont->float_children,
  2936.                                 css_computed_clear(br_box->style));
  2937.                 if (used_height < clear_y - cy)
  2938.                         used_height = clear_y - cy;
  2939.         }
  2940.  
  2941.         if (move_y)
  2942.                 *y += used_height;
  2943.         *next_box = b;
  2944.         *width = x; /* return actual width */
  2945.         return true;
  2946. }
  2947.  
  2948.  
  2949. /**
  2950.  * Calculate minimum and maximum width of a line.
  2951.  *
  2952.  * \param  first       a box in an inline container
  2953.  * \param  line_min    updated to minimum width of line starting at first
  2954.  * \param  line_max    updated to maximum width of line starting at first
  2955.  * \param  first_line  true iff this is the first line in the inline container
  2956.  * \param  line_has_height  updated to true or false, depending on line
  2957.  * \return  first box in next line, or 0 if no more lines
  2958.  * \post  0 <= *line_min <= *line_max
  2959.  */
  2960.  
  2961. struct box *layout_minmax_line(struct box *first,
  2962.                 int *line_min, int *line_max, bool first_line,
  2963.                 bool *line_has_height, const struct font_functions *font_func)
  2964. {
  2965.         int min = 0, max = 0, width, height, fixed;
  2966.         float frac;
  2967.         size_t i, j;
  2968.         struct box *b;
  2969.         plot_font_style_t fstyle;
  2970.  
  2971.         assert(first->parent);
  2972.         assert(first->parent->parent);
  2973.         assert(first->parent->parent->style);
  2974.  
  2975.         *line_has_height = false;
  2976.  
  2977.         /* corresponds to the pass 1 loop in layout_line() */
  2978.         for (b = first; b; b = b->next) {
  2979.                 enum css_width_e wtype;
  2980.                 enum css_height_e htype;
  2981.                 css_fixed value = 0;
  2982.                 css_unit unit = CSS_UNIT_PX;
  2983.  
  2984.                 assert(b->type == BOX_INLINE || b->type == BOX_INLINE_BLOCK ||
  2985.                                 b->type == BOX_FLOAT_LEFT ||
  2986.                                 b->type == BOX_FLOAT_RIGHT ||
  2987.                                 b->type == BOX_BR || b->type == BOX_TEXT ||
  2988.                                 b->type == BOX_INLINE_END);
  2989.  
  2990. #ifdef LAYOUT_DEBUG
  2991.                 LOG(("%p: min %i, max %i", b, min, max));
  2992. #endif
  2993.  
  2994.                 if (b->type == BOX_BR) {
  2995.                         b = b->next;
  2996.                         break;
  2997.                 }
  2998.  
  2999.                 if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT) {
  3000.                         assert(b->children);
  3001.                         if (b->children->type == BOX_BLOCK)
  3002.                                 layout_minmax_block(b->children, font_func);
  3003.                         else
  3004.                                 layout_minmax_table(b->children, font_func);
  3005.                         b->min_width = b->children->min_width;
  3006.                         b->max_width = b->children->max_width;
  3007.                         if (min < b->min_width)
  3008.                                 min = b->min_width;
  3009.                         max += b->max_width;
  3010.                         continue;
  3011.                 }
  3012.  
  3013.                 if (b->type == BOX_INLINE_BLOCK) {
  3014.                         layout_minmax_block(b, font_func);
  3015.                         if (min < b->min_width)
  3016.                                 min = b->min_width;
  3017.                         max += b->max_width;
  3018.  
  3019.                         if (b->flags & HAS_HEIGHT)
  3020.                                 *line_has_height = true;
  3021.                         continue;
  3022.                 }
  3023.  
  3024.                 assert(b->style);
  3025.                 font_plot_style_from_css(b->style, &fstyle);
  3026.  
  3027.                 if (b->type == BOX_INLINE && !b->object &&
  3028.                                 !(b->flags & REPLACE_DIM) &&
  3029.                                 !(b->flags & IFRAME)) {
  3030.                         fixed = frac = 0;
  3031.                         calculate_mbp_width(b->style, LEFT, true, true, true,
  3032.                                         &fixed, &frac);
  3033.                         if (!b->inline_end)
  3034.                                 calculate_mbp_width(b->style, RIGHT,
  3035.                                                 true, true, true,
  3036.                                                 &fixed, &frac);
  3037.                         if (0 < fixed)
  3038.                                 max += fixed;
  3039.                         *line_has_height = true;
  3040.                         /* \todo  update min width, consider fractional extra */
  3041.                 } else if (b->type == BOX_INLINE_END) {
  3042.                         fixed = frac = 0;
  3043.                         calculate_mbp_width(b->inline_end->style, RIGHT,
  3044.                                         true, true, true,
  3045.                                         &fixed, &frac);
  3046.                         if (0 < fixed)
  3047.                                 max += fixed;
  3048.  
  3049.                         if (b->next) {
  3050.                                 if (b->space == UNKNOWN_WIDTH) {
  3051.                                         font_func->font_width(&fstyle, " ", 1,
  3052.                                                         &b->space);
  3053.                                 }
  3054.                                 max += b->space;
  3055.                         }
  3056.  
  3057.                         *line_has_height = true;
  3058.                         continue;
  3059.                 }
  3060.  
  3061.                 if (!b->object && !(b->flags & IFRAME) && !b->gadget &&
  3062.                                 !(b->flags & REPLACE_DIM)) {
  3063.                         /* inline non-replaced, 10.3.1 and 10.6.1 */
  3064.                         if (!b->text)
  3065.                                 continue;
  3066.  
  3067.                         if (b->width == UNKNOWN_WIDTH) {
  3068.                                 /** \todo handle errors */
  3069.  
  3070.                                 /* If it's a select element, we must use the
  3071.                                  * width of the widest option text */
  3072.                                 if (b->parent->parent->gadget &&
  3073.                                                 b->parent->parent->gadget->type
  3074.                                                 == GADGET_SELECT) {
  3075.                                         int opt_maxwidth = 0;
  3076.                                         struct form_option *o;
  3077.  
  3078.                                         for (o = b->parent->parent->gadget->
  3079.                                                         data.select.items; o;
  3080.                                                         o = o->next) {
  3081.                                                 int opt_width;
  3082.                                                 font_func->font_width(&fstyle,
  3083.                                                                 o->text,
  3084.                                                                 strlen(o->text),
  3085.                                                                 &opt_width);
  3086.  
  3087.                                                 if (opt_maxwidth < opt_width)
  3088.                                                         opt_maxwidth =opt_width;
  3089.                                         }
  3090.  
  3091.                                         b->width = opt_maxwidth;
  3092.                                         if (nsoption_bool(core_select_menu))
  3093.                                                 b->width += SCROLLBAR_WIDTH;
  3094.  
  3095.                                 } else {
  3096.                                         font_func->font_width(&fstyle, b->text,
  3097.                                                 b->length, &b->width);
  3098.                                         b->flags |= MEASURED;
  3099.                                 }
  3100.                         }
  3101.                         max += b->width;
  3102.                         if (b->next) {
  3103.                                 if (b->space == UNKNOWN_WIDTH) {
  3104.                                         font_func->font_width(&fstyle, " ", 1,
  3105.                                                         &b->space);
  3106.                                 }
  3107.                                 max += b->space;
  3108.                         }
  3109.  
  3110.                         /* min = widest word */
  3111.                         if (b->parent->flags & NEED_MIN) {
  3112.                                 /* If we care what the minimum width is,
  3113.                                  * calculate it.  (It's only needed if we're
  3114.                                  * shrinking-to-fit.) */
  3115.                                 i = 0;
  3116.                                 do {
  3117.                                         for (j = i; j != b->length &&
  3118.                                                         b->text[j] != ' '; j++)
  3119.                                                 ;
  3120.                                         font_func->font_width(&fstyle,
  3121.                                                         b->text + i,
  3122.                                                         j - i, &width);
  3123.                                         if (min < width)
  3124.                                                 min = width;
  3125.                                         i = j + 1;
  3126.                                 } while (j != b->length);
  3127.                         }
  3128.  
  3129.                         *line_has_height = true;
  3130.  
  3131.                         continue;
  3132.                 }
  3133.  
  3134.                 /* inline replaced, 10.3.2 and 10.6.2 */
  3135.                 assert(b->style);
  3136.  
  3137.                 /* calculate box width */
  3138.                 wtype = css_computed_width(b->style, &value, &unit);
  3139.                 if (wtype == CSS_WIDTH_SET) {
  3140.                         if (unit == CSS_UNIT_PCT) {
  3141.                                 /*
  3142.                                 b->width = FPCT_OF_INT_TOINT(value, width);
  3143.                                 */
  3144.  
  3145.                                 width = AUTO;
  3146.                         } else {
  3147.                                 width = FIXTOINT(nscss_len2px(value, unit,
  3148.                                                 b->style));
  3149.                                 if (width < 0)
  3150.                                         width = 0;
  3151.                         }
  3152.                 } else {
  3153.                         width = AUTO;
  3154.                 }
  3155.  
  3156.                 /* height */
  3157.                 htype = css_computed_height(b->style, &value, &unit);
  3158.                 if (htype == CSS_HEIGHT_SET) {
  3159.                         height = FIXTOINT(nscss_len2px(value, unit, b->style));
  3160.                 } else {
  3161.                         height = AUTO;
  3162.                 }
  3163.  
  3164.                 if (b->object || (b->flags & REPLACE_DIM)) {
  3165.                         if (b->object) {
  3166.                                 int temp_height = height;
  3167.                                 layout_get_object_dimensions(b,
  3168.                                                 &width, &temp_height,
  3169.                                                 INT_MIN, INT_MAX);
  3170.                         }
  3171.  
  3172.                         fixed = frac = 0;
  3173.                         calculate_mbp_width(b->style, LEFT, true, true, true,
  3174.                                         &fixed, &frac);
  3175.                         calculate_mbp_width(b->style, RIGHT, true, true, true,
  3176.                                         &fixed, &frac);
  3177.  
  3178.                         if (0 < width + fixed)
  3179.                                 width += fixed;
  3180.                 } else if (b->flags & IFRAME) {
  3181.                         /* TODO: handle percentage widths properly */
  3182.                         if (width == AUTO)
  3183.                                 width = 400;
  3184.  
  3185.                         fixed = frac = 0;
  3186.                         calculate_mbp_width(b->style, LEFT, true, true, true,
  3187.                                         &fixed, &frac);
  3188.                         calculate_mbp_width(b->style, RIGHT, true, true, true,
  3189.                                         &fixed, &frac);
  3190.  
  3191.                         if (0 < width + fixed)
  3192.                                 width += fixed;
  3193.                 } else {
  3194.                         /* form control with no object */
  3195.                         if (width == AUTO)
  3196.                                 width = FIXTOINT(nscss_len2px(INTTOFIX(1),
  3197.                                                 CSS_UNIT_EM, b->style));
  3198.                 }
  3199.  
  3200.                 if (min < width)
  3201.                         min = width;
  3202.                 max += width;
  3203.  
  3204.                 *line_has_height = true;
  3205.         }
  3206.  
  3207.         if (first_line) {
  3208.                 /* todo: handle percentage values properly */
  3209.                 /* todo: handle text-indent interaction with floats */
  3210.                 int text_indent = layout_text_indent(
  3211.                                 first->parent->parent->style, 100);
  3212.                 min = (min + text_indent < 0) ? 0 : min + text_indent;
  3213.                 max = (max + text_indent < 0) ? 0 : max + text_indent;
  3214.         }
  3215.  
  3216.         *line_min = min;
  3217.         *line_max = max;
  3218.  
  3219. #ifdef LAYOUT_DEBUG
  3220.         LOG(("line_min %i, line_max %i", min, max));
  3221. #endif
  3222.  
  3223.         assert(b != first);
  3224.         assert(0 <= *line_min);
  3225.         assert(*line_min <= *line_max);
  3226.         return b;
  3227. }
  3228.  
  3229.  
  3230. /**
  3231.  * Calculate the text-indent length.
  3232.  *
  3233.  * \param  style  style of block
  3234.  * \param  width  width of containing block
  3235.  * \return  length of indent
  3236.  */
  3237.  
  3238. int layout_text_indent(const css_computed_style *style, int width)
  3239. {
  3240.         css_fixed value = 0;
  3241.         css_unit unit = CSS_UNIT_PX;
  3242.  
  3243.         css_computed_text_indent(style, &value, &unit);
  3244.  
  3245.         if (unit == CSS_UNIT_PCT) {
  3246.                 return FPCT_OF_INT_TOINT(value, width);
  3247.         } else {
  3248.                 return FIXTOINT(nscss_len2px(value, unit, style));
  3249.         }
  3250. }
  3251.  
  3252.  
  3253. /**
  3254.  * Layout the contents of a float or inline block.
  3255.  *
  3256.  * \param  b      float or inline block box
  3257.  * \param  width  available width
  3258.  * \param  content  memory pool for any new boxes
  3259.  * \return  true on success, false on memory exhaustion
  3260.  */
  3261.  
  3262. bool layout_float(struct box *b, int width, html_content *content)
  3263. {
  3264.         assert(b->type == BOX_TABLE || b->type == BOX_BLOCK ||
  3265.                         b->type == BOX_INLINE_BLOCK);
  3266.         layout_float_find_dimensions(width, b->style, b);
  3267.         if (b->type == BOX_TABLE) {
  3268.                 if (!layout_table(b, width, content))
  3269.                         return false;
  3270.                 if (b->margin[LEFT] == AUTO)
  3271.                         b->margin[LEFT] = 0;
  3272.                 if (b->margin[RIGHT] == AUTO)
  3273.                         b->margin[RIGHT] = 0;
  3274.                 if (b->margin[TOP] == AUTO)
  3275.                         b->margin[TOP] = 0;
  3276.                 if (b->margin[BOTTOM] == AUTO)
  3277.                         b->margin[BOTTOM] = 0;
  3278.         } else
  3279.                 return layout_block_context(b, -1, content);
  3280.         return true;
  3281. }
  3282.  
  3283.  
  3284. /**
  3285.  * Position a float in the first available space.
  3286.  *
  3287.  * \param  c      float box to position
  3288.  * \param  width  available width
  3289.  * \param  cx     x coordinate relative to cont to place float right of
  3290.  * \param  y      y coordinate relative to cont to place float below
  3291.  * \param  cont   ancestor box which defines horizontal space, for floats
  3292.  */
  3293.  
  3294. void place_float_below(struct box *c, int width, int cx, int y,
  3295.                 struct box *cont)
  3296. {
  3297.         int x0, x1, yy = y;
  3298.         struct box *left;
  3299.         struct box *right;
  3300.  
  3301. #ifdef LAYOUT_DEBUG
  3302.         LOG(("c %p, width %i, cx %i, y %i, cont %p", c, width, cx, y, cont));
  3303. #endif
  3304.  
  3305.         do {
  3306.                 y = yy;
  3307.                 x0 = cx;
  3308.                 x1 = cx + width;
  3309.                 find_sides(cont->float_children, y, y + c->height, &x0, &x1,
  3310.                                 &left, &right);
  3311.                 if (left != 0 && right != 0) {
  3312.                         yy = (left->y + left->height <
  3313.                                         right->y + right->height ?
  3314.                                         left->y + left->height :
  3315.                                         right->y + right->height);
  3316.                 } else if (left == 0 && right != 0) {
  3317.                         yy = right->y + right->height;
  3318.                 } else if (left != 0 && right == 0) {
  3319.                         yy = left->y + left->height;
  3320.                 }
  3321.         } while (!((left == 0 && right == 0) || (c->width <= x1 - x0)));
  3322.  
  3323.         if (c->type == BOX_FLOAT_LEFT) {
  3324.                 c->x = x0;
  3325.         } else {
  3326.                 c->x = x1 - c->width;
  3327.         }
  3328.         c->y = y;
  3329. }
  3330.  
  3331.  
  3332. /**
  3333.  * Layout a table.
  3334.  *
  3335.  * \param  table            table to layout
  3336.  * \param  available_width  width of containing block
  3337.  * \param  content         memory pool for any new boxes
  3338.  * \return  true on success, false on memory exhaustion
  3339.  */
  3340.  
  3341. bool layout_table(struct box *table, int available_width,
  3342.                 html_content *content)
  3343. {
  3344.         unsigned int columns = table->columns;  /* total columns */
  3345.         unsigned int i;
  3346.         unsigned int *row_span;
  3347.         int *excess_y;
  3348.         int table_width, min_width = 0, max_width = 0;
  3349.         int required_width = 0;
  3350.         int x, remainder = 0, count = 0;
  3351.         int table_height = 0;
  3352.         int min_height = 0;
  3353.         int *xs;  /* array of column x positions */
  3354.         int auto_width;
  3355.         int spare_width;
  3356.         int relative_sum = 0;
  3357.         int border_spacing_h = 0, border_spacing_v = 0;
  3358.         int spare_height;
  3359.         int positioned_columns = 0;
  3360.         struct box *containing_block = NULL;
  3361.         struct box *c;
  3362.         struct box *row;
  3363.         struct box *row_group;
  3364.         struct box **row_span_cell;
  3365.         struct column *col;
  3366.         const css_computed_style *style = table->style;
  3367.         enum css_width_e wtype;
  3368.         enum css_height_e htype;
  3369.         css_fixed value = 0;
  3370.         css_unit unit = CSS_UNIT_PX;
  3371.  
  3372.         assert(table->type == BOX_TABLE);
  3373.         assert(style);
  3374.         assert(table->children && table->children->children);
  3375.         assert(columns);
  3376.  
  3377.         /* allocate working buffers */
  3378.         col = malloc(columns * sizeof col[0]);
  3379.         excess_y = malloc(columns * sizeof excess_y[0]);
  3380.         row_span = malloc(columns * sizeof row_span[0]);
  3381.         row_span_cell = malloc(columns * sizeof row_span_cell[0]);
  3382.         xs = malloc((columns + 1) * sizeof xs[0]);
  3383.         if (!col || !xs || !row_span || !excess_y || !row_span_cell) {
  3384.                 free(col);
  3385.                 free(excess_y);
  3386.                 free(row_span);
  3387.                 free(row_span_cell);
  3388.                 free(xs);
  3389.                 return false;
  3390.         }
  3391.  
  3392.         memcpy(col, table->col, sizeof(col[0]) * columns);
  3393.  
  3394.         /* find margins, paddings, and borders for table and cells */
  3395.         layout_find_dimensions(available_width, -1, table, style, 0, 0, 0, 0,
  3396.                         table->margin, table->padding, table->border);
  3397.         for (row_group = table->children; row_group;
  3398.                         row_group = row_group->next) {
  3399.                 for (row = row_group->children; row; row = row->next) {
  3400.                         for (c = row->children; c; c = c->next) {
  3401.                                 assert(c->style);
  3402.                                 table_used_border_for_cell(c);
  3403.                                 layout_find_dimensions(available_width, -1,
  3404.                                                 c, c->style, 0, 0, 0, 0, 0,
  3405.                                                 c->padding, c->border);
  3406.                                 if (css_computed_overflow(c->style) ==
  3407.                                                 CSS_OVERFLOW_SCROLL ||
  3408.                                         css_computed_overflow(c->style) ==
  3409.                                                 CSS_OVERFLOW_AUTO) {
  3410.                                         c->padding[RIGHT] += SCROLLBAR_WIDTH;
  3411.                                         c->padding[BOTTOM] += SCROLLBAR_WIDTH;
  3412.                                 }
  3413.                         }
  3414.                 }
  3415.         }
  3416.  
  3417.         /* border-spacing is used in the separated borders model */
  3418.         if (css_computed_border_collapse(style) ==
  3419.                         CSS_BORDER_COLLAPSE_SEPARATE) {
  3420.                 css_fixed h = 0, v = 0;
  3421.                 css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX;
  3422.  
  3423.                 css_computed_border_spacing(style, &h, &hu, &v, &vu);
  3424.  
  3425.                 border_spacing_h = FIXTOINT(nscss_len2px(h, hu, style));
  3426.                 border_spacing_v = FIXTOINT(nscss_len2px(v, vu, style));
  3427.         }
  3428.  
  3429.         /* find specified table width, or available width if auto-width */
  3430.         wtype = css_computed_width(style, &value, &unit);
  3431.         if (wtype == CSS_WIDTH_SET) {
  3432.                 if (unit == CSS_UNIT_PCT) {
  3433.                         table_width = FPCT_OF_INT_TOINT(value, available_width);
  3434.                 } else {
  3435.                         table_width =
  3436.                                 FIXTOINT(nscss_len2px(value, unit, style));
  3437.                 }
  3438.  
  3439.                 /* specified width includes border */
  3440.                 table_width -= table->border[LEFT].width +
  3441.                                 table->border[RIGHT].width;
  3442.                 table_width = table_width < 0 ? 0 : table_width;
  3443.  
  3444.                 auto_width = table_width;
  3445.         } else {
  3446.                 table_width = AUTO;
  3447.                 auto_width = available_width -
  3448.                                 ((table->margin[LEFT] == AUTO ? 0 :
  3449.                                                 table->margin[LEFT]) +
  3450.                                  table->border[LEFT].width +
  3451.                                  table->padding[LEFT] +
  3452.                                  table->padding[RIGHT] +
  3453.                                  table->border[RIGHT].width +
  3454.                                  (table->margin[RIGHT] == AUTO ? 0 :
  3455.                                                 table->margin[RIGHT]));
  3456.         }
  3457.  
  3458.         /* Find any table height specified within CSS/HTML */
  3459.         htype = css_computed_height(style, &value, &unit);
  3460.         if (htype == CSS_HEIGHT_SET) {
  3461.                 if (unit == CSS_UNIT_PCT) {
  3462.                         /* This is the minimum height for the table
  3463.                          * (see 17.5.3) */
  3464.                         if (css_computed_position(table->style) ==
  3465.                                         CSS_POSITION_ABSOLUTE) {
  3466.                                 /* Table is absolutely positioned */
  3467.                                 assert(table->float_container);
  3468.                                 containing_block = table->float_container;
  3469.                         } else if (table->float_container &&
  3470.                                         css_computed_position(table->style) !=
  3471.                                                 CSS_POSITION_ABSOLUTE &&
  3472.                                         (css_computed_float(table->style) ==
  3473.                                                 CSS_FLOAT_LEFT ||
  3474.                                         css_computed_float(table->style) ==
  3475.                                                 CSS_FLOAT_RIGHT)) {
  3476.                                 /* Table is a float */
  3477.                                 assert(table->parent && table->parent->parent &&
  3478.                                                 table->parent->parent->parent);
  3479.                                 containing_block =
  3480.                                                 table->parent->parent->parent;
  3481.                         } else if (table->parent && table->parent->type !=
  3482.                                         BOX_INLINE_CONTAINER) {
  3483.                                 /* Table is a block level element */
  3484.                                 containing_block = table->parent;
  3485.                         } else if (table->parent && table->parent->type ==
  3486.                                         BOX_INLINE_CONTAINER) {
  3487.                                 /* Table is an inline block */
  3488.                                 assert(table->parent->parent);
  3489.                                 containing_block = table->parent->parent;
  3490.                         }
  3491.  
  3492.                         if (containing_block) {
  3493.                                 css_fixed ignored = 0;
  3494.  
  3495.                                 htype = css_computed_height(
  3496.                                                 containing_block->style,
  3497.                                                 &ignored, &unit);
  3498.                         }
  3499.  
  3500.                         if (containing_block &&
  3501.                                         containing_block->height != AUTO &&
  3502.                                         (css_computed_position(table->style) ==
  3503.                                                         CSS_POSITION_ABSOLUTE ||
  3504.                                         htype == CSS_HEIGHT_SET)) {
  3505.                                 /* Table is absolutely positioned or its
  3506.                                  * containing block has a valid specified
  3507.                                  * height. (CSS 2.1 Section 10.5) */
  3508.                                 min_height = FPCT_OF_INT_TOINT(value,
  3509.                                                 containing_block->height);
  3510.                         }
  3511.                 } else {
  3512.                         /* This is the minimum height for the table
  3513.                          * (see 17.5.3) */
  3514.                         min_height = FIXTOINT(nscss_len2px(value, unit, style));
  3515.                 }
  3516.         }
  3517.  
  3518.         /* calculate width required by cells */
  3519.         for (i = 0; i != columns; i++) {
  3520. #ifdef LAYOUT_DEBUG
  3521.                 LOG(("table %p, column %u: type %s, width %i, min %i, max %i",
  3522.                                 table, i,
  3523.                                 ((const char *[]) {"UNKNOWN", "FIXED", "AUTO",
  3524.                                 "PERCENT", "RELATIVE"})[col[i].type],
  3525.                                 col[i].width, col[i].min, col[i].max));
  3526. #endif
  3527.  
  3528.                 if (col[i].positioned) {
  3529.                         positioned_columns++;
  3530.                         continue;
  3531.                 } else if (col[i].type == COLUMN_WIDTH_FIXED) {
  3532.                         if (col[i].width < col[i].min)
  3533.                                 col[i].width = col[i].max = col[i].min;
  3534.                         else
  3535.                                 col[i].min = col[i].max = col[i].width;
  3536.                         required_width += col[i].width;
  3537.                 } else if (col[i].type == COLUMN_WIDTH_PERCENT) {
  3538.                         int width = col[i].width * auto_width / 100;
  3539.                         required_width += col[i].min < width ? width :
  3540.                                         col[i].min;
  3541.                 } else
  3542.                         required_width += col[i].min;
  3543.  
  3544. #ifdef LAYOUT_DEBUG
  3545.                 LOG(("required_width %i", required_width));
  3546. #endif
  3547.         }
  3548.         required_width += (columns + 1 - positioned_columns) *
  3549.                         border_spacing_h;
  3550.  
  3551. #ifdef LAYOUT_DEBUG
  3552.         LOG(("width %i, min %i, max %i, auto %i, required %i",
  3553.                         table_width, table->min_width, table->max_width,
  3554.                         auto_width, required_width));
  3555. #endif
  3556.  
  3557.         if (auto_width < required_width) {
  3558.                 /* table narrower than required width for columns:
  3559.                  * treat percentage widths as maximums */
  3560.                 for (i = 0; i != columns; i++) {
  3561.                         if (col[i].type == COLUMN_WIDTH_RELATIVE)
  3562.                                 continue;
  3563.                         if (col[i].type == COLUMN_WIDTH_PERCENT) {
  3564.                                 col[i].max = auto_width * col[i].width / 100;
  3565.                                 if (col[i].max < col[i].min)
  3566.                                         col[i].max = col[i].min;
  3567.                         }
  3568.                         min_width += col[i].min;
  3569.                         max_width += col[i].max;
  3570.                 }
  3571.         } else {
  3572.                 /* take percentages exactly */
  3573.                 for (i = 0; i != columns; i++) {
  3574.                         if (col[i].type == COLUMN_WIDTH_RELATIVE)
  3575.                                 continue;
  3576.                         if (col[i].type == COLUMN_WIDTH_PERCENT) {
  3577.                                 int width = auto_width * col[i].width / 100;
  3578.                                 if (width < col[i].min)
  3579.                                         width = col[i].min;
  3580.                                 col[i].min = col[i].width = col[i].max = width;
  3581.                                 col[i].type = COLUMN_WIDTH_FIXED;
  3582.                         }
  3583.                         min_width += col[i].min;
  3584.                         max_width += col[i].max;
  3585.                 }
  3586.         }
  3587.  
  3588.         /* allocate relative widths */
  3589.         spare_width = auto_width;
  3590.         for (i = 0; i != columns; i++) {
  3591.                 if (col[i].type == COLUMN_WIDTH_RELATIVE)
  3592.                         relative_sum += col[i].width;
  3593.                 else if (col[i].type == COLUMN_WIDTH_FIXED)
  3594.                         spare_width -= col[i].width;
  3595.                 else
  3596.                         spare_width -= col[i].min;
  3597.         }
  3598.         spare_width -= (columns + 1) * border_spacing_h;
  3599.         if (relative_sum != 0) {
  3600.                 if (spare_width < 0)
  3601.                         spare_width = 0;
  3602.                 for (i = 0; i != columns; i++) {
  3603.                         if (col[i].type == COLUMN_WIDTH_RELATIVE) {
  3604.                                 col[i].min = ceil(col[i].max =
  3605.                                                 (float) spare_width
  3606.                                                 * (float) col[i].width
  3607.                                                 / relative_sum);
  3608.                                 min_width += col[i].min;
  3609.                                 max_width += col[i].max;
  3610.                         }
  3611.                 }
  3612.         }
  3613.         min_width += (columns + 1) * border_spacing_h;
  3614.         max_width += (columns + 1) * border_spacing_h;
  3615.  
  3616.         if (auto_width <= min_width) {
  3617.                 /* not enough space: minimise column widths */
  3618.                 for (i = 0; i < columns; i++) {
  3619.                         col[i].width = col[i].min;
  3620.                 }
  3621.                 table_width = min_width;
  3622.         } else if (max_width <= auto_width) {
  3623.                 /* more space than maximum width */
  3624.                 if (table_width == AUTO) {
  3625.                         /* for auto-width tables, make columns max width */
  3626.                         for (i = 0; i < columns; i++) {
  3627.                                 col[i].width = col[i].max;
  3628.                         }
  3629.                         table_width = max_width;
  3630.                 } else {
  3631.                         /* for fixed-width tables, distribute the extra space
  3632.                          * too */
  3633.                         unsigned int flexible_columns = 0;
  3634.                         for (i = 0; i != columns; i++)
  3635.                                 if (col[i].type != COLUMN_WIDTH_FIXED)
  3636.                                         flexible_columns++;
  3637.                         if (flexible_columns == 0) {
  3638.                                 int extra = (table_width - max_width) / columns;
  3639.                                 remainder = (table_width - max_width) -
  3640.                                                 (extra * columns);
  3641.                                 for (i = 0; i != columns; i++) {
  3642.                                         col[i].width = col[i].max + extra;
  3643.                                         count -= remainder;
  3644.                                         if (count < 0) {
  3645.                                                 col[i].width++;
  3646.                                                 count += columns;
  3647.                                         }
  3648.                                 }
  3649.  
  3650.                         } else {
  3651.                                 int extra = (table_width - max_width) /
  3652.                                                 flexible_columns;
  3653.                                 remainder = (table_width - max_width) -
  3654.                                                 (extra * flexible_columns);
  3655.                                 for (i = 0; i != columns; i++)
  3656.                                         if (col[i].type != COLUMN_WIDTH_FIXED) {
  3657.                                                 col[i].width = col[i].max +
  3658.                                                                 extra;
  3659.                                                 count -= remainder;
  3660.                                                 if (count < 0) {
  3661.                                                         col[i].width++;
  3662.                                                         count += flexible_columns;
  3663.                                                 }
  3664.                                         }
  3665.                         }
  3666.                 }
  3667.         } else {
  3668.                 /* space between min and max: fill it exactly */
  3669.                 float scale = (float) (auto_width - min_width) /
  3670.                                 (float) (max_width - min_width);
  3671.                 /* fprintf(stderr, "filling, scale %f\n", scale); */
  3672.                 for (i = 0; i < columns; i++) {
  3673.                         col[i].width = col[i].min + (int) (0.5 +
  3674.                                         (col[i].max - col[i].min) * scale);
  3675.                 }
  3676.                 table_width = auto_width;
  3677.         }
  3678.  
  3679.         xs[0] = x = border_spacing_h;
  3680.         for (i = 0; i != columns; i++) {
  3681.                 if (!col[i].positioned)
  3682.                         x += col[i].width + border_spacing_h;
  3683.                 xs[i + 1] = x;
  3684.                 row_span[i] = 0;
  3685.                 excess_y[i] = 0;
  3686.                 row_span_cell[i] = 0;
  3687.         }
  3688.  
  3689.         /* position cells */
  3690.         table_height = border_spacing_v;
  3691.         for (row_group = table->children; row_group;
  3692.                         row_group = row_group->next) {
  3693.                 int row_group_height = 0;
  3694.                 for (row = row_group->children; row; row = row->next) {
  3695.                         int row_height = 0;
  3696.  
  3697.                         htype = css_computed_height(row->style, &value, &unit);
  3698.                         if (htype == CSS_HEIGHT_SET && unit != CSS_UNIT_PCT) {
  3699.                                 row_height = FIXTOINT(nscss_len2px(value, unit,
  3700.                                                 row->style));
  3701.                         }
  3702.                         for (c = row->children; c; c = c->next) {
  3703.                                 assert(c->style);
  3704.                                 c->width = xs[c->start_column + c->columns] -
  3705.                                                 xs[c->start_column] -
  3706.                                                 border_spacing_h -
  3707.                                                 c->border[LEFT].width -
  3708.                                                 c->padding[LEFT] -
  3709.                                                 c->padding[RIGHT] -
  3710.                                                 c->border[RIGHT].width;
  3711.                                 c->float_children = 0;
  3712.  
  3713.                                 c->height = AUTO;
  3714.                                 if (!layout_block_context(c, -1, content)) {
  3715.                                         free(col);
  3716.                                         free(excess_y);
  3717.                                         free(row_span);
  3718.                                         free(row_span_cell);
  3719.                                         free(xs);
  3720.                                         return false;
  3721.                                 }
  3722.                                 /* warning: c->descendant_y0 and
  3723.                                  * c->descendant_y1 used as temporary storage
  3724.                                  * until after vertical alignment is complete */
  3725.                                 c->descendant_y0 = c->height;
  3726.                                 c->descendant_y1 = c->padding[BOTTOM];
  3727.  
  3728.                                 htype = css_computed_height(c->style,
  3729.                                                 &value, &unit);
  3730.  
  3731.                                 if (htype == CSS_HEIGHT_SET &&
  3732.                                                 unit != CSS_UNIT_PCT) {
  3733.                                         /* some sites use height="1" or similar
  3734.                                          * to attempt to make cells as small as
  3735.                                          * possible, so treat it as a minimum */
  3736.                                         int h = FIXTOINT(nscss_len2px(value,
  3737.                                                         unit, c->style));
  3738.                                         if (c->height < h)
  3739.                                                 c->height = h;
  3740.                                 }
  3741.                                 /* specified row height is treated as a minimum
  3742.                                  */
  3743.                                 if (c->height < row_height)
  3744.                                         c->height = row_height;
  3745.                                 c->x = xs[c->start_column] +
  3746.                                                 c->border[LEFT].width;
  3747.                                 c->y = c->border[TOP].width;
  3748.                                 for (i = 0; i != c->columns; i++) {
  3749.                                         row_span[c->start_column + i] = c->rows;
  3750.                                         excess_y[c->start_column + i] =
  3751.                                                         c->border[TOP].width +
  3752.                                                         c->padding[TOP] +
  3753.                                                         c->height +
  3754.                                                         c->padding[BOTTOM] +
  3755.                                                         c->border[BOTTOM].width;
  3756.                                         row_span_cell[c->start_column + i] = 0;
  3757.                                 }
  3758.                                 row_span_cell[c->start_column] = c;
  3759.                                 c->padding[BOTTOM] = -border_spacing_v -
  3760.                                                 c->border[TOP].width -
  3761.                                                 c->padding[TOP] -
  3762.                                                 c->height -
  3763.                                                 c->border[BOTTOM].width;
  3764.                         }
  3765.                         for (i = 0; i != columns; i++)
  3766.                                 if (row_span[i] != 0)
  3767.                                         row_span[i]--;
  3768.                                 else
  3769.                                         row_span_cell[i] = 0;
  3770.                         if (row->next || row_group->next) {
  3771.                                 /* row height is greatest excess of a cell
  3772.                                  * which ends in this row */
  3773.                                 for (i = 0; i != columns; i++)
  3774.                                         if (row_span[i] == 0 && row_height <
  3775.                                                         excess_y[i])
  3776.                                                 row_height = excess_y[i];
  3777.                         } else {
  3778.                                 /* except in the last row */
  3779.                                 for (i = 0; i != columns; i++)
  3780.                                         if (row_height < excess_y[i])
  3781.                                                 row_height = excess_y[i];
  3782.                         }
  3783.                         for (i = 0; i != columns; i++) {
  3784.                                 if (row_height < excess_y[i])
  3785.                                         excess_y[i] -= row_height;
  3786.                                 else
  3787.                                         excess_y[i] = 0;
  3788.                                 if (row_span_cell[i] != 0)
  3789.                                         row_span_cell[i]->padding[BOTTOM] +=
  3790.                                                         row_height +
  3791.                                                         border_spacing_v;
  3792.                         }
  3793.  
  3794.                         row->x = 0;
  3795.                         row->y = row_group_height;
  3796.                         row->width = table_width;
  3797.                         row->height = row_height;
  3798.                         row_group_height += row_height + border_spacing_v;
  3799.                 }
  3800.                 row_group->x = 0;
  3801.                 row_group->y = table_height;
  3802.                 row_group->width = table_width;
  3803.                 row_group->height = row_group_height;
  3804.                 table_height += row_group_height;
  3805.         }
  3806.         /* Table height is either the height of the contents, or specified
  3807.          * height if greater */
  3808.         table_height = max(table_height, min_height);
  3809.         /** \TODO distribute spare height over the row groups / rows / cells */
  3810.  
  3811.         /* perform vertical alignment */
  3812.         for (row_group = table->children; row_group;
  3813.                         row_group = row_group->next) {
  3814.                 for (row = row_group->children; row; row = row->next) {
  3815.                         for (c = row->children; c; c = c->next) {
  3816.                                 enum css_vertical_align_e vertical_align;
  3817.  
  3818.                                 /* unextended bottom padding is in
  3819.                                  * c->descendant_y1, and unextended
  3820.                                  * cell height is in c->descendant_y0 */
  3821.                                 spare_height = (c->padding[BOTTOM] -
  3822.                                                 c->descendant_y1) +
  3823.                                                 (c->height - c->descendant_y0);
  3824.  
  3825.                                 vertical_align = css_computed_vertical_align(
  3826.                                                 c->style, &value, &unit);
  3827.  
  3828.                                 switch (vertical_align) {
  3829.                                 case CSS_VERTICAL_ALIGN_SUB:
  3830.                                 case CSS_VERTICAL_ALIGN_SUPER:
  3831.                                 case CSS_VERTICAL_ALIGN_TEXT_TOP:
  3832.                                 case CSS_VERTICAL_ALIGN_TEXT_BOTTOM:
  3833.                                 case CSS_VERTICAL_ALIGN_SET:
  3834.                                 case CSS_VERTICAL_ALIGN_BASELINE:
  3835.                                         /* todo: baseline alignment, for now
  3836.                                          * just use ALIGN_TOP */
  3837.                                 case CSS_VERTICAL_ALIGN_TOP:
  3838.                                         break;
  3839.                                 case CSS_VERTICAL_ALIGN_MIDDLE:
  3840.                                         c->padding[TOP] += spare_height / 2;
  3841.                                         c->padding[BOTTOM] -= spare_height / 2;
  3842.                                         layout_move_children(c, 0,
  3843.                                                         spare_height / 2);
  3844.                                         break;
  3845.                                 case CSS_VERTICAL_ALIGN_BOTTOM:
  3846.                                         c->padding[TOP] += spare_height;
  3847.                                         c->padding[BOTTOM] -= spare_height;
  3848.                                         layout_move_children(c, 0,
  3849.                                                         spare_height);
  3850.                                         break;
  3851.                                 case CSS_VERTICAL_ALIGN_INHERIT:
  3852.                                         assert(0);
  3853.                                         break;
  3854.                                 }
  3855.                         }
  3856.                 }
  3857.         }
  3858.  
  3859.         /* Top and bottom margins of 'auto' are set to 0.  CSS2.1 10.6.3 */
  3860.         if (table->margin[TOP] == AUTO)
  3861.                 table->margin[TOP] = 0;
  3862.         if (table->margin[BOTTOM] == AUTO)
  3863.                 table->margin[BOTTOM] = 0;
  3864.  
  3865.         free(col);
  3866.         free(excess_y);
  3867.         free(row_span);
  3868.         free(row_span_cell);
  3869.         free(xs);
  3870.  
  3871.         table->width = table_width;
  3872.         table->height = table_height;
  3873.  
  3874.         return true;
  3875. }
  3876.  
  3877.  
  3878. /**
  3879.  * Calculate minimum and maximum width of a table.
  3880.  *
  3881.  * \param  table  box of type TABLE
  3882.  * \post  table->min_width and table->max_width filled in,
  3883.  *        0 <= table->min_width <= table->max_width
  3884.  */
  3885.  
  3886. void layout_minmax_table(struct box *table,
  3887.                 const struct font_functions *font_func)
  3888. {
  3889.         unsigned int i, j;
  3890.         int border_spacing_h = 0;
  3891.         int table_min = 0, table_max = 0;
  3892.         int extra_fixed = 0;
  3893.         float extra_frac = 0;
  3894.         struct column *col = table->col;
  3895.         struct box *row_group, *row, *cell;
  3896.         enum css_width_e wtype;
  3897.         css_fixed value = 0;
  3898.         css_unit unit = CSS_UNIT_PX;
  3899.  
  3900.         /* check if the widths have already been calculated */
  3901.         if (table->max_width != UNKNOWN_MAX_WIDTH)
  3902.                 return;
  3903.  
  3904.         /* start with 0 except for fixed-width columns */
  3905.         for (i = 0; i != table->columns; i++) {
  3906.                 if (col[i].type == COLUMN_WIDTH_FIXED)
  3907.                         col[i].min = col[i].max = col[i].width;
  3908.                 else
  3909.                         col[i].min = col[i].max = 0;
  3910.         }
  3911.  
  3912.         /* border-spacing is used in the separated borders model */
  3913.         if (css_computed_border_collapse(table->style) ==
  3914.                         CSS_BORDER_COLLAPSE_SEPARATE) {
  3915.                 css_fixed h = 0, v = 0;
  3916.                 css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX;
  3917.  
  3918.                 css_computed_border_spacing(table->style, &h, &hu, &v, &vu);
  3919.  
  3920.                 border_spacing_h = FIXTOINT(nscss_len2px(h, hu, table->style));
  3921.         }
  3922.  
  3923.         /* 1st pass: consider cells with colspan 1 only */
  3924.         for (row_group = table->children; row_group; row_group =row_group->next)
  3925.         for (row = row_group->children; row; row = row->next)
  3926.         for (cell = row->children; cell; cell = cell->next) {
  3927.                 assert(cell->type == BOX_TABLE_CELL);
  3928.                 assert(cell->style);
  3929.                 /** TODO: Handle colspan="0" correctly.
  3930.                  *        It's currently converted to 1 in box normaisation */
  3931.                 assert(cell->columns != 0);
  3932.  
  3933.                 if (cell->columns != 1)
  3934.                         continue;
  3935.  
  3936.                 layout_minmax_block(cell, font_func);
  3937.                 i = cell->start_column;
  3938.  
  3939.                 if (col[i].positioned)
  3940.                         continue;
  3941.  
  3942.                 /* update column min, max widths using cell widths */
  3943.                 if (col[i].min < cell->min_width)
  3944.                         col[i].min = cell->min_width;
  3945.                 if (col[i].max < cell->max_width)
  3946.                         col[i].max = cell->max_width;
  3947.         }
  3948.  
  3949.         /* 2nd pass: cells which span multiple columns */
  3950.         for (row_group = table->children; row_group; row_group =row_group->next)
  3951.         for (row = row_group->children; row; row = row->next)
  3952.         for (cell = row->children; cell; cell = cell->next) {
  3953.                 unsigned int flexible_columns = 0;
  3954.                 int min = 0, max = 0, fixed_width = 0, extra;
  3955.  
  3956.                 if (cell->columns == 1)
  3957.                         continue;
  3958.  
  3959.                 layout_minmax_block(cell, font_func);
  3960.                 i = cell->start_column;
  3961.  
  3962.                 /* find min width so far of spanned columns, and count
  3963.                  * number of non-fixed spanned columns and total fixed width */
  3964.                 for (j = 0; j != cell->columns; j++) {
  3965.                         min += col[i + j].min;
  3966.                         if (col[i + j].type == COLUMN_WIDTH_FIXED)
  3967.                                 fixed_width += col[i + j].width;
  3968.                         else
  3969.                                 flexible_columns++;
  3970.                 }
  3971.                 min += (cell->columns - 1) * border_spacing_h;
  3972.  
  3973.                 /* distribute extra min to spanned columns */
  3974.                 if (min < cell->min_width) {
  3975.                         if (flexible_columns == 0) {
  3976.                                 extra = 1 + (cell->min_width - min) /
  3977.                                                 cell->columns;
  3978.                                 for (j = 0; j != cell->columns; j++) {
  3979.                                         col[i + j].min += extra;
  3980.                                         if (col[i + j].max < col[i + j].min)
  3981.                                                 col[i + j].max = col[i + j].min;
  3982.                                 }
  3983.                         } else {
  3984.                                 extra = 1 + (cell->min_width - min) /
  3985.                                                 flexible_columns;
  3986.                                 for (j = 0; j != cell->columns; j++) {
  3987.                                         if (col[i + j].type !=
  3988.                                                         COLUMN_WIDTH_FIXED) {
  3989.                                                 col[i + j].min += extra;
  3990.                                                 if (col[i + j].max <
  3991.                                                                 col[i + j].min)
  3992.                                                         col[i + j].max =
  3993.                                                                 col[i + j].min;
  3994.                                         }
  3995.                                 }
  3996.                         }
  3997.                 }
  3998.  
  3999.                 /* find max width so far of spanned columns */
  4000.                 for (j = 0; j != cell->columns; j++)
  4001.                         max += col[i + j].max;
  4002.                 max += (cell->columns - 1) * border_spacing_h;
  4003.  
  4004.                 /* distribute extra max to spanned columns */
  4005.                 if (max < cell->max_width && flexible_columns) {
  4006.                         extra = 1 + (cell->max_width - max) / flexible_columns;
  4007.                         for (j = 0; j != cell->columns; j++)
  4008.                                 if (col[i + j].type != COLUMN_WIDTH_FIXED)
  4009.                                         col[i + j].max += extra;
  4010.                 }
  4011.         }
  4012.  
  4013.         for (i = 0; i != table->columns; i++) {
  4014.                 if (col[i].max < col[i].min) {
  4015.                         box_dump(stderr, table, 0);
  4016.                         assert(0);
  4017.                 }
  4018.                 table_min += col[i].min;
  4019.                 table_max += col[i].max;
  4020.         }
  4021.  
  4022.         /* fixed width takes priority, unless it is too narrow */
  4023.         wtype = css_computed_width(table->style, &value, &unit);
  4024.         if (wtype == CSS_WIDTH_SET && unit != CSS_UNIT_PCT) {
  4025.                 int width = FIXTOINT(nscss_len2px(value, unit, table->style));
  4026.                 if (table_min < width)
  4027.                         table_min = width;
  4028.                 if (table_max < width)
  4029.                         table_max = width;
  4030.         }
  4031.  
  4032.         /* add margins, border, padding to min, max widths */
  4033.         calculate_mbp_width(table->style, LEFT, true, true, true,
  4034.                         &extra_fixed, &extra_frac);
  4035.         calculate_mbp_width(table->style, RIGHT, true, true, true,
  4036.                         &extra_fixed, &extra_frac);
  4037.         if (extra_fixed < 0)
  4038.                 extra_fixed = 0;
  4039.         if (extra_frac < 0)
  4040.                 extra_frac = 0;
  4041.         if (1.0 <= extra_frac)
  4042.                 extra_frac = 0.9;
  4043.         table->min_width = (table_min + extra_fixed) / (1.0 - extra_frac);
  4044.         table->max_width = (table_max + extra_fixed) / (1.0 - extra_frac);
  4045.         table->min_width += (table->columns + 1) * border_spacing_h;
  4046.         table->max_width += (table->columns + 1) * border_spacing_h;
  4047.  
  4048.         assert(0 <= table->min_width && table->min_width <= table->max_width);
  4049. }
  4050.  
  4051.  
  4052. /**
  4053.  * Moves the children of a box by a specified amount
  4054.  *
  4055.  * \param  box  top of tree of boxes
  4056.  * \param  x    the amount to move children by horizontally
  4057.  * \param  y    the amount to move children by vertically
  4058.  */
  4059.  
  4060. void layout_move_children(struct box *box, int x, int y)
  4061. {
  4062.         assert(box);
  4063.  
  4064.         for (box = box->children; box; box = box->next) {
  4065.                 box->x += x;
  4066.                 box->y += y;
  4067.         }
  4068. }
  4069.  
  4070.  
  4071. /**
  4072.  * Determine width of margin, borders, and padding on one side of a box.
  4073.  *
  4074.  * \param  style    style to measure
  4075.  * \param  size     side of box to measure
  4076.  * \param  margin   whether margin width is required
  4077.  * \param  border   whether border width is required
  4078.  * \param  padding  whether padding width is required
  4079.  * \param  fixed    increased by sum of fixed margin, border, and padding
  4080.  * \param  frac     increased by sum of fractional margin and padding
  4081.  */
  4082.  
  4083. void calculate_mbp_width(const css_computed_style *style, unsigned int side,
  4084.                 bool margin, bool border, bool padding,
  4085.                 int *fixed, float *frac)
  4086. {
  4087.         typedef uint8_t (*len_func)(const css_computed_style *style,
  4088.                         css_fixed *length, css_unit *unit);
  4089.  
  4090.         static len_func margin_funcs[4] = {
  4091.                 css_computed_margin_top,
  4092.                 css_computed_margin_right,
  4093.                 css_computed_margin_bottom,
  4094.                 css_computed_margin_left
  4095.         };
  4096.         static len_func padding_funcs[4] = {
  4097.                 css_computed_padding_top,
  4098.                 css_computed_padding_right,
  4099.                 css_computed_padding_bottom,
  4100.                 css_computed_padding_left
  4101.         };
  4102.         static struct {
  4103.                 len_func width;
  4104.                 uint8_t (*style)(const css_computed_style *style);
  4105.         } border_funcs[4] = {
  4106.                 { css_computed_border_top_width,
  4107.                                 css_computed_border_top_style },
  4108.                 { css_computed_border_right_width,
  4109.                                 css_computed_border_right_style },
  4110.                 { css_computed_border_bottom_width,
  4111.                                 css_computed_border_bottom_style },
  4112.                 { css_computed_border_left_width,
  4113.                                 css_computed_border_left_style }
  4114.         };
  4115.  
  4116.         css_fixed value = 0;
  4117.         css_unit unit = CSS_UNIT_PX;
  4118.  
  4119.         assert(style);
  4120.  
  4121.         /* margin */
  4122.         if (margin) {
  4123.                 enum css_margin_e type;
  4124.  
  4125.                 type = margin_funcs[side](style, &value, &unit);
  4126.                 if (type == CSS_MARGIN_SET) {
  4127.                         if (unit == CSS_UNIT_PCT) {
  4128.                                 *frac += FIXTOINT(FDIV(value, F_100));
  4129.                         } else {
  4130.                                 *fixed += FIXTOINT(nscss_len2px(value, unit,
  4131.                                                 style));
  4132.                         }
  4133.                 }
  4134.         }
  4135.  
  4136.         /* border */
  4137.         if (border) {
  4138.                 if (border_funcs[side].style(style) !=
  4139.                                 CSS_BORDER_STYLE_NONE) {
  4140.                         border_funcs[side].width(style, &value, &unit);
  4141.  
  4142.                         *fixed += FIXTOINT(nscss_len2px(value, unit, style));
  4143.                 }
  4144.         }
  4145.  
  4146.         /* padding */
  4147.         if (padding) {
  4148.                 padding_funcs[side](style, &value, &unit);
  4149.                 if (unit == CSS_UNIT_PCT) {
  4150.                         *frac += FIXTOINT(FDIV(value, F_100));
  4151.                 } else {
  4152.                         *fixed += FIXTOINT(nscss_len2px(value, unit, style));
  4153.                 }
  4154.         }
  4155. }
  4156.  
  4157.  
  4158. /**
  4159.  * Layout list markers.
  4160.  */
  4161.  
  4162. void layout_lists(struct box *box,
  4163.                 const struct font_functions *font_func)
  4164. {
  4165.         struct box *child;
  4166.         struct box *marker;
  4167.         plot_font_style_t fstyle;
  4168.  
  4169.         for (child = box->children; child; child = child->next) {
  4170.                 if (child->list_marker) {
  4171.                         marker = child->list_marker;
  4172.                         if (marker->object) {
  4173.                                 marker->width =
  4174.                                         content_get_width(marker->object);
  4175.                                 marker->x = -marker->width;
  4176.                                 marker->height =
  4177.                                         content_get_height(marker->object);
  4178.                                 marker->y = (line_height(marker->style) -
  4179.                                                 marker->height) / 2;
  4180.                         } else if (marker->text) {
  4181.                                 if (marker->width == UNKNOWN_WIDTH) {
  4182.                                         font_plot_style_from_css(marker->style,
  4183.                                                         &fstyle);
  4184.                                         font_func->font_width(&fstyle,
  4185.                                                         marker->text,
  4186.                                                         marker->length,
  4187.                                                         &marker->width);
  4188.                                         marker->flags |= MEASURED;
  4189.                                 }
  4190.                                 marker->x = -marker->width;
  4191.                                 marker->y = 0;
  4192.                                 marker->height = line_height(marker->style);
  4193.                         } else {
  4194.                                 marker->x = 0;
  4195.                                 marker->y = 0;
  4196.                                 marker->width = 0;
  4197.                                 marker->height = 0;
  4198.                         }
  4199.                         /* Gap between marker and content */
  4200.                         marker->x -= 4;
  4201.                 }
  4202.                 layout_lists(child, font_func);
  4203.         }
  4204. }
  4205.  
  4206.  
  4207. /**
  4208.  * Adjust positions of relatively positioned boxes.
  4209.  *
  4210.  * \param  root  box to adjust the position of
  4211.  * \param  fp    box which forms the block formatting context for children of
  4212.  *               "root" which are floats
  4213.  * \param  fx    x offset due to intervening relatively positioned boxes
  4214.  *               between current box, "root", and the block formatting context
  4215.  *               box, "fp", for float children of "root"
  4216.  * \param  fy    y offset due to intervening relatively positioned boxes
  4217.  *               between current box, "root", and the block formatting context
  4218.  *               box, "fp", for float children of "root"
  4219.  */
  4220.  
  4221. void layout_position_relative(struct box *root, struct box *fp, int fx, int fy)
  4222. {
  4223.         struct box *box; /* for children of "root" */
  4224.         struct box *fn;  /* for block formatting context box for children of
  4225.                           * "box" */
  4226.         struct box *fc;  /* for float children of the block formatting context,
  4227.                           * "fp" */
  4228.         int x, y;        /* for the offsets resulting from any relative
  4229.                           * positioning on the current block */
  4230.         int fnx, fny;    /* for affsets which apply to flat children of "box" */
  4231.  
  4232.         /**\todo ensure containing box is large enough after moving boxes */
  4233.  
  4234.         assert(root);
  4235.  
  4236.         /* Normal children */
  4237.         for (box = root->children; box; box = box->next) {
  4238.  
  4239.                 if (box->type == BOX_TEXT)
  4240.                         continue;
  4241.  
  4242.                 /* If relatively positioned, get offsets */
  4243.                 if (box->style && css_computed_position(box->style) ==
  4244.                                 CSS_POSITION_RELATIVE)
  4245.                         layout_compute_relative_offset(box, &x, &y);
  4246.                 else
  4247.                         x = y = 0;
  4248.  
  4249.                 /* Adjust float coordinates.
  4250.                  * (note float x and y are relative to their block formatting
  4251.                  * context box and not their parent) */
  4252.                 if (box->style && (css_computed_float(box->style) ==
  4253.                                         CSS_FLOAT_LEFT ||
  4254.                                 css_computed_float(box->style) ==
  4255.                                         CSS_FLOAT_RIGHT) &&
  4256.                                 (fx != 0 || fy != 0)) {
  4257.                         /* box is a float and there is a float offset to
  4258.                          * apply */
  4259.                         for (fc = fp->float_children; fc; fc = fc->next_float) {
  4260.                                 if (box == fc->children) {
  4261.                                         /* Box is floated in the block
  4262.                                          * formatting context block, fp.
  4263.                                          * Apply float offsets. */
  4264.                                         box->x += fx;
  4265.                                         box->y += fy;
  4266.                                         fx = fy = 0;
  4267.                                 }
  4268.                         }
  4269.                 }
  4270.  
  4271.                 if (box->float_children) {
  4272.                         fn = box;
  4273.                         fnx = fny = 0;
  4274.                 } else {
  4275.                         fn = fp;
  4276.                         fnx = fx + x;
  4277.                         fny = fy + y;
  4278.                 }
  4279.  
  4280.                 /* recurse first */
  4281.                 layout_position_relative(box, fn, fnx, fny);
  4282.  
  4283.                 /* Ignore things we're not interested in. */
  4284.                 if (!box->style || (box->style &&
  4285.                                 css_computed_position(box->style) !=
  4286.                                 CSS_POSITION_RELATIVE))
  4287.                         continue;
  4288.  
  4289.                 box->x += x;
  4290.                 box->y += y;
  4291.  
  4292.                 /* Handle INLINEs - their "children" are in fact
  4293.                  * the sibling boxes between the INLINE and
  4294.                  * INLINE_END boxes */
  4295.                 if (box->type == BOX_INLINE && box->inline_end) {
  4296.                         struct box *b;
  4297.                         for (b = box->next; b && b != box->inline_end;
  4298.                                         b = b->next) {
  4299.                                 b->x += x;
  4300.                                 b->y += y;
  4301.                         }
  4302.                 }
  4303.         }
  4304. }
  4305.  
  4306.  
  4307. /**
  4308.  * Compute a box's relative offset as per CSS 2.1 9.4.3
  4309.  *
  4310.  * \param  box  Box to compute relative offsets for.
  4311.  * \param  x    Receives relative offset in x.
  4312.  * \param  y    Receives relative offset in y.
  4313.  */
  4314.  
  4315. void layout_compute_relative_offset(struct box *box, int *x, int *y)
  4316. {
  4317.         int left, right, top, bottom;
  4318.         struct box *containing_block;
  4319.  
  4320.         assert(box && box->parent && box->style &&
  4321.                         css_computed_position(box->style) ==
  4322.                         CSS_POSITION_RELATIVE);
  4323.  
  4324.         if (box->float_container &&
  4325.                         (css_computed_float(box->style) == CSS_FLOAT_LEFT ||
  4326.                         css_computed_float(box->style) == CSS_FLOAT_RIGHT)) {
  4327.                 containing_block = box->float_container;
  4328.         } else {
  4329.                 containing_block = box->parent;
  4330.         }
  4331.  
  4332.         layout_compute_offsets(box, containing_block,
  4333.                         &top, &right, &bottom, &left);
  4334.  
  4335.         if (left == AUTO && right == AUTO)
  4336.                 left = right = 0;
  4337.         else if (left == AUTO)
  4338.                 /* left is auto => computed = -right */
  4339.                 left = -right;
  4340.         else if (right == AUTO)
  4341.                 /* right is auto => computed = -left */
  4342.                 right = -left;
  4343.         else {
  4344.                 /* over constrained => examine direction property
  4345.                  * of containing block */
  4346.                 if (containing_block->style &&
  4347.                                 css_computed_direction(
  4348.                                 containing_block->style) ==
  4349.                                 CSS_DIRECTION_RTL) {
  4350.                         /* right wins */
  4351.                         left = -right;
  4352.                 } else {
  4353.                         /* assume LTR in all other cases */
  4354.                         right = -left;
  4355.                 }
  4356.         }
  4357.  
  4358.         assert(left == -right);
  4359.  
  4360.         if (top == AUTO && bottom == AUTO)
  4361.                 top = bottom = 0;
  4362.         else if (top == AUTO)
  4363.                 top = -bottom;
  4364.         else if (bottom == AUTO)
  4365.                 bottom = -top;
  4366.         else
  4367.                 bottom = -top;
  4368.  
  4369. #ifdef LAYOUT_DEBUG
  4370.         LOG(("left %i, right %i, top %i, bottom %i", left, right, top, bottom));
  4371. #endif
  4372.  
  4373.         *x = left;
  4374.         *y = top;
  4375. }
  4376.  
  4377.  
  4378. /**
  4379.  * Recursively layout and position absolutely positioned boxes.
  4380.  *
  4381.  * \param  box               tree of boxes to layout
  4382.  * \param  containing_block  current containing block
  4383.  * \param  cx                position of box relative to containing_block
  4384.  * \param  cy                position of box relative to containing_block
  4385.  * \param  content           memory pool for any new boxes
  4386.  * \return  true on success, false on memory exhaustion
  4387.  */
  4388.  
  4389. bool layout_position_absolute(struct box *box,
  4390.                 struct box *containing_block,
  4391.                 int cx, int cy,
  4392.                 html_content *content)
  4393. {
  4394.         struct box *c;
  4395.  
  4396.         for (c = box->children; c; c = c->next) {
  4397.                 if ((c->type == BOX_BLOCK || c->type == BOX_TABLE ||
  4398.                                 c->type == BOX_INLINE_BLOCK) &&
  4399.                                 (css_computed_position(c->style) ==
  4400.                                                 CSS_POSITION_ABSOLUTE ||
  4401.                                  css_computed_position(c->style) ==
  4402.                                                 CSS_POSITION_FIXED)) {
  4403.                         if (!layout_absolute(c, containing_block,
  4404.                                         cx, cy, content))
  4405.                                 return false;
  4406.                         if (!layout_position_absolute(c, c, 0, 0, content))
  4407.                                 return false;
  4408.                 } else if (c->style && css_computed_position(c->style) ==
  4409.                                 CSS_POSITION_RELATIVE) {
  4410.                         if (!layout_position_absolute(c, c, 0, 0, content))
  4411.                                 return false;
  4412.                 } else {
  4413.                         int px, py;
  4414.                         if (c->style && (css_computed_float(c->style) ==
  4415.                                                 CSS_FLOAT_LEFT ||
  4416.                                         css_computed_float(c->style) ==
  4417.                                                 CSS_FLOAT_RIGHT)) {
  4418.                                 /* Float x/y coords are relative to nearest
  4419.                                  * ansestor with float_children, rather than
  4420.                                  * relative to parent. Need to get x/y relative
  4421.                                  * to parent */
  4422.                                 struct box *p;
  4423.                                 px = c->x;
  4424.                                 py = c->y;
  4425.                                 for (p = box->parent; p && !p->float_children;
  4426.                                                 p = p->parent) {
  4427.                                         px -= p->x;
  4428.                                         py -= p->y;
  4429.                                 }
  4430.                         } else {
  4431.                                 /* Not a float, so box x/y coords are relative
  4432.                                  * to parent */
  4433.                                 px = c->x;
  4434.                                 py = c->y;
  4435.                         }
  4436.                         if (!layout_position_absolute(c, containing_block,
  4437.                                         cx + px, cy + py, content))
  4438.                                 return false;
  4439.                 }
  4440.         }
  4441.  
  4442.         return true;
  4443. }
  4444.  
  4445.  
  4446. /**
  4447.  * Layout and position an absolutely positioned box.
  4448.  *
  4449.  * \param  box               absolute box to layout and position
  4450.  * \param  containing_block  containing block
  4451.  * \param  cx                position of box relative to containing_block
  4452.  * \param  cy                position of box relative to containing_block
  4453.  * \param  content           memory pool for any new boxes
  4454.  * \return  true on success, false on memory exhaustion
  4455.  */
  4456.  
  4457. bool layout_absolute(struct box *box, struct box *containing_block,
  4458.                 int cx, int cy,
  4459.                 html_content *content)
  4460. {
  4461.         int static_left, static_top;  /* static position */
  4462.         int top, right, bottom, left;
  4463.         int width, height, max_width, min_width;
  4464.         int *margin = box->margin;
  4465.         int *padding = box->padding;
  4466.         struct box_border *border = box->border;
  4467.         int available_width = containing_block->width;
  4468.         int space;
  4469.  
  4470.         assert(box->type == BOX_BLOCK || box->type == BOX_TABLE ||
  4471.                         box->type == BOX_INLINE_BLOCK);
  4472.  
  4473.         /* The static position is where the box would be if it was not
  4474.          * absolutely positioned. The x and y are filled in by
  4475.          * layout_block_context(). */
  4476.         static_left = cx + box->x;
  4477.         static_top = cy + box->y;
  4478.  
  4479.         if (containing_block->type == BOX_BLOCK ||
  4480.                         containing_block->type == BOX_INLINE_BLOCK ||
  4481.                         containing_block->type == BOX_TABLE_CELL) {
  4482.                 /* Block level container => temporarily increase containing
  4483.                  * block dimensions to include padding (we restore this
  4484.                  * again at the end) */
  4485.                 containing_block->width += containing_block->padding[LEFT] +
  4486.                                 containing_block->padding[RIGHT];
  4487.                 containing_block->height += containing_block->padding[TOP] +
  4488.                                 containing_block->padding[BOTTOM];
  4489.         } else {
  4490.                 /** \todo inline containers */
  4491.         }
  4492.  
  4493.         layout_compute_offsets(box, containing_block,
  4494.                         &top, &right, &bottom, &left);
  4495.  
  4496.         /* Pass containing block into layout_find_dimensions via the float
  4497.          * containing block box member. This is unused for absolutely positioned
  4498.          * boxes because a box can't be floated and absolutely positioned. */
  4499.         box->float_container = containing_block;
  4500.         layout_find_dimensions(available_width, -1, box, box->style,
  4501.                         &width, &height, &max_width, &min_width,
  4502.                         margin, padding, border);
  4503.         box->float_container = NULL;
  4504.  
  4505.         /* 10.3.7 */
  4506. #ifdef LAYOUT_DEBUG
  4507.         LOG(("%i + %i + %i + %i + %i + %i + %i + %i + %i = %i",
  4508.                         left, margin[LEFT], border[LEFT].width,
  4509.                         padding[LEFT], width, padding[RIGHT],
  4510.                         border[RIGHT].width, margin[RIGHT], right,
  4511.                         containing_block->width));
  4512. #endif
  4513.  
  4514.         if (left == AUTO && width == AUTO && right == AUTO) {
  4515.                 if (margin[LEFT] == AUTO)
  4516.                         margin[LEFT] = 0;
  4517.                 if (margin[RIGHT] == AUTO)
  4518.                         margin[RIGHT] = 0;
  4519.                 left = static_left;
  4520.  
  4521.                 width = min(max(box->min_width, available_width),
  4522.                         box->max_width);
  4523.                 width -= box->margin[LEFT] + box->border[LEFT].width +
  4524.                         box->padding[LEFT] + box->padding[RIGHT] +
  4525.                         box->border[RIGHT].width + box->margin[RIGHT];
  4526.  
  4527.                 /* Adjust for {min|max}-width */
  4528.                 if (max_width >= 0 && width > max_width) width = max_width;
  4529.                 if (min_width >  0 && width < min_width) width = min_width;
  4530.  
  4531.                 right = containing_block->width -
  4532.                         left -
  4533.                         margin[LEFT] - border[LEFT].width - padding[LEFT] -
  4534.                         width -
  4535.                         padding[RIGHT] - border[RIGHT].width - margin[RIGHT];
  4536.         } else if (left != AUTO && width != AUTO && right != AUTO) {
  4537.  
  4538.                 /* Adjust for {min|max}-width */
  4539.                 if (max_width >= 0 && width > max_width) width = max_width;
  4540.                 if (min_width >  0 && width < min_width) width = min_width;
  4541.  
  4542.                 if (margin[LEFT] == AUTO && margin[RIGHT] == AUTO) {
  4543.                         space = containing_block->width -
  4544.                                         left - border[LEFT].width -
  4545.                                         padding[LEFT] - width - padding[RIGHT] -
  4546.                                         border[RIGHT].width - right;
  4547.                         if (space < 0) {
  4548.                                 margin[LEFT] = 0;
  4549.                                 margin[RIGHT] = space;
  4550.                         } else {
  4551.                                 margin[LEFT] = margin[RIGHT] = space / 2;
  4552.                         }
  4553.                 } else if (margin[LEFT] == AUTO) {
  4554.                         margin[LEFT] = containing_block->width -
  4555.                                         left - border[LEFT].width -
  4556.                                         padding[LEFT] - width - padding[RIGHT] -
  4557.                                         border[RIGHT].width - margin[RIGHT] -
  4558.                                         right;
  4559.                 } else if (margin[RIGHT] == AUTO) {
  4560.                         margin[RIGHT] = containing_block->width -
  4561.                                         left - margin[LEFT] -
  4562.                                         border[LEFT].width -
  4563.                                         padding[LEFT] - width - padding[RIGHT] -
  4564.                                         border[RIGHT].width - right;
  4565.                 } else {
  4566.                         right = containing_block->width -
  4567.                                         left - margin[LEFT] -
  4568.                                         border[LEFT].width -
  4569.                                         padding[LEFT] - width - padding[RIGHT] -
  4570.                                         border[RIGHT].width - margin[RIGHT];
  4571.                 }
  4572.         } else {
  4573.                 if (margin[LEFT] == AUTO)
  4574.                         margin[LEFT] = 0;
  4575.                 if (margin[RIGHT] == AUTO)
  4576.                         margin[RIGHT] = 0;
  4577.  
  4578.                 if (left == AUTO && width == AUTO && right != AUTO) {
  4579.                         available_width -= right;
  4580.  
  4581.                         width = min(max(box->min_width, available_width),
  4582.                                 box->max_width);
  4583.                         width -= box->margin[LEFT] + box->border[LEFT].width +
  4584.                                 box->padding[LEFT] + box->padding[RIGHT] +
  4585.                                 box->border[RIGHT].width + box->margin[RIGHT];
  4586.  
  4587.                         /* Adjust for {min|max}-width */
  4588.                         if (max_width >= 0 && width > max_width)
  4589.                                 width = max_width;
  4590.                         if (min_width >  0 && width < min_width)
  4591.                                 width = min_width;
  4592.  
  4593.                         left = containing_block->width -
  4594.                                         margin[LEFT] - border[LEFT].width -
  4595.                                         padding[LEFT] - width - padding[RIGHT] -
  4596.                                         border[RIGHT].width - margin[RIGHT] -
  4597.                                         right;
  4598.                 } else if (left == AUTO && width != AUTO && right == AUTO) {
  4599.  
  4600.                         /* Adjust for {min|max}-width */
  4601.                         if (max_width >= 0 && width > max_width)
  4602.                                 width = max_width;
  4603.                         if (min_width >  0 && width < min_width)
  4604.                                 width = min_width;
  4605.  
  4606.                         left = static_left;
  4607.                         right = containing_block->width -
  4608.                                         left - margin[LEFT] -
  4609.                                         border[LEFT].width -
  4610.                                         padding[LEFT] - width - padding[RIGHT] -
  4611.                                         border[RIGHT].width - margin[RIGHT];
  4612.                 } else if (left != AUTO && width == AUTO && right == AUTO) {
  4613.                         available_width -= left;
  4614.  
  4615.                         width = min(max(box->min_width, available_width),
  4616.                                 box->max_width);
  4617.                         width -= box->margin[LEFT] + box->border[LEFT].width +
  4618.                                 box->padding[LEFT] + box->padding[RIGHT] +
  4619.                                 box->border[RIGHT].width + box->margin[RIGHT];
  4620.  
  4621.                         /* Adjust for {min|max}-width */
  4622.                         if (max_width >= 0 && width > max_width)
  4623.                                 width = max_width;
  4624.                         if (min_width >  0 && width < min_width)
  4625.                                 width = min_width;
  4626.  
  4627.                         right = containing_block->width -
  4628.                                         left - margin[LEFT] -
  4629.                                         border[LEFT].width -
  4630.                                         padding[LEFT] - width - padding[RIGHT] -
  4631.                                         border[RIGHT].width - margin[RIGHT];
  4632.                 } else if (left == AUTO && width != AUTO && right != AUTO) {
  4633.  
  4634.                         /* Adjust for {min|max}-width */
  4635.                         if (max_width >= 0 && width > max_width)
  4636.                                 width = max_width;
  4637.                         if (min_width >  0 && width < min_width)
  4638.                                 width = min_width;
  4639.  
  4640.                         left = containing_block->width -
  4641.                                         margin[LEFT] - border[LEFT].width -
  4642.                                         padding[LEFT] - width - padding[RIGHT] -
  4643.                                         border[RIGHT].width - margin[RIGHT] -
  4644.                                         right;
  4645.                 } else if (left != AUTO && width == AUTO && right != AUTO) {
  4646.                         width = containing_block->width -
  4647.                                         left - margin[LEFT] -
  4648.                                         border[LEFT].width -
  4649.                                         padding[LEFT] - padding[RIGHT] -
  4650.                                         border[RIGHT].width - margin[RIGHT] -
  4651.                                         right;
  4652.  
  4653.                         /* Adjust for {min|max}-width */
  4654.                         if (max_width >= 0 && width > max_width)
  4655.                                 width = max_width;
  4656.                         if (min_width >  0 && width < min_width)
  4657.                                 width = min_width;
  4658.  
  4659.                 } else if (left != AUTO && width != AUTO && right == AUTO) {
  4660.  
  4661.                         /* Adjust for {min|max}-width */
  4662.                         if (max_width >= 0 && width > max_width)
  4663.                                 width = max_width;
  4664.                         if (min_width >  0 && width < min_width)
  4665.                                 width = min_width;
  4666.  
  4667.                         right = containing_block->width -
  4668.                                         left - margin[LEFT] -
  4669.                                         border[LEFT].width -
  4670.                                         padding[LEFT] - width - padding[RIGHT] -
  4671.                                         border[RIGHT].width - margin[RIGHT];
  4672.                 }
  4673.         }
  4674.  
  4675. #ifdef LAYOUT_DEBUG
  4676.         LOG(("%i + %i + %i + %i + %i + %i + %i + %i + %i = %i",
  4677.                         left, margin[LEFT], border[LEFT].width, padding[LEFT],
  4678.                         width, padding[RIGHT], border[RIGHT].width,
  4679.                         margin[RIGHT], right,
  4680.                         containing_block->width));
  4681. #endif
  4682.  
  4683.         box->x = left + margin[LEFT] + border[LEFT].width - cx;
  4684.         if (containing_block->type == BOX_BLOCK ||
  4685.                         containing_block->type == BOX_INLINE_BLOCK ||
  4686.                         containing_block->type == BOX_TABLE_CELL) {
  4687.                 /* Block-level ancestor => reset container's width */
  4688.                 containing_block->width -= containing_block->padding[LEFT] +
  4689.                                 containing_block->padding[RIGHT];
  4690.         } else {
  4691.                 /** \todo inline ancestors */
  4692.         }
  4693.         box->width = width;
  4694.         box->height = height;
  4695.  
  4696.         if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
  4697.                         box->object || box->flags & IFRAME) {
  4698.                 if (!layout_block_context(box, -1, content))
  4699.                         return false;
  4700.         } else if (box->type == BOX_TABLE) {
  4701.                 /* layout_table also expects the containing block to be
  4702.                  * stored in the float_container field */
  4703.                 box->float_container = containing_block;
  4704.                 /* \todo  layout_table considers margins etc. again */
  4705.                 if (!layout_table(box, width, content))
  4706.                         return false;
  4707.                 box->float_container = NULL;
  4708.                 layout_solve_width(box, box->parent->width, box->width, 0, 0,
  4709.                                 -1, -1);
  4710.         }
  4711.  
  4712.         /* 10.6.4 */
  4713. #ifdef LAYOUT_DEBUG
  4714.         LOG(("%i + %i + %i + %i + %i + %i + %i + %i + %i = %i",
  4715.                         top, margin[TOP], border[TOP].width, padding[TOP],
  4716.                         height, padding[BOTTOM], border[BOTTOM].width,
  4717.                         margin[BOTTOM], bottom,
  4718.                         containing_block->height));
  4719. #endif
  4720.  
  4721.         if (top == AUTO && height == AUTO && bottom == AUTO) {
  4722.                 top = static_top;
  4723.                 height = box->height;
  4724.                 if (margin[TOP] == AUTO)
  4725.                         margin[TOP] = 0;
  4726.                 if (margin[BOTTOM] == AUTO)
  4727.                         margin[BOTTOM] = 0;
  4728.                 bottom = containing_block->height -
  4729.                                 top - margin[TOP] - border[TOP].width -
  4730.                                 padding[TOP] - height - padding[BOTTOM] -
  4731.                                 border[BOTTOM].width - margin[BOTTOM];
  4732.         } else if (top != AUTO && height != AUTO && bottom != AUTO) {
  4733.                 if (margin[TOP] == AUTO && margin[BOTTOM] == AUTO) {
  4734.                         space = containing_block->height -
  4735.                                         top - border[TOP].width - padding[TOP] -
  4736.                                         height - padding[BOTTOM] -
  4737.                                         border[BOTTOM].width - bottom;
  4738.                         margin[TOP] = margin[BOTTOM] = space / 2;
  4739.                 } else if (margin[TOP] == AUTO) {
  4740.                         margin[TOP] = containing_block->height -
  4741.                                         top - border[TOP].width - padding[TOP] -
  4742.                                         height - padding[BOTTOM] -
  4743.                                         border[BOTTOM].width - margin[BOTTOM] -
  4744.                                         bottom;
  4745.                 } else if (margin[BOTTOM] == AUTO) {
  4746.                         margin[BOTTOM] = containing_block->height -
  4747.                                         top - margin[TOP] - border[TOP].width -
  4748.                                         padding[TOP] - height -
  4749.                                         padding[BOTTOM] - border[BOTTOM].width -
  4750.                                         bottom;
  4751.                 } else {
  4752.                         bottom = containing_block->height -
  4753.                                         top - margin[TOP] - border[TOP].width -
  4754.                                         padding[TOP] - height -
  4755.                                         padding[BOTTOM] - border[BOTTOM].width -
  4756.                                         margin[BOTTOM];
  4757.                 }
  4758.         } else {
  4759.                 if (margin[TOP] == AUTO)
  4760.                         margin[TOP] = 0;
  4761.                 if (margin[BOTTOM] == AUTO)
  4762.                         margin[BOTTOM] = 0;
  4763.                 if (top == AUTO && height == AUTO && bottom != AUTO) {
  4764.                         height = box->height;
  4765.                         top = containing_block->height -
  4766.                                         margin[TOP] - border[TOP].width -
  4767.                                         padding[TOP] - height -
  4768.                                         padding[BOTTOM] - border[BOTTOM].width -
  4769.                                         margin[BOTTOM] - bottom;
  4770.                 } else if (top == AUTO && height != AUTO && bottom == AUTO) {
  4771.                         top = static_top;
  4772.                         bottom = containing_block->height -
  4773.                                         top - margin[TOP] - border[TOP].width -
  4774.                                         padding[TOP] - height -
  4775.                                         padding[BOTTOM] - border[BOTTOM].width -
  4776.                                         margin[BOTTOM];
  4777.                 } else if (top != AUTO && height == AUTO && bottom == AUTO) {
  4778.                         height = box->height;
  4779.                         bottom = containing_block->height -
  4780.                                         top - margin[TOP] - border[TOP].width -
  4781.                                         padding[TOP] - height -
  4782.                                         padding[BOTTOM] - border[BOTTOM].width -
  4783.                                         margin[BOTTOM];
  4784.                 } else if (top == AUTO && height != AUTO && bottom != AUTO) {
  4785.                         top = containing_block->height -
  4786.                                         margin[TOP] - border[TOP].width -
  4787.                                         padding[TOP] - height -
  4788.                                         padding[BOTTOM] - border[BOTTOM].width -
  4789.                                         margin[BOTTOM] - bottom;
  4790.                 } else if (top != AUTO && height == AUTO && bottom != AUTO) {
  4791.                         height = containing_block->height -
  4792.                                         top - margin[TOP] - border[TOP].width -
  4793.                                         padding[TOP] - padding[BOTTOM] -
  4794.                                         border[BOTTOM].width - margin[BOTTOM] -
  4795.                                         bottom;
  4796.                 } else if (top != AUTO && height != AUTO && bottom == AUTO) {
  4797.                         bottom = containing_block->height -
  4798.                                         top - margin[TOP] - border[TOP].width -
  4799.                                         padding[TOP] - height -
  4800.                                         padding[BOTTOM] - border[BOTTOM].width -
  4801.                                         margin[BOTTOM];
  4802.                 }
  4803.         }
  4804.  
  4805. #ifdef LAYOUT_DEBUG
  4806.         LOG(("%i + %i + %i + %i + %i + %i + %i + %i + %i = %i",
  4807.                         top, margin[TOP], border[TOP].width, padding[TOP],
  4808.                         height, padding[BOTTOM], border[BOTTOM].width,
  4809.                         margin[BOTTOM], bottom,
  4810.                         containing_block->height));
  4811. #endif
  4812.  
  4813.         box->y = top + margin[TOP] + border[TOP].width - cy;
  4814.         if (containing_block->type == BOX_BLOCK ||
  4815.                         containing_block->type == BOX_INLINE_BLOCK ||
  4816.                         containing_block->type == BOX_TABLE_CELL) {
  4817.                 /* Block-level ancestor => reset container's height */
  4818.                 containing_block->height -= containing_block->padding[TOP] +
  4819.                                 containing_block->padding[BOTTOM];
  4820.         } else {
  4821.                 /** \todo Inline ancestors */
  4822.         }
  4823.         box->height = height;
  4824.         layout_apply_minmax_height(box, containing_block);
  4825.  
  4826.         return true;
  4827. }
  4828.  
  4829.  
  4830. /**
  4831.  * Compute box offsets for a relatively or absolutely positioned box with
  4832.  * respect to a box.
  4833.  *
  4834.  * \param  box               box to compute offsets for
  4835.  * \param  containing_block  box to compute percentages with respect to
  4836.  * \param  top               updated to top offset, or AUTO
  4837.  * \param  right             updated to right offset, or AUTO
  4838.  * \param  bottom            updated to bottom offset, or AUTO
  4839.  * \param  left              updated to left offset, or AUTO
  4840.  *
  4841.  * See CSS 2.1 9.3.2. containing_block must have width and height.
  4842.  */
  4843.  
  4844. void layout_compute_offsets(struct box *box,
  4845.                 struct box *containing_block,
  4846.                 int *top, int *right, int *bottom, int *left)
  4847. {
  4848.         uint32_t type;
  4849.         css_fixed value = 0;
  4850.         css_unit unit = CSS_UNIT_PX;
  4851.  
  4852.         assert(containing_block->width != UNKNOWN_WIDTH &&
  4853.                         containing_block->width != AUTO &&
  4854.                         containing_block->height != AUTO);
  4855.  
  4856.         /* left */
  4857.         type = css_computed_left(box->style, &value, &unit);
  4858.         if (type == CSS_LEFT_SET) {
  4859.                 if (unit == CSS_UNIT_PCT) {
  4860.                         *left = FPCT_OF_INT_TOINT(value,
  4861.                                         containing_block->width);
  4862.                 } else {
  4863.                         *left = FIXTOINT(nscss_len2px(value, unit, box->style));
  4864.                 }
  4865.         } else {
  4866.                 *left = AUTO;
  4867.         }
  4868.  
  4869.         /* right */
  4870.         type = css_computed_right(box->style, &value, &unit);
  4871.         if (type == CSS_RIGHT_SET) {
  4872.                 if (unit == CSS_UNIT_PCT) {
  4873.                         *right = FPCT_OF_INT_TOINT(value,
  4874.                                         containing_block->width);
  4875.                 } else {
  4876.                         *right = FIXTOINT(nscss_len2px(value, unit,
  4877.                                         box->style));
  4878.                 }
  4879.         } else {
  4880.                 *right = AUTO;
  4881.         }
  4882.  
  4883.         /* top */
  4884.         type = css_computed_top(box->style, &value, &unit);
  4885.         if (type == CSS_TOP_SET) {
  4886.                 if (unit == CSS_UNIT_PCT) {
  4887.                         *top = FPCT_OF_INT_TOINT(value,
  4888.                                         containing_block->height);
  4889.                 } else {
  4890.                         *top = FIXTOINT(nscss_len2px(value, unit, box->style));
  4891.                 }
  4892.         } else {
  4893.                 *top = AUTO;
  4894.         }
  4895.  
  4896.         /* bottom */
  4897.         type = css_computed_bottom(box->style, &value, &unit);
  4898.         if (type == CSS_BOTTOM_SET) {
  4899.                 if (unit == CSS_UNIT_PCT) {
  4900.                         *bottom = FPCT_OF_INT_TOINT(value,
  4901.                                         containing_block->height);
  4902.                 } else {
  4903.                         *bottom = FIXTOINT(nscss_len2px(value, unit,
  4904.                                         box->style));
  4905.                 }
  4906.         } else {
  4907.                 *bottom = AUTO;
  4908.         }
  4909. }
  4910.  
  4911.  
  4912. /**
  4913.  * Find a box's bounding box relative to itself, i.e. the box's border edge box
  4914.  *
  4915.  * \param  box      box find bounding box of
  4916.  * \param  desc_x0  updated to left of box's bbox
  4917.  * \param  desc_y0  updated to top of box's bbox
  4918.  * \param  desc_x1  updated to right of box's bbox
  4919.  * \param  desc_y1  updated to bottom of box's bbox
  4920.  */
  4921.  
  4922. static void layout_get_box_bbox(struct box *box, int *desc_x0, int *desc_y0,
  4923.                 int *desc_x1, int *desc_y1)
  4924. {
  4925.         *desc_x0 = -box->border[LEFT].width;
  4926.         *desc_y0 = -box->border[TOP].width;
  4927.         *desc_x1 = box->padding[LEFT] + box->width + box->padding[RIGHT] +
  4928.                         box->border[RIGHT].width;
  4929.         *desc_y1 = box->padding[TOP] + box->height + box->padding[BOTTOM] +
  4930.                         box->border[BOTTOM].width;
  4931. }
  4932.  
  4933.  
  4934. /**
  4935.  * Apply changes to box descendant_[xy][01] values due to given child.
  4936.  *
  4937.  * \param  box    box to update
  4938.  * \param  child  a box, which may affect box's descendant bbox
  4939.  * \param  off_x  offset to apply to child->x coord to treat as child of box
  4940.  * \param  off_y  offset to apply to child->y coord to treat as child of box
  4941.  */
  4942.  
  4943. static void layout_update_descendant_bbox(struct box *box, struct box *child,
  4944.                 int off_x, int off_y)
  4945. {
  4946.         int child_desc_x0, child_desc_y0, child_desc_x1, child_desc_y1;
  4947.  
  4948.         /* get coordinates of child relative to box */
  4949.         int child_x = child->x - off_x;
  4950.         int child_y = child->y - off_y;
  4951.  
  4952.         bool html_object = (child->object &&
  4953.                         content_get_type(child->object) == CONTENT_HTML);
  4954.  
  4955.         if (child->style == NULL ||
  4956.                         (child->style && css_computed_overflow(child->style) ==
  4957.                         CSS_OVERFLOW_VISIBLE && html_object == false)) {
  4958.                 /* get child's descendant bbox relative to box */
  4959.                 child_desc_x0 = child_x + child->descendant_x0;
  4960.                 child_desc_y0 = child_y + child->descendant_y0;
  4961.                 child_desc_x1 = child_x + child->descendant_x1;
  4962.                 child_desc_y1 = child_y + child->descendant_y1;
  4963.         } else {
  4964.                 /* child's descendants don't matter; use child's border edge */
  4965.                 layout_get_box_bbox(child, &child_desc_x0, &child_desc_y0,
  4966.                                 &child_desc_x1, &child_desc_y1);
  4967.                 /* get the bbox relative to box */
  4968.                 child_desc_x0 += child_x;
  4969.                 child_desc_y0 += child_y;
  4970.                 child_desc_x1 += child_x;
  4971.                 child_desc_y1 += child_y;
  4972.         }
  4973.  
  4974.         /* increase box's descendant bbox to contain descendants */
  4975.         if (child_desc_x0 < box->descendant_x0)
  4976.                 box->descendant_x0 = child_desc_x0;
  4977.         if (child_desc_y0 < box->descendant_y0)
  4978.                 box->descendant_y0 = child_desc_y0;
  4979.         if (box->descendant_x1 < child_desc_x1)
  4980.                 box->descendant_x1 = child_desc_x1;
  4981.         if (box->descendant_y1 < child_desc_y1)
  4982.                 box->descendant_y1 = child_desc_y1;
  4983. }
  4984.  
  4985.  
  4986. /**
  4987.  * Recursively calculate the descendant_[xy][01] values for a laid-out box tree
  4988.  * and inform iframe browser windows of their size and position.
  4989.  *
  4990.  * \param  box  tree of boxes to update
  4991.  */
  4992.  
  4993. void layout_calculate_descendant_bboxes(struct box *box)
  4994. {
  4995.         struct box *child;
  4996.  
  4997.         assert((box->width != UNKNOWN_WIDTH) && (box->height != AUTO));
  4998.         /* assert((box->width >= 0) && (box->height >= 0)); */
  4999.  
  5000.         /* Initialise box's descendant box to border edge box */
  5001.         layout_get_box_bbox(box, &box->descendant_x0, &box->descendant_y0,
  5002.                         &box->descendant_x1, &box->descendant_y1);
  5003.  
  5004.         /* Extend it to contain HTML contents if box is replaced */
  5005.         if (box->object && content_get_type(box->object) == CONTENT_HTML) {
  5006.                 if (box->descendant_x1 < content_get_width(box->object))
  5007.                         box->descendant_x1 = content_get_width(box->object);
  5008.                 if (box->descendant_y1 < content_get_height(box->object))
  5009.                         box->descendant_y1 = content_get_height(box->object);
  5010.         }
  5011.  
  5012.         if (box->iframe != NULL) {
  5013.                 int x, y;
  5014.                 box_coords(box, &x, &y);
  5015.  
  5016.                 browser_window_set_position(box->iframe, x, y);
  5017.                 browser_window_set_dimensions(box->iframe,
  5018.                                 box->width, box->height);
  5019.                 browser_window_reformat(box->iframe, true,
  5020.                                 box->width, box->height);
  5021.         }
  5022.  
  5023.         if (box->type == BOX_INLINE || box->type == BOX_TEXT)
  5024.                 return;
  5025.  
  5026.         if (box->type == BOX_INLINE_END) {
  5027.                 box = box->inline_end;
  5028.                 for (child = box->next; child;
  5029.                                 child = child->next) {
  5030.                         if (child->type == BOX_FLOAT_LEFT ||
  5031.                                         child->type == BOX_FLOAT_RIGHT)
  5032.                                 continue;
  5033.  
  5034.                         layout_update_descendant_bbox(box, child,
  5035.                                         box->x, box->y);
  5036.  
  5037.                         if (child == box->inline_end)
  5038.                                 break;
  5039.                 }
  5040.                 return;
  5041.         }
  5042.  
  5043.         if (box->flags & REPLACE_DIM)
  5044.                 /* Box's children aren't displayed if the box is replaced */
  5045.                 return;
  5046.  
  5047.         for (child = box->children; child; child = child->next) {
  5048.                 if (child->type == BOX_FLOAT_LEFT ||
  5049.                                 child->type == BOX_FLOAT_RIGHT)
  5050.                         continue;
  5051.  
  5052.                 layout_calculate_descendant_bboxes(child);
  5053.  
  5054.                 if (box->style && css_computed_overflow(box->style) ==
  5055.                                 CSS_OVERFLOW_HIDDEN)
  5056.                         continue;
  5057.  
  5058.                 layout_update_descendant_bbox(box, child, 0, 0);
  5059.         }
  5060.  
  5061.         for (child = box->float_children; child; child = child->next_float) {
  5062.                 assert(child->type == BOX_FLOAT_LEFT ||
  5063.                                 child->type == BOX_FLOAT_RIGHT);
  5064.  
  5065.                 layout_calculate_descendant_bboxes(child);
  5066.  
  5067.                 layout_update_descendant_bbox(box, child, 0, 0);
  5068.         }
  5069.  
  5070.         if (box->list_marker) {
  5071.                 child = box->list_marker;
  5072.                 layout_calculate_descendant_bboxes(child);
  5073.  
  5074.                 layout_update_descendant_bbox(box, child, 0, 0);
  5075.         }
  5076. }
  5077.  
  5078.