NetSurf
box_normalise.c
Go to the documentation of this file.
1/*
2 * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
3 * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
4 * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk>
5 * Copyright 2004 Kevin Bagust <kevin.bagust@ntlworld.com>
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 * Box tree normalisation implementation.
25 */
26
27#include <assert.h>
28#include <stdbool.h>
29#include <string.h>
30
31#include "utils/log.h"
32#include "utils/errors.h"
33#include "css/select.h"
34
35#include "html/private.h"
36#include "html/table.h"
37#include "html/box.h"
38#include "html/box_manipulate.h"
39#include "html/box_normalise.h"
40
41/* Define to enable box normalise debug */
42#undef BOX_NORMALISE_DEBUG
43
44/**
45 * Row spanning information for a cell
46 */
47struct span_info {
48 /** Number of rows this cell spans */
49 unsigned int row_span;
50 /** Row group of cell */
51 struct box *rg;
52 /** The cell in this column spans all rows until the end of the table */
54};
55
56/**
57 * Column record for a table
58 */
59struct columns {
60 /** Current column index */
61 unsigned int current_column;
62 /** Number of columns in main part of table 1..max columns */
63 unsigned int num_columns;
64 /** Information about columns in main table, array [0, num_columns) */
66 /** Number of rows in table */
67 unsigned int num_rows;
68};
69
70
71/**
72 * Compute the column index at which the current cell begins.
73 * Additionally, update the column record to reflect row spanning.
74 *
75 * \param col_info Column record
76 * \param col_span Number of columns that current cell spans
77 * \param row_span Number of rows that current cell spans
78 * \param start_column Pointer to location to receive column index
79 * \param cell Box for current table cell
80 * \return true on success, false on memory exhaustion
81 */
82static bool
84 unsigned int col_span,
85 unsigned int row_span,
86 unsigned int *start_column,
87 struct box *cell)
88{
89 unsigned int cell_start_col = col_info->current_column;
90 unsigned int cell_end_col;
91 unsigned int i;
92 struct span_info *spans;
93 struct box *rg = cell->parent->parent; /* Cell's row group */
94
95 /* Skip columns with cells spanning from above */
96 /* TODO: Need to ignore cells spanning from above that belong to
97 * different row group. We don't have that info here. */
98 while (col_info->spans[cell_start_col].row_span != 0 &&
99 col_info->spans[cell_start_col].rg == rg) {
100 cell_start_col++;
101 }
102
103 /* Update current column with calculated start */
104 col_info->current_column = cell_start_col;
105
106 /* If this cell has a colspan of 0, then assume 1.
107 * No other browser supports colspan=0, anyway. */
108 if (col_span == 0)
109 col_span = 1;
110
111 cell_end_col = cell_start_col + col_span;
112
113 if (col_info->num_columns < cell_end_col) {
114 /* It appears that this row has more columns than
115 * the maximum recorded for the table so far.
116 * Allocate more span records. */
117 spans = realloc(col_info->spans,
118 sizeof *spans * (cell_end_col + 1));
119 if (spans == NULL)
120 return false;
121
122 col_info->spans = spans;
123 col_info->num_columns = cell_end_col;
124
125 /* Mark new final column as sentinel */
126 col_info->spans[cell_end_col].row_span = 0;
127 col_info->spans[cell_end_col].auto_row = false;
128 }
129
130 /* This cell may span multiple columns. If it also wants to span
131 * multiple rows, temporarily assume it spans 1 row only. This will
132 * be fixed up in box_normalise_table_spans() */
133 for (i = cell_start_col; i < cell_end_col; i++) {
134 col_info->spans[i].row_span = (row_span == 0) ? 1 : row_span;
135 col_info->spans[i].auto_row = (row_span == 0);
136 col_info->spans[i].rg = rg;
137 }
138
139 /* Update current column with calculated end. */
140 col_info->current_column = cell_end_col;
141
142 *start_column = cell_start_col;
143
144 return true;
145}
146
147
148static bool
150 const struct box *root,
151 struct columns *col_info,
152 html_content * c)
153{
154 struct box *child;
155 struct box *next_child;
156 struct box *cell = NULL;
157 css_computed_style *style;
158 unsigned int i;
160
161 assert(row != NULL);
162 assert(row->type == BOX_TABLE_ROW);
163
164 ctx.root_style = root->style;
165
166#ifdef BOX_NORMALISE_DEBUG
167 NSLOG(netsurf, INFO, "row %p", row);
168#endif
169
170 for (child = row->children; child != NULL; child = next_child) {
171 next_child = child->next;
172
173 switch (child->type) {
174 case BOX_TABLE_CELL:
175 /* ok */
176 if (box_normalise_block(child, root, c) == false)
177 return false;
178 cell = child;
179 break;
180 case BOX_FLEX:
181 case BOX_BLOCK:
183 case BOX_TABLE:
185 case BOX_TABLE_ROW:
186 /* insert implied table cell */
187 assert(row->style != NULL);
188
189 ctx.ctx = c->select_ctx;
190 ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
191 ctx.base_url = c->base_url;
192 ctx.universal = c->universal;
193
195 row->style);
196 if (style == NULL)
197 return false;
198
199 cell = box_create(NULL, style, true, row->href,
200 row->target, NULL, NULL, c->bctx);
201 if (cell == NULL) {
202 css_computed_style_destroy(style);
203 return false;
204 }
205 cell->type = BOX_TABLE_CELL;
206
207 if (child->prev == NULL)
208 row->children = cell;
209 else
210 child->prev->next = cell;
211
212 cell->prev = child->prev;
213
214 while (child != NULL && (
215 child->type == BOX_FLEX ||
216 child->type == BOX_BLOCK ||
217 child->type == BOX_INLINE_CONTAINER ||
218 child->type == BOX_TABLE ||
219 child->type == BOX_TABLE_ROW_GROUP ||
220 child->type == BOX_TABLE_ROW)) {
221 box_add_child(cell, child);
222
223 next_child = child->next;
224 child->next = NULL;
225 child = next_child;
226 }
227
228 assert(cell->last != NULL);
229
230 cell->last->next = NULL;
231 cell->next = next_child = child;
232 if (cell->next != NULL)
233 cell->next->prev = cell;
234 else
235 row->last = cell;
236 cell->parent = row;
237
238 if (box_normalise_block(cell, root, c) == false)
239 return false;
240 break;
241 case BOX_INLINE:
242 case BOX_INLINE_END:
243 case BOX_INLINE_FLEX:
244 case BOX_INLINE_BLOCK:
245 case BOX_FLOAT_LEFT:
246 case BOX_FLOAT_RIGHT:
247 case BOX_BR:
248 case BOX_TEXT:
249 /* should have been wrapped in inline
250 container by convert_xml_to_box() */
251 assert(0);
252 break;
253 default:
254 assert(0);
255 }
256
257 if (calculate_table_row(col_info, cell->columns, cell->rows,
258 &cell->start_column, cell) == false)
259 return false;
260 }
261
262
263 /* Update row spanning details for all columns */
264 for (i = 0; i < col_info->num_columns; i++) {
265 if (col_info->spans[i].row_span != 0 &&
266 col_info->spans[i].auto_row == false) {
267 /* This cell spans rows, and is not an auto row.
268 * Reduce number of rows left to span */
269 col_info->spans[i].row_span--;
270 }
271 }
272
273 /* Reset current column for next row */
274 col_info->current_column = 0;
275
276 /* Increment row counter */
277 col_info->num_rows++;
278
279#ifdef BOX_NORMALISE_DEBUG
280 NSLOG(netsurf, INFO, "row %p done", row);
281#endif
282
283 return true;
284}
285
286
287static bool
289 const struct box *root,
290 struct columns *col_info,
291 html_content * c)
292{
293 struct box *child;
294 struct box *next_child;
295 struct box *row;
296 css_computed_style *style;
298 unsigned int group_row_count = 0;
299
300 assert(row_group != 0);
301 assert(row_group->type == BOX_TABLE_ROW_GROUP);
302
303 ctx.root_style = root->style;
304
305#ifdef BOX_NORMALISE_DEBUG
306 NSLOG(netsurf, INFO, "row_group %p", row_group);
307#endif
308
309 for (child = row_group->children; child != NULL; child = next_child) {
310 next_child = child->next;
311
312 switch (child->type) {
313 case BOX_TABLE_ROW:
314 /* ok */
315 group_row_count++;
316 if (box_normalise_table_row(child, root, col_info,
317 c) == false)
318 return false;
319 break;
320 case BOX_FLEX:
321 case BOX_BLOCK:
323 case BOX_TABLE:
325 case BOX_TABLE_CELL:
326 /* insert implied table row */
327 assert(row_group->style != NULL);
328
329 ctx.ctx = c->select_ctx;
330 ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
331 ctx.base_url = c->base_url;
332 ctx.universal = c->universal;
333
335 row_group->style);
336 if (style == NULL)
337 return false;
338
339 row = box_create(NULL, style, true, row_group->href,
340 row_group->target, NULL, NULL, c->bctx);
341 if (row == NULL) {
342 css_computed_style_destroy(style);
343 return false;
344 }
345 row->type = BOX_TABLE_ROW;
346
347 if (child->prev == NULL)
348 row_group->children = row;
349 else
350 child->prev->next = row;
351
352 row->prev = child->prev;
353
354 while (child != NULL && (
355 child->type == BOX_FLEX ||
356 child->type == BOX_BLOCK ||
357 child->type == BOX_INLINE_CONTAINER ||
358 child->type == BOX_TABLE ||
359 child->type == BOX_TABLE_ROW_GROUP ||
360 child->type == BOX_TABLE_CELL)) {
361 box_add_child(row, child);
362
363 next_child = child->next;
364 child->next = NULL;
365 child = next_child;
366 }
367
368 assert(row->last != NULL);
369
370 row->last->next = NULL;
371 row->next = next_child = child;
372 if (row->next != NULL)
373 row->next->prev = row;
374 else
375 row_group->last = row;
376 row->parent = row_group;
377
378 group_row_count++;
379 if (box_normalise_table_row(row, root, col_info,
380 c) == false)
381 return false;
382 break;
383 case BOX_INLINE:
384 case BOX_INLINE_END:
385 case BOX_INLINE_FLEX:
386 case BOX_INLINE_BLOCK:
387 case BOX_FLOAT_LEFT:
388 case BOX_FLOAT_RIGHT:
389 case BOX_BR:
390 case BOX_TEXT:
391 /* should have been wrapped in inline
392 container by convert_xml_to_box() */
393 assert(0);
394 break;
395 default:
396 assert(0);
397 }
398 }
399
400 if (row_group->children == NULL) {
401#ifdef BOX_NORMALISE_DEBUG
402 NSLOG(netsurf, INFO,
403 "row_group->children == 0, inserting implied row");
404#endif
405
406 assert(row_group->style != NULL);
407
408 ctx.ctx = c->select_ctx;
409 ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
410 ctx.base_url = c->base_url;
411 ctx.universal = c->universal;
412
414 row_group->style);
415 if (style == NULL) {
416 return false;
417 }
418
419 row = box_create(NULL, style, true, row_group->href,
420 row_group->target, NULL, NULL, c->bctx);
421 if (row == NULL) {
422 css_computed_style_destroy(style);
423 return false;
424 }
425 row->type = BOX_TABLE_ROW;
426
427 row->parent = row_group;
428 row_group->children = row_group->last = row;
429
430 group_row_count = 1;
431
432 /* Keep table's row count in sync */
433 col_info->num_rows++;
434 }
435
436 row_group->rows = group_row_count;
437
438#ifdef BOX_NORMALISE_DEBUG
439 NSLOG(netsurf, INFO, "row_group %p done", row_group);
440#endif
441
442 return true;
443}
444
445
446/**
447 * Normalise table cell column/row counts for colspan/rowspan = 0.
448 * Additionally, generate empty cells.
449 *
450 * \param table Table to process
451 * \param root root box of document
452 * \param spans Array of length table->columns for use in empty cell detection
453 * \param c Content containing table
454 * \return True on success, false on memory exhaustion.
455 */
456static bool
458 const struct box *root,
459 struct span_info *spans,
460 html_content *c)
461{
462 struct box *table_row_group;
463 struct box *table_row;
464 struct box *table_cell;
465 unsigned int rows_left = table->rows;
466 unsigned int group_rows_left;
467 unsigned int col;
469
470 ctx.root_style = root->style;
471
472 /* Clear span data */
473 memset(spans, 0, table->columns * sizeof(struct span_info));
474
475 /* Scan table, filling in width and height of table cells with
476 * colspan = 0 and rowspan = 0. Also generate empty cells */
477 for (table_row_group = table->children;
478 table_row_group != NULL;
479 table_row_group = table_row_group->next) {
480
481 group_rows_left = table_row_group->rows;
482
483 for (table_row = table_row_group->children;
484 table_row != NULL;
485 table_row = table_row->next) {
486
487 for (table_cell = table_row->children;
488 table_cell != NULL;
489 table_cell = table_cell->next) {
490
491 /* colspan = 0 -> colspan = 1 */
492 if (table_cell->columns == 0) {
493 table_cell->columns = 1;
494 }
495
496 /* if rowspan is 0 it is expanded to
497 * the number of rows left in the row
498 * group
499 */
500 if (table_cell->rows == 0) {
501 table_cell->rows = group_rows_left;
502 }
503
504 /* limit rowspans within group */
505 if (table_cell->rows > group_rows_left) {
506 table_cell->rows = group_rows_left;
507 }
508
509 /* Record span information */
510 for (col = table_cell->start_column;
511 col < table_cell->start_column +
512 table_cell->columns; col++) {
513 spans[col].row_span = table_cell->rows;
514 }
515 }
516
517 /* Reduce span count of each column */
518 for (col = 0; col < table->columns; col++) {
519 if (spans[col].row_span == 0) {
520 unsigned int start = col;
521 css_computed_style *style;
522 struct box *cell, *prev;
523
524 /* If it's already zero, then we need
525 * to generate an empty cell for the
526 * gap in the row that spans as many
527 * columns as remain blank.
528 */
529 assert(table_row->style != NULL);
530
531 /* Find width of gap */
532 while (col < table->columns &&
533 spans[col].row_span ==
534 0) {
535 col++;
536 }
537
538 ctx.ctx = c->select_ctx;
539 ctx.quirks = (c->quirks ==
540 DOM_DOCUMENT_QUIRKS_MODE_FULL);
541 ctx.base_url = c->base_url;
542 ctx.universal = c->universal;
543
545 &c->unit_len_ctx,
546 table_row->style);
547 if (style == NULL)
548 return false;
549
550 cell = box_create(NULL, style, true,
551 table_row->href,
552 table_row->target,
553 NULL, NULL, c->bctx);
554 if (cell == NULL) {
555 css_computed_style_destroy(
556 style);
557 return false;
558 }
559 cell->type = BOX_TABLE_CELL;
560
561 cell->rows = 1;
562 cell->columns = col - start;
563 cell->start_column = start;
564
565 /* Find place to insert cell */
566 for (prev = table_row->children;
567 prev != NULL;
568 prev = prev->next) {
569 if (prev->start_column +
570 prev->columns ==
571 start)
572 break;
573 if (prev->next == NULL)
574 break;
575 }
576
577 /* Insert it */
578 if (prev == NULL) {
579 if (table_row->children != NULL)
580 table_row->children->
581 prev = cell;
582 else
583 table_row->last = cell;
584
585 cell->next =
586 table_row->children;
587 table_row->children = cell;
588 } else {
589 if (prev->next != NULL)
590 prev->next->prev = cell;
591 else
592 table_row->last = cell;
593
594 cell->next = prev->next;
595 prev->next = cell;
596 cell->prev = prev;
597 }
598 cell->parent = table_row;
599 } else {
600 spans[col].row_span--;
601 }
602 }
603
604 assert(rows_left > 0);
605
606 rows_left--;
607 }
608
609 group_rows_left--;
610 }
611
612 return true;
613}
614
615
616static bool
617box_normalise_table(struct box *table, const struct box *root, html_content * c)
618{
619 struct box *child;
620 struct box *next_child;
621 struct box *row_group;
622 css_computed_style *style;
623 struct columns col_info;
625
626 assert(table != NULL);
627 assert(table->type == BOX_TABLE);
628
629 ctx.root_style = root->style;
630
631#ifdef BOX_NORMALISE_DEBUG
632 NSLOG(netsurf, INFO, "table %p", table);
633#endif
634
635 col_info.num_columns = 1;
636 col_info.current_column = 0;
637 col_info.spans = malloc(2 * sizeof *col_info.spans);
638 if (col_info.spans == NULL)
639 return false;
640
641 col_info.spans[0].row_span = col_info.spans[1].row_span = 0;
642 col_info.spans[0].auto_row = false;
643 col_info.spans[1].auto_row = false;
644 col_info.num_rows = 0;
645
646 for (child = table->children; child != NULL; child = next_child) {
647 next_child = child->next;
648 switch (child->type) {
650 /* ok */
652 &col_info, c) == false) {
653 free(col_info.spans);
654 return false;
655 }
656 break;
657 case BOX_FLEX:
658 case BOX_BLOCK:
660 case BOX_TABLE:
661 case BOX_TABLE_ROW:
662 case BOX_TABLE_CELL:
663 /* insert implied table row group */
664 assert(table->style != NULL);
665
666 ctx.ctx = c->select_ctx;
667 ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
668 ctx.base_url = c->base_url;
669 ctx.universal = c->universal;
670
671 style = nscss_get_blank_style(&ctx, &c->unit_len_ctx,
672 table->style);
673 if (style == NULL) {
674 free(col_info.spans);
675 return false;
676 }
677
678 row_group = box_create(NULL, style, true, table->href,
679 table->target, NULL, NULL, c->bctx);
680 if (row_group == NULL) {
681 css_computed_style_destroy(style);
682 free(col_info.spans);
683 return false;
684 }
685
686 row_group->type = BOX_TABLE_ROW_GROUP;
687
688 if (child->prev == NULL)
689 table->children = row_group;
690 else
691 child->prev->next = row_group;
692
693 row_group->prev = child->prev;
694
695 while (child != NULL && (
696 child->type == BOX_FLEX ||
697 child->type == BOX_BLOCK ||
698 child->type == BOX_INLINE_CONTAINER ||
699 child->type == BOX_TABLE ||
700 child->type == BOX_TABLE_ROW ||
701 child->type == BOX_TABLE_CELL)) {
702 box_add_child(row_group, child);
703
704 next_child = child->next;
705 child->next = NULL;
706 child = next_child;
707 }
708
709 assert(row_group->last != NULL);
710
711 row_group->last->next = NULL;
712 row_group->next = next_child = child;
713 if (row_group->next != NULL)
714 row_group->next->prev = row_group;
715 else
716 table->last = row_group;
717 row_group->parent = table;
718
719 if (box_normalise_table_row_group(row_group, root,
720 &col_info, c) == false) {
721 free(col_info.spans);
722 return false;
723 }
724 break;
725 case BOX_INLINE:
726 case BOX_INLINE_END:
727 case BOX_INLINE_FLEX:
728 case BOX_INLINE_BLOCK:
729 case BOX_FLOAT_LEFT:
730 case BOX_FLOAT_RIGHT:
731 case BOX_BR:
732 case BOX_TEXT:
733 /* should have been wrapped in inline
734 container by convert_xml_to_box() */
735 assert(0);
736 break;
737 default:
738 fprintf(stderr, "%i\n", child->type);
739 assert(0);
740 }
741 }
742
743 table->columns = col_info.num_columns;
744 table->rows = col_info.num_rows;
745
746 if (table->children == NULL) {
747 struct box *row;
748
749#ifdef BOX_NORMALISE_DEBUG
750 NSLOG(netsurf, INFO,
751 "table->children == 0, creating implied row");
752#endif
753
754 assert(table->style != NULL);
755
756 ctx.ctx = c->select_ctx;
757 ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
758 ctx.base_url = c->base_url;
759 ctx.universal = c->universal;
760
762 table->style);
763 if (style == NULL) {
764 free(col_info.spans);
765 return false;
766 }
767
768 row_group = box_create(NULL, style, true, table->href,
769 table->target, NULL, NULL, c->bctx);
770 if (row_group == NULL) {
771 css_computed_style_destroy(style);
772 free(col_info.spans);
773 return false;
774 }
775 row_group->type = BOX_TABLE_ROW_GROUP;
776
778 row_group->style);
779 if (style == NULL) {
780 box_free(row_group);
781 free(col_info.spans);
782 return false;
783 }
784
785 row = box_create(NULL, style, true, row_group->href,
786 row_group->target, NULL, NULL, c->bctx);
787 if (row == NULL) {
788 css_computed_style_destroy(style);
789 box_free(row_group);
790 free(col_info.spans);
791 return false;
792 }
793 row->type = BOX_TABLE_ROW;
794
795 row->parent = row_group;
796 row_group->children = row_group->last = row;
797
798 row_group->parent = table;
799 table->children = table->last = row_group;
800
801 table->rows = 1;
802 }
803
804 if (box_normalise_table_spans(table, root, col_info.spans, c) == false) {
805 free(col_info.spans);
806 return false;
807 }
808
809 free(col_info.spans);
810
811#ifdef BOX_NORMALISE_DEBUG
812 NSLOG(netsurf, INFO, "table %p done", table);
813#endif
814
815 return true;
816}
817
819 struct box *flex_container,
820 const struct box *root,
821 html_content *c)
822{
823 struct box *child;
824 struct box *next_child;
825 struct box *implied_flex_item;
826 css_computed_style *style;
828
829 assert(flex_container != NULL);
830 assert(root != NULL);
831
832 ctx.root_style = root->style;
833
834#ifdef BOX_NORMALISE_DEBUG
835 NSLOG(netsurf, INFO, "flex_container %p, flex_container->type %u",
836 flex_container, flex_container->type);
837#endif
838
839 assert(flex_container->type == BOX_FLEX ||
840 flex_container->type == BOX_INLINE_FLEX);
841
842 for (child = flex_container->children; child != NULL; child = next_child) {
843#ifdef BOX_NORMALISE_DEBUG
844 NSLOG(netsurf, INFO, "child %p, child->type = %d",
845 child, child->type);
846#endif
847
848 next_child = child->next; /* child may be destroyed */
849
850 switch (child->type) {
851 case BOX_FLEX:
852 /* ok */
853 if (box_normalise_flex(child, root, c) == false)
854 return false;
855 break;
856 case BOX_BLOCK:
857 /* ok */
858 if (box_normalise_block(child, root, c) == false)
859 return false;
860 break;
862 /* insert implied flex item */
863 assert(flex_container->style != NULL);
864
865 ctx.ctx = c->select_ctx;
866 ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
867 ctx.base_url = c->base_url;
868 ctx.universal = c->universal;
869
871 flex_container->style);
872 if (style == NULL)
873 return false;
874
875 implied_flex_item = box_create(NULL, style, true,
876 flex_container->href,
877 flex_container->target,
878 NULL, NULL, c->bctx);
879 if (implied_flex_item == NULL) {
880 css_computed_style_destroy(style);
881 return false;
882 }
883 implied_flex_item->type = BOX_BLOCK;
884
885 if (child->prev == NULL)
886 flex_container->children = implied_flex_item;
887 else
888 child->prev->next = implied_flex_item;
889
890 implied_flex_item->prev = child->prev;
891
892 while (child != NULL &&
893 child->type == BOX_INLINE_CONTAINER) {
894 box_add_child(implied_flex_item, child);
895
896 next_child = child->next;
897 child->next = NULL;
898 child = next_child;
899 }
900
901 implied_flex_item->last->next = NULL;
902 implied_flex_item->next = next_child = child;
903 if (implied_flex_item->next != NULL)
904 implied_flex_item->next->prev = implied_flex_item;
905 else
906 flex_container->last = implied_flex_item;
907 implied_flex_item->parent = flex_container;
908
909 if (box_normalise_block(implied_flex_item,
910 root, c) == false)
911 return false;
912 break;
913
914 case BOX_TABLE:
915 if (box_normalise_table(child, root, c) == false)
916 return false;
917 break;
918 case BOX_INLINE:
919 case BOX_INLINE_END:
920 case BOX_INLINE_FLEX:
921 case BOX_INLINE_BLOCK:
922 case BOX_FLOAT_LEFT:
923 case BOX_FLOAT_RIGHT:
924 case BOX_BR:
925 case BOX_TEXT:
926 /* should have been wrapped in inline
927 container by convert_xml_to_box() */
928 assert(0);
929 break;
931 case BOX_TABLE_ROW:
932 case BOX_TABLE_CELL:
933 /* insert implied table */
934 assert(flex_container->style != NULL);
935
936 ctx.ctx = c->select_ctx;
937 ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
938 ctx.base_url = c->base_url;
939 ctx.universal = c->universal;
940
942 flex_container->style);
943 if (style == NULL)
944 return false;
945
946 implied_flex_item = box_create(NULL, style, true,
947 flex_container->href,
948 flex_container->target,
949 NULL, NULL, c->bctx);
950 if (implied_flex_item == NULL) {
951 css_computed_style_destroy(style);
952 return false;
953 }
954 implied_flex_item->type = BOX_TABLE;
955
956 if (child->prev == NULL)
957 flex_container->children = implied_flex_item;
958 else
959 child->prev->next = implied_flex_item;
960
961 implied_flex_item->prev = child->prev;
962
963 while (child != NULL && (
964 child->type == BOX_TABLE_ROW_GROUP ||
965 child->type == BOX_TABLE_ROW ||
966 child->type == BOX_TABLE_CELL)) {
967 box_add_child(implied_flex_item, child);
968
969 next_child = child->next;
970 child->next = NULL;
971 child = next_child;
972 }
973
974 implied_flex_item->last->next = NULL;
975 implied_flex_item->next = next_child = child;
976 if (implied_flex_item->next != NULL)
977 implied_flex_item->next->prev = implied_flex_item;
978 else
979 flex_container->last = implied_flex_item;
980 implied_flex_item->parent = flex_container;
981
982 if (box_normalise_table(implied_flex_item,
983 root, c) == false)
984 return false;
985 break;
986 default:
987 assert(0);
988 }
989 }
990
991 return true;
992}
993
994static bool
996 const struct box *root,
997 html_content * c)
998{
999 struct box *child;
1000 struct box *next_child;
1001
1002 assert(cont != NULL);
1003 assert(cont->type == BOX_INLINE_CONTAINER);
1004
1005#ifdef BOX_NORMALISE_DEBUG
1006 NSLOG(netsurf, INFO, "cont %p", cont);
1007#endif
1008
1009 for (child = cont->children; child != NULL; child = next_child) {
1010 next_child = child->next;
1011 switch (child->type) {
1012 case BOX_INLINE:
1013 case BOX_INLINE_END:
1014 case BOX_BR:
1015 case BOX_TEXT:
1016 /* ok */
1017 break;
1018 case BOX_INLINE_BLOCK:
1019 /* ok */
1020 if (box_normalise_block(child, root, c) == false)
1021 return false;
1022 break;
1023 case BOX_INLINE_FLEX:
1024 /* ok */
1025 if (box_normalise_flex(child, root, c) == false)
1026 return false;
1027 break;
1028 case BOX_FLOAT_LEFT:
1029 case BOX_FLOAT_RIGHT:
1030 /* ok */
1031 assert(child->children != NULL);
1032
1033 switch (child->children->type) {
1034 case BOX_BLOCK:
1035 if (box_normalise_block(child->children, root,
1036 c) == false)
1037 return false;
1038 break;
1039 case BOX_TABLE:
1040 if (box_normalise_table(child->children, root,
1041 c) == false)
1042 return false;
1043 break;
1044 case BOX_FLEX:
1045 if (box_normalise_flex(child->children, root,
1046 c) == false)
1047 return false;
1048 break;
1049 default:
1050 assert(0);
1051 }
1052
1053 if (child->children == NULL) {
1054 /* the child has destroyed itself: remove float */
1055 if (child->prev == NULL)
1056 child->parent->children = child->next;
1057 else
1058 child->prev->next = child->next;
1059 if (child->next != NULL)
1060 child->next->prev = child->prev;
1061 else
1062 child->parent->last = child->prev;
1063
1064 box_free(child);
1065 }
1066 break;
1067 case BOX_FLEX:
1068 case BOX_BLOCK:
1070 case BOX_TABLE:
1072 case BOX_TABLE_ROW:
1073 case BOX_TABLE_CELL:
1074 default:
1075 assert(0);
1076 }
1077 }
1078
1079#ifdef BOX_NORMALISE_DEBUG
1080 NSLOG(netsurf, INFO, "cont %p done", cont);
1081#endif
1082
1083 return true;
1084}
1085
1086/* Exported function documented in html/box_normalise.h */
1087bool
1088box_normalise_block(struct box *block, const struct box *root, html_content *c)
1089{
1090 struct box *child;
1091 struct box *next_child;
1092 struct box *table;
1093 css_computed_style *style;
1094 nscss_select_ctx ctx;
1095
1096 assert(block != NULL);
1097 assert(root != NULL);
1098
1099 ctx.root_style = root->style;
1100
1101#ifdef BOX_NORMALISE_DEBUG
1102 NSLOG(netsurf, INFO, "block %p, block->type %u", block, block->type);
1103#endif
1104
1105 assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK ||
1106 block->type == BOX_TABLE_CELL);
1107
1108 for (child = block->children; child != NULL; child = next_child) {
1109#ifdef BOX_NORMALISE_DEBUG
1110 NSLOG(netsurf, INFO, "child %p, child->type = %d", child,
1111 child->type);
1112#endif
1113
1114 next_child = child->next; /* child may be destroyed */
1115
1116 switch (child->type) {
1117 case BOX_FLEX:
1118 /* ok */
1119 if (box_normalise_flex(child, root, c) == false)
1120 return false;
1121 break;
1122 case BOX_BLOCK:
1123 /* ok */
1124 if (box_normalise_block(child, root, c) == false)
1125 return false;
1126 break;
1128 if (box_normalise_inline_container(child, root, c) == false)
1129 return false;
1130 break;
1131 case BOX_TABLE:
1132 if (box_normalise_table(child, root, c) == false)
1133 return false;
1134 break;
1135 case BOX_INLINE:
1136 case BOX_INLINE_END:
1137 case BOX_INLINE_FLEX:
1138 case BOX_INLINE_BLOCK:
1139 case BOX_FLOAT_LEFT:
1140 case BOX_FLOAT_RIGHT:
1141 case BOX_BR:
1142 case BOX_TEXT:
1143 /* should have been wrapped in inline
1144 container by convert_xml_to_box() */
1145 assert(0);
1146 break;
1148 case BOX_TABLE_ROW:
1149 case BOX_TABLE_CELL:
1150 /* insert implied table */
1151 assert(block->style != NULL);
1152
1153 ctx.ctx = c->select_ctx;
1154 ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
1155 ctx.base_url = c->base_url;
1156 ctx.universal = c->universal;
1157
1159 block->style);
1160 if (style == NULL)
1161 return false;
1162
1163 table = box_create(NULL, style, true, block->href,
1164 block->target, NULL, NULL, c->bctx);
1165 if (table == NULL) {
1166 css_computed_style_destroy(style);
1167 return false;
1168 }
1169 table->type = BOX_TABLE;
1170
1171 if (child->prev == NULL)
1172 block->children = table;
1173 else
1174 child->prev->next = table;
1175
1176 table->prev = child->prev;
1177
1178 while (child != NULL && (
1179 child->type == BOX_TABLE_ROW_GROUP ||
1180 child->type == BOX_TABLE_ROW ||
1181 child->type == BOX_TABLE_CELL)) {
1182 box_add_child(table, child);
1183
1184 next_child = child->next;
1185 child->next = NULL;
1186 child = next_child;
1187 }
1188
1189 table->last->next = NULL;
1190 table->next = next_child = child;
1191 if (table->next != NULL)
1192 table->next->prev = table;
1193 else
1194 block->last = table;
1195 table->parent = block;
1196
1197 if (box_normalise_table(table, root, c) == false)
1198 return false;
1199 break;
1200 default:
1201 assert(0);
1202 }
1203 }
1204
1205 return true;
1206}
Box interface.
@ 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_TABLE_ROW_GROUP
Definition: box.h:62
@ BOX_INLINE_END
Definition: box.h:68
@ BOX_TABLE
Definition: box.h:59
@ BOX_INLINE
Definition: box.h:58
@ BOX_TABLE_ROW
Definition: box.h:60
@ 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
struct box * box_create(css_select_results *styles, css_computed_style *style, bool style_owned, nsurl *href, const char *target, const char *title, lwc_string *id, void *context)
Create a box tree node.
void box_free(struct box *box)
Free a box tree recursively.
void box_add_child(struct box *parent, struct box *child)
Add a child to a box tree node.
Box tree manipulation interface.
static bool box_normalise_flex(struct box *flex_container, const struct box *root, html_content *c)
static bool box_normalise_table(struct box *table, const struct box *root, html_content *c)
static bool box_normalise_table_row(struct box *row, const struct box *root, struct columns *col_info, html_content *c)
static bool calculate_table_row(struct columns *col_info, unsigned int col_span, unsigned int row_span, unsigned int *start_column, struct box *cell)
Compute the column index at which the current cell begins.
Definition: box_normalise.c:83
static bool box_normalise_table_row_group(struct box *row_group, const struct box *root, struct columns *col_info, html_content *c)
static bool box_normalise_table_spans(struct box *table, const struct box *root, struct span_info *spans, html_content *c)
Normalise table cell column/row counts for colspan/rowspan = 0.
bool box_normalise_block(struct box *block, const struct box *root, html_content *c)
Ensure the box tree is correctly nested by adding and removing nodes.
static bool box_normalise_inline_container(struct box *cont, const struct box *root, html_content *c)
HTML Box tree normalise interface.
Error codes.
static struct directory * root
Definition: filename.c:55
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
Private data for text/html content.
css_computed_style * nscss_get_blank_style(nscss_select_ctx *ctx, const css_unit_ctx *unit_len_ctx, const css_computed_style *parent)
Get a blank style.
Definition: select.c:334
Interface to utility string handling.
Node in box tree.
Definition: box.h:177
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 * children
First child box, or NULL.
Definition: box.h:226
struct box * prev
Previous sibling box, or NULL.
Definition: box.h:221
const char * target
Link target, or NULL.
Definition: box.h:381
struct box * last
Last child box, or NULL.
Definition: box.h:231
struct box * next
Next sibling box, or NULL.
Definition: box.h:216
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 nsurl * href
Link, or NULL.
Definition: box.h:376
css_computed_style * style
Style for this box.
Definition: box.h:205
unsigned int rows
Number of rows for TABLE only.
Definition: box.h:397
unsigned int columns
Number of columns for TABLE / TABLE_CELL.
Definition: box.h:392
Column record for a table.
Definition: box_normalise.c:59
unsigned int num_columns
Number of columns in main part of table 1..max columns.
Definition: box_normalise.c:63
unsigned int num_rows
Number of rows in table.
Definition: box_normalise.c:67
unsigned int current_column
Current column index.
Definition: box_normalise.c:61
struct span_info * spans
Information about columns in main table, array [0, num_columns)
Definition: box_normalise.c:65
Data specific to CONTENT_HTML.
Definition: private.h:93
dom_document_quirks_mode quirks
Quirkyness of document.
Definition: private.h:103
int * bctx
A talloc context purely for the render box tree.
Definition: private.h:134
struct nsurl * base_url
Base URL (may be a copy of content->url).
Definition: private.h:111
css_select_ctx * select_ctx
Style selection media specification.
Definition: private.h:159
css_unit_ctx unit_len_ctx
CSS length conversion context for document.
Definition: private.h:163
lwc_string * universal
Definition: private.h:165
Selection context.
Definition: select.h:35
struct nsurl * base_url
Definition: select.h:38
css_select_ctx * ctx
Definition: select.h:36
lwc_string * universal
Definition: select.h:39
const css_computed_style * root_style
Definition: select.h:40
Row spanning information for a cell.
Definition: box_normalise.c:47
unsigned int row_span
Number of rows this cell spans.
Definition: box_normalise.c:49
bool auto_row
The cell in this column spans all rows until the end of the table.
Definition: box_normalise.c:53
struct box * rg
Row group of cell.
Definition: box_normalise.c:51
Interface to HTML table processing and layout.