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