Subversion Repositories Kolibri OS

Rev

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

  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. /** \file
  23.  * Box tree normalisation (implementation).
  24.  */
  25.  
  26. #include <assert.h>
  27. #include <stdbool.h>
  28. #include <string.h>
  29. #include "css/css.h"
  30. #include "css/select.h"
  31. #include "render/box.h"
  32. #include "render/html_internal.h"
  33. #include "render/table.h"
  34. #include "utils/log.h"
  35.  
  36. /* Define to enable box normalise debug */
  37. #undef BOX_NORMALISE_DEBUG
  38.  
  39. /**
  40.  * Row spanning information for a cell
  41.  */
  42. struct span_info {
  43.         /** Number of rows this cell spans */
  44.         unsigned int row_span;
  45.         /** The cell in this column spans all rows until the end of the table */
  46.         bool auto_row;
  47. };
  48.  
  49. /**
  50.  * Column record for a table
  51.  */
  52. struct columns {
  53.         /** Current column index */
  54.         unsigned int current_column;
  55.         /** Number of columns in main part of table 1..max columns */
  56.         unsigned int num_columns;
  57.         /** Information about columns in main table, array [0, num_columns) */
  58.         struct span_info *spans;
  59.         /** Number of rows in table */
  60.         unsigned int num_rows;
  61. };
  62.  
  63.  
  64. static bool box_normalise_table(struct box *table, html_content *c);
  65. static bool box_normalise_table_spans(struct box *table,
  66.                 struct span_info *spans, html_content *c);
  67. static bool box_normalise_table_row_group(struct box *row_group,
  68.                 struct columns *col_info,
  69.                 html_content *c);
  70. static bool box_normalise_table_row(struct box *row,
  71.                 struct columns *col_info,
  72.                 html_content *c);
  73. static bool calculate_table_row(struct columns *col_info,
  74.                 unsigned int col_span, unsigned int row_span,
  75.                 unsigned int *start_column);
  76. static bool box_normalise_inline_container(struct box *cont, html_content *c);
  77.  
  78. /**
  79.  * Ensure the box tree is correctly nested by adding and removing nodes.
  80.  *
  81.  * \param  block     box of type BLOCK, INLINE_BLOCK, or TABLE_CELL
  82.  * \param  box_pool  pool to allocate new boxes in
  83.  * \return  true on success, false on memory exhaustion
  84.  *
  85.  * The tree is modified to satisfy the following:
  86.  * \code
  87.  * parent               permitted child nodes
  88.  * BLOCK, INLINE_BLOCK  BLOCK, INLINE_CONTAINER, TABLE
  89.  * INLINE_CONTAINER     INLINE, INLINE_BLOCK, FLOAT_LEFT, FLOAT_RIGHT, BR, TEXT
  90.  * INLINE, TEXT         none
  91.  * TABLE                at least 1 TABLE_ROW_GROUP
  92.  * TABLE_ROW_GROUP      at least 1 TABLE_ROW
  93.  * TABLE_ROW            at least 1 TABLE_CELL
  94.  * TABLE_CELL           BLOCK, INLINE_CONTAINER, TABLE (same as BLOCK)
  95.  * FLOAT_(LEFT|RIGHT)   exactly 1 BLOCK or TABLE
  96.  * \endcode
  97.  */
  98.  
  99. bool box_normalise_block(struct box *block, html_content *c)
  100. {
  101.         struct box *child;
  102.         struct box *next_child;
  103.         struct box *table;
  104.         css_computed_style *style;
  105.         nscss_select_ctx ctx;
  106.  
  107.         assert(block != NULL);
  108.  
  109. #ifdef BOX_NORMALISE_DEBUG
  110.         LOG(("block %p, block->type %u", block, block->type));
  111. #endif
  112.  
  113.         assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK ||
  114.                         block->type == BOX_TABLE_CELL);
  115.  
  116.         for (child = block->children; child != NULL; child = next_child) {
  117. #ifdef BOX_NORMALISE_DEBUG
  118.                 LOG(("child %p, child->type = %d", child, child->type));
  119. #endif
  120.  
  121.                 next_child = child->next;       /* child may be destroyed */
  122.  
  123.                 switch (child->type) {
  124.                 case BOX_BLOCK:
  125.                         /* ok */
  126.                         if (box_normalise_block(child, c) == false)
  127.                                 return false;
  128.                         break;
  129.                 case BOX_INLINE_CONTAINER:
  130.                         if (box_normalise_inline_container(child, c) == false)
  131.                                 return false;
  132.                         break;
  133.                 case BOX_TABLE:
  134.                         if (box_normalise_table(child, c) == false)
  135.                                 return false;
  136.                         break;
  137.                 case BOX_INLINE:
  138.                 case BOX_INLINE_END:
  139.                 case BOX_INLINE_BLOCK:
  140.                 case BOX_FLOAT_LEFT:
  141.                 case BOX_FLOAT_RIGHT:
  142.                 case BOX_BR:
  143.                 case BOX_TEXT:
  144.                         /* should have been wrapped in inline
  145.                            container by convert_xml_to_box() */
  146.                         assert(0);
  147.                         break;
  148.                 case BOX_TABLE_ROW_GROUP:
  149.                 case BOX_TABLE_ROW:
  150.                 case BOX_TABLE_CELL:
  151.                         /* insert implied table */
  152.                         assert(block->style != NULL);
  153.  
  154.                         ctx.ctx = c->select_ctx;
  155.                         ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
  156.                         ctx.base_url = c->base_url;
  157.                         ctx.universal = c->universal;
  158.  
  159.                         style = nscss_get_blank_style(&ctx, block->style,
  160.                                         box_style_alloc, NULL);
  161.                         if (style == NULL)
  162.                                 return false;
  163.  
  164.                         table = box_create(NULL, style, true, block->href,
  165.                                         block->target, NULL, NULL, c->bctx);
  166.                         if (table == NULL) {
  167.                                 css_computed_style_destroy(style);
  168.                                 return false;
  169.                         }
  170.                         table->type = BOX_TABLE;
  171.  
  172.                         if (child->prev == NULL)
  173.                                 block->children = table;
  174.                         else
  175.                                 child->prev->next = table;
  176.  
  177.                         table->prev = child->prev;
  178.  
  179.                         while (child != NULL && (
  180.                                         child->type == BOX_TABLE_ROW_GROUP ||
  181.                                         child->type == BOX_TABLE_ROW ||
  182.                                         child->type == BOX_TABLE_CELL)) {
  183.                                 box_add_child(table, child);
  184.  
  185.                                 next_child = child->next;
  186.                                 child->next = NULL;
  187.                                 child = next_child;
  188.                         }
  189.  
  190.                         table->last->next = NULL;
  191.                         table->next = next_child = child;
  192.                         if (table->next != NULL)
  193.                                 table->next->prev = table;
  194.                         else
  195.                                 block->last = table;
  196.                         table->parent = block;
  197.  
  198.                         if (box_normalise_table(table, c) == false)
  199.                                 return false;
  200.                         break;
  201.                 default:
  202.                         assert(0);
  203.                 }
  204.         }
  205.  
  206.         return true;
  207. }
  208.  
  209.  
  210. bool box_normalise_table(struct box *table, html_content * c)
  211. {
  212.         struct box *child;
  213.         struct box *next_child;
  214.         struct box *row_group;
  215.         css_computed_style *style;
  216.         struct columns col_info;
  217.         nscss_select_ctx ctx;
  218.  
  219.         assert(table != NULL);
  220.         assert(table->type == BOX_TABLE);
  221.  
  222. #ifdef BOX_NORMALISE_DEBUG
  223.         LOG(("table %p", table));
  224. #endif
  225.  
  226.         col_info.num_columns = 1;
  227.         col_info.current_column = 0;
  228.         col_info.spans = malloc(2 * sizeof *col_info.spans);
  229.         if (col_info.spans == NULL)
  230.                 return false;
  231.  
  232.         col_info.spans[0].row_span = col_info.spans[1].row_span = 0;
  233.         col_info.spans[0].auto_row = false;
  234.         col_info.spans[1].auto_row = false;
  235.         col_info.num_rows = 0;
  236.  
  237.         for (child = table->children; child != NULL; child = next_child) {
  238.                 next_child = child->next;
  239.                 switch (child->type) {
  240.                 case BOX_TABLE_ROW_GROUP:
  241.                         /* ok */
  242.                         if (box_normalise_table_row_group(child,
  243.                                         &col_info, c) == false) {
  244.                                 free(col_info.spans);
  245.                                 return false;
  246.                         }
  247.                         break;
  248.                 case BOX_BLOCK:
  249.                 case BOX_INLINE_CONTAINER:
  250.                 case BOX_TABLE:
  251.                 case BOX_TABLE_ROW:
  252.                 case BOX_TABLE_CELL:
  253.                         /* insert implied table row group */
  254.                         assert(table->style != NULL);
  255.  
  256.                         ctx.ctx = c->select_ctx;
  257.                         ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
  258.                         ctx.base_url = c->base_url;
  259.                         ctx.universal = c->universal;
  260.  
  261.                         style = nscss_get_blank_style(&ctx, table->style,
  262.                                         box_style_alloc, NULL);
  263.                         if (style == NULL) {
  264.                                 free(col_info.spans);
  265.                                 return false;
  266.                         }
  267.  
  268.                         row_group = box_create(NULL, style, true, table->href,
  269.                                         table->target, NULL, NULL, c->bctx);
  270.                         if (row_group == NULL) {
  271.                                 css_computed_style_destroy(style);
  272.                                 free(col_info.spans);
  273.                                 return false;
  274.                         }
  275.  
  276.                         row_group->type = BOX_TABLE_ROW_GROUP;
  277.  
  278.                         if (child->prev == NULL)
  279.                                 table->children = row_group;
  280.                         else
  281.                                 child->prev->next = row_group;
  282.  
  283.                         row_group->prev = child->prev;
  284.  
  285.                         while (child != NULL && (
  286.                                         child->type == BOX_BLOCK ||
  287.                                         child->type == BOX_INLINE_CONTAINER ||
  288.                                         child->type == BOX_TABLE ||
  289.                                         child->type == BOX_TABLE_ROW ||
  290.                                         child->type == BOX_TABLE_CELL)) {
  291.                                 box_add_child(row_group, child);
  292.  
  293.                                 next_child = child->next;
  294.                                 child->next = NULL;
  295.                                 child = next_child;
  296.                         }
  297.  
  298.                         assert(row_group->last != NULL);
  299.  
  300.                         row_group->last->next = NULL;
  301.                         row_group->next = next_child = child;
  302.                         if (row_group->next != NULL)
  303.                                 row_group->next->prev = row_group;
  304.                         else
  305.                                 table->last = row_group;
  306.                         row_group->parent = table;
  307.  
  308.                         if (box_normalise_table_row_group(row_group,
  309.                                         &col_info, c) == false) {
  310.                                 free(col_info.spans);
  311.                                 return false;
  312.                         }
  313.                         break;
  314.                 case BOX_INLINE:
  315.                 case BOX_INLINE_END:
  316.                 case BOX_INLINE_BLOCK:
  317.                 case BOX_FLOAT_LEFT:
  318.                 case BOX_FLOAT_RIGHT:
  319.                 case BOX_BR:
  320.                 case BOX_TEXT:
  321.                         /* should have been wrapped in inline
  322.                            container by convert_xml_to_box() */
  323.                         assert(0);
  324.                         break;
  325.                 default:
  326.                         fprintf(stderr, "%i\n", child->type);
  327.                         assert(0);
  328.                 }
  329.         }
  330.  
  331.         table->columns = col_info.num_columns;
  332.         table->rows = col_info.num_rows;
  333.  
  334.         if (table->children == NULL) {
  335.                 struct box *row;
  336.  
  337. #ifdef BOX_NORMALISE_DEBUG
  338.                 LOG(("table->children == 0, creating implied row"));
  339. #endif
  340.  
  341.                 assert(table->style != NULL);
  342.  
  343.                 ctx.ctx = c->select_ctx;
  344.                 ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
  345.                 ctx.base_url = c->base_url;
  346.                 ctx.universal = c->universal;
  347.  
  348.                 style = nscss_get_blank_style(&ctx, table->style,
  349.                                 box_style_alloc, NULL);
  350.                 if (style == NULL) {
  351.                         free(col_info.spans);
  352.                         return false;
  353.                 }
  354.  
  355.                 row_group = box_create(NULL, style, true, table->href,
  356.                                 table->target, NULL, NULL, c->bctx);
  357.                 if (row_group == NULL) {
  358.                         css_computed_style_destroy(style);
  359.                         free(col_info.spans);
  360.                         return false;
  361.                 }
  362.                 row_group->type = BOX_TABLE_ROW_GROUP;
  363.  
  364.                 style = nscss_get_blank_style(&ctx, row_group->style,
  365.                                 box_style_alloc, NULL);
  366.                 if (style == NULL) {
  367.                         box_free(row_group);
  368.                         free(col_info.spans);
  369.                         return false;
  370.                 }
  371.  
  372.                 row = box_create(NULL, style, true, row_group->href,
  373.                                 row_group->target, NULL, NULL, c->bctx);
  374.                 if (row == NULL) {
  375.                         css_computed_style_destroy(style);
  376.                         box_free(row_group);
  377.                         free(col_info.spans);
  378.                         return false;
  379.                 }
  380.                 row->type = BOX_TABLE_ROW;
  381.  
  382.                 row->parent = row_group;
  383.                 row_group->children = row_group->last = row;
  384.  
  385.                 row_group->parent = table;
  386.                 table->children = table->last = row_group;
  387.  
  388.                 table->rows = 1;
  389.         }
  390.  
  391.         if (box_normalise_table_spans(table, col_info.spans, c) == false) {
  392.                 free(col_info.spans);
  393.                 return false;
  394.         }
  395.  
  396.         free(col_info.spans);
  397.  
  398.         if (table_calculate_column_types(table) == false)
  399.                 return false;
  400.  
  401. #ifdef BOX_NORMALISE_DEBUG
  402.         LOG(("table %p done", table));
  403. #endif
  404.  
  405.         return true;
  406. }
  407.  
  408.  
  409. /**
  410.  * Normalise table cell column/row counts for colspan/rowspan = 0.
  411.  * Additionally, generate empty cells.
  412.  *
  413.  * \param table  Table to process
  414.  * \param spans  Array of length table->columns for use in empty cell detection
  415.  * \param c      Content containing table
  416.  * \return True on success, false on memory exhaustion.
  417.  */
  418.  
  419. bool box_normalise_table_spans(struct box *table, struct span_info *spans,
  420.                 html_content *c)
  421. {
  422.         struct box *table_row_group;
  423.         struct box *table_row;
  424.         struct box *table_cell;
  425.         unsigned int rows_left = table->rows;
  426.         unsigned int col;
  427.         nscss_select_ctx ctx;
  428.  
  429.         /* Clear span data */
  430.         memset(spans, 0, table->columns * sizeof(struct span_info));
  431.  
  432.         /* Scan table, filling in width and height of table cells with
  433.          * colspan = 0 and rowspan = 0. Also generate empty cells */
  434.         for (table_row_group = table->children; table_row_group != NULL;
  435.                         table_row_group = table_row_group->next) {
  436.                 for (table_row = table_row_group->children; table_row != NULL;
  437.                                 table_row = table_row->next){
  438.                         for (table_cell = table_row->children;
  439.                                         table_cell != NULL;
  440.                                         table_cell = table_cell->next) {
  441.                                 /* colspan = 0 -> colspan = 1 */
  442.                                 if (table_cell->columns == 0)
  443.                                         table_cell->columns = 1;
  444.  
  445.                                 /* rowspan = 0 -> rowspan = rows_left */
  446.                                 if (table_cell->rows == 0)
  447.                                         table_cell->rows = rows_left;
  448.  
  449.                                 /* Record span information */
  450.                                 for (col = table_cell->start_column;
  451.                                                 col < table_cell->start_column +
  452.                                                 table_cell->columns; col++) {
  453.                                         spans[col].row_span = table_cell->rows;
  454.                                 }
  455.                         }
  456.  
  457.                         /* Reduce span count of each column */
  458.                         for (col = 0; col < table->columns; col++) {
  459.                                 if (spans[col].row_span == 0) {
  460.                                         unsigned int start = col;
  461.                                         css_computed_style *style;
  462.                                         struct box *cell, *prev;
  463.  
  464.                                         /* If it's already zero, then we need
  465.                                          * to generate an empty cell for the
  466.                                          * gap in the row that spans as many
  467.                                          * columns as remain blank.
  468.                                          */
  469.                                         assert(table_row->style != NULL);
  470.  
  471.                                         /* Find width of gap */
  472.                                         while (col < table->columns &&
  473.                                                         spans[col].row_span ==
  474.                                                         0) {
  475.                                                 col++;
  476.                                         }
  477.  
  478.                                         ctx.ctx = c->select_ctx;
  479.                                         ctx.quirks = (c->quirks ==
  480.                                                 DOM_DOCUMENT_QUIRKS_MODE_FULL);
  481.                                         ctx.base_url = c->base_url;
  482.                                         ctx.universal = c->universal;
  483.  
  484.                                         style = nscss_get_blank_style(&ctx,
  485.                                                         table_row->style,
  486.                                                         box_style_alloc, NULL);
  487.                                         if (style == NULL)
  488.                                                 return false;
  489.  
  490.                                         cell = box_create(NULL, style, true,
  491.                                                         table_row->href,
  492.                                                         table_row->target,
  493.                                                         NULL, NULL, c->bctx);
  494.                                         if (cell == NULL) {
  495.                                                 css_computed_style_destroy(
  496.                                                                 style);
  497.                                                 return false;
  498.                                         }
  499.                                         cell->type = BOX_TABLE_CELL;
  500.  
  501.                                         cell->rows = 1;
  502.                                         cell->columns = col - start;
  503.                                         cell->start_column = start;
  504.  
  505.                                         /* Find place to insert cell */
  506.                                         for (prev = table_row->children;
  507.                                                         prev != NULL;
  508.                                                         prev = prev->next) {
  509.                                                 if (prev->start_column +
  510.                                                         prev->columns ==
  511.                                                                 start)
  512.                                                         break;
  513.                                                 if (prev->next == NULL)
  514.                                                         break;
  515.                                         }
  516.  
  517.                                         /* Insert it */
  518.                                         if (prev == NULL) {
  519.                                                 if (table_row->children != NULL)
  520.                                                         table_row->children->
  521.                                                                 prev = cell;
  522.                                                 else
  523.                                                         table_row->last = cell;
  524.  
  525.                                                 cell->next =
  526.                                                         table_row->children;
  527.                                                 table_row->children = cell;
  528.                                         } else {
  529.                                                 if (prev->next != NULL)
  530.                                                         prev->next->prev = cell;
  531.                                                 else
  532.                                                         table_row->last = cell;
  533.  
  534.                                                 cell->next = prev->next;
  535.                                                 prev->next = cell;
  536.                                                 cell->prev = prev;
  537.                                         }
  538.                                         cell->parent = table_row;
  539.                                 } else {
  540.                                         spans[col].row_span--;
  541.                                 }
  542.                         }
  543.  
  544.                         assert(rows_left > 0);
  545.  
  546.                         rows_left--;
  547.                 }
  548.         }
  549.  
  550.         return true;
  551. }
  552.  
  553.  
  554. bool box_normalise_table_row_group(struct box *row_group,
  555.                 struct columns *col_info,
  556.                 html_content * c)
  557. {
  558.         struct box *child;
  559.         struct box *next_child;
  560.         struct box *row;
  561.         css_computed_style *style;
  562.         nscss_select_ctx ctx;
  563.  
  564.         assert(row_group != 0);
  565.         assert(row_group->type == BOX_TABLE_ROW_GROUP);
  566.  
  567. #ifdef BOX_NORMALISE_DEBUG
  568.         LOG(("row_group %p", row_group));
  569. #endif
  570.  
  571.         for (child = row_group->children; child != NULL; child = next_child) {
  572.                 next_child = child->next;
  573.  
  574.                 switch (child->type) {
  575.                 case BOX_TABLE_ROW:
  576.                         /* ok */
  577.                         if (box_normalise_table_row(child, col_info,
  578.                                         c) == false)
  579.                                 return false;
  580.                         break;
  581.                 case BOX_BLOCK:
  582.                 case BOX_INLINE_CONTAINER:
  583.                 case BOX_TABLE:
  584.                 case BOX_TABLE_ROW_GROUP:
  585.                 case BOX_TABLE_CELL:
  586.                         /* insert implied table row */
  587.                         assert(row_group->style != NULL);
  588.  
  589.                         ctx.ctx = c->select_ctx;
  590.                         ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
  591.                         ctx.base_url = c->base_url;
  592.                         ctx.universal = c->universal;
  593.  
  594.                         style = nscss_get_blank_style(&ctx, row_group->style,
  595.                                         box_style_alloc, NULL);
  596.                         if (style == NULL)
  597.                                 return false;
  598.  
  599.                         row = box_create(NULL, style, true, row_group->href,
  600.                                         row_group->target, NULL, NULL, c->bctx);
  601.                         if (row == NULL) {
  602.                                 css_computed_style_destroy(style);
  603.                                 return false;
  604.                         }
  605.                         row->type = BOX_TABLE_ROW;
  606.  
  607.                         if (child->prev == NULL)
  608.                                 row_group->children = row;
  609.                         else
  610.                                 child->prev->next = row;
  611.  
  612.                         row->prev = child->prev;
  613.  
  614.                         while (child != NULL && (
  615.                                         child->type == BOX_BLOCK ||
  616.                                         child->type == BOX_INLINE_CONTAINER ||
  617.                                         child->type == BOX_TABLE ||
  618.                                         child->type == BOX_TABLE_ROW_GROUP ||
  619.                                         child->type == BOX_TABLE_CELL)) {
  620.                                 box_add_child(row, child);
  621.  
  622.                                 next_child = child->next;
  623.                                 child->next = NULL;
  624.                                 child = next_child;
  625.                         }
  626.  
  627.                         assert(row->last != NULL);
  628.  
  629.                         row->last->next = NULL;
  630.                         row->next = next_child = child;
  631.                         if (row->next != NULL)
  632.                                 row->next->prev = row;
  633.                         else
  634.                                 row_group->last = row;
  635.                         row->parent = row_group;
  636.  
  637.                         if (box_normalise_table_row(row, col_info,
  638.                                         c) == false)
  639.                                 return false;
  640.                         break;
  641.                 case BOX_INLINE:
  642.                 case BOX_INLINE_END:
  643.                 case BOX_INLINE_BLOCK:
  644.                 case BOX_FLOAT_LEFT:
  645.                 case BOX_FLOAT_RIGHT:
  646.                 case BOX_BR:
  647.                 case BOX_TEXT:
  648.                         /* should have been wrapped in inline
  649.                            container by convert_xml_to_box() */
  650.                         assert(0);
  651.                         break;
  652.                 default:
  653.                         assert(0);
  654.                 }
  655.         }
  656.  
  657.         if (row_group->children == NULL) {
  658. #ifdef BOX_NORMALISE_DEBUG
  659.                 LOG(("row_group->children == 0, inserting implied row"));
  660. #endif
  661.  
  662.                 assert(row_group->style != NULL);
  663.  
  664.                 ctx.ctx = c->select_ctx;
  665.                 ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
  666.                 ctx.base_url = c->base_url;
  667.                 ctx.universal = c->universal;
  668.  
  669.                 style = nscss_get_blank_style(&ctx, row_group->style,
  670.                                 box_style_alloc, NULL);
  671.                 if (style == NULL) {
  672.                         return false;
  673.                 }
  674.  
  675.                 row = box_create(NULL, style, true, row_group->href,
  676.                                 row_group->target, NULL, NULL, c->bctx);
  677.                 if (row == NULL) {
  678.                         css_computed_style_destroy(style);
  679.                         return false;
  680.                 }
  681.                 row->type = BOX_TABLE_ROW;
  682.  
  683.                 row->parent = row_group;
  684.                 row_group->children = row_group->last = row;
  685.  
  686.                 /* Keep table's row count in sync */
  687.                 col_info->num_rows++;
  688.         }
  689.  
  690. #ifdef BOX_NORMALISE_DEBUG
  691.         LOG(("row_group %p done", row_group));
  692. #endif
  693.  
  694.         return true;
  695. }
  696.  
  697.  
  698. bool box_normalise_table_row(struct box *row,
  699.                 struct columns *col_info,
  700.                 html_content * c)
  701. {
  702.         struct box *child;
  703.         struct box *next_child;
  704.         struct box *cell = NULL;
  705.         css_computed_style *style;
  706.         unsigned int i;
  707.         nscss_select_ctx ctx;
  708.  
  709.         assert(row != NULL);
  710.         assert(row->type == BOX_TABLE_ROW);
  711.  
  712. #ifdef BOX_NORMALISE_DEBUG
  713.         LOG(("row %p", row));
  714. #endif
  715.  
  716.         for (child = row->children; child != NULL; child = next_child) {
  717.                 next_child = child->next;
  718.  
  719.                 switch (child->type) {
  720.                 case BOX_TABLE_CELL:
  721.                         /* ok */
  722.                         if (box_normalise_block(child, c) == false)
  723.                                 return false;
  724.                         cell = child;
  725.                         break;
  726.                 case BOX_BLOCK:
  727.                 case BOX_INLINE_CONTAINER:
  728.                 case BOX_TABLE:
  729.                 case BOX_TABLE_ROW_GROUP:
  730.                 case BOX_TABLE_ROW:
  731.                         /* insert implied table cell */
  732.                         assert(row->style != NULL);
  733.  
  734.                         ctx.ctx = c->select_ctx;
  735.                         ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
  736.                         ctx.base_url = c->base_url;
  737.                         ctx.universal = c->universal;
  738.  
  739.                         style = nscss_get_blank_style(&ctx, row->style,
  740.                                         box_style_alloc, NULL);
  741.                         if (style == NULL)
  742.                                 return false;
  743.  
  744.                         cell = box_create(NULL, style, true, row->href,
  745.                                         row->target, NULL, NULL, c->bctx);
  746.                         if (cell == NULL) {
  747.                                 css_computed_style_destroy(style);
  748.                                 return false;
  749.                         }
  750.                         cell->type = BOX_TABLE_CELL;
  751.  
  752.                         if (child->prev == NULL)
  753.                                 row->children = cell;
  754.                         else
  755.                                 child->prev->next = cell;
  756.  
  757.                         cell->prev = child->prev;
  758.  
  759.                         while (child != NULL && (
  760.                                         child->type == BOX_BLOCK ||
  761.                                         child->type == BOX_INLINE_CONTAINER ||
  762.                                         child->type == BOX_TABLE ||
  763.                                         child->type == BOX_TABLE_ROW_GROUP ||
  764.                                         child->type == BOX_TABLE_ROW)) {
  765.                                 box_add_child(cell, child);
  766.  
  767.                                 next_child = child->next;
  768.                                 child->next = NULL;
  769.                                 child = next_child;
  770.                         }
  771.  
  772.                         assert(cell->last != NULL);
  773.  
  774.                         cell->last->next = NULL;
  775.                         cell->next = next_child = child;
  776.                         if (cell->next != NULL)
  777.                                 cell->next->prev = cell;
  778.                         else
  779.                                 row->last = cell;
  780.                         cell->parent = row;
  781.  
  782.                         if (box_normalise_block(cell, c) == false)
  783.                                 return false;
  784.                         break;
  785.                 case BOX_INLINE:
  786.                 case BOX_INLINE_END:
  787.                 case BOX_INLINE_BLOCK:
  788.                 case BOX_FLOAT_LEFT:
  789.                 case BOX_FLOAT_RIGHT:
  790.                 case BOX_BR:
  791.                 case BOX_TEXT:
  792.                         /* should have been wrapped in inline
  793.                            container by convert_xml_to_box() */
  794.                         assert(0);
  795.                         break;
  796.                 default:
  797.                         assert(0);
  798.                 }
  799.  
  800.                 if (calculate_table_row(col_info, cell->columns, cell->rows,
  801.                                 &cell->start_column) == false)
  802.                         return false;
  803.         }
  804.  
  805.  
  806.         /* Update row spanning details for all columns */
  807.         for (i = 0; i < col_info->num_columns; i++) {
  808.                 if (col_info->spans[i].row_span != 0 &&
  809.                                 col_info->spans[i].auto_row == false) {
  810.                         /* This cell spans rows, and is not an auto row.
  811.                          * Reduce number of rows left to span */
  812.                         col_info->spans[i].row_span--;
  813.                 }
  814.         }
  815.  
  816.         /* Reset current column for next row */
  817.         col_info->current_column = 0;
  818.  
  819.         /* Increment row counter */
  820.         col_info->num_rows++;
  821.  
  822. #ifdef BOX_NORMALISE_DEBUG
  823.         LOG(("row %p done", row));
  824. #endif
  825.  
  826.         return true;
  827. }
  828.  
  829.  
  830. /**
  831.  * Compute the column index at which the current cell begins.
  832.  * Additionally, update the column record to reflect row spanning.
  833.  *
  834.  * \param col_info      Column record
  835.  * \param col_span      Number of columns that current cell spans
  836.  * \param row_span      Number of rows that current cell spans
  837.  * \param start_column  Pointer to location to receive column index
  838.  * \return  true on success, false on memory exhaustion
  839.  */
  840.  
  841. bool calculate_table_row(struct columns *col_info,
  842.                 unsigned int col_span, unsigned int row_span,
  843.                 unsigned int *start_column)
  844. {
  845.         unsigned int cell_start_col = col_info->current_column;
  846.         unsigned int cell_end_col;
  847.         unsigned int i;
  848.         struct span_info *spans;
  849.  
  850.         /* Skip columns with cells spanning from above */
  851.         while (col_info->spans[cell_start_col].row_span != 0)
  852.                 cell_start_col++;
  853.  
  854.         /* Update current column with calculated start */
  855.         col_info->current_column = cell_start_col;
  856.  
  857.         /* If this cell has a colspan of 0, then assume 1.
  858.          * No other browser supports colspan=0, anyway. */
  859.         if (col_span == 0)
  860.                 col_span = 1;
  861.  
  862.         cell_end_col = cell_start_col + col_span;
  863.  
  864.         if (col_info->num_columns < cell_end_col) {
  865.                 /* It appears that this row has more columns than
  866.                  * the maximum recorded for the table so far.
  867.                  * Allocate more span records. */
  868.                 spans = realloc(col_info->spans,
  869.                                 sizeof *spans * (cell_end_col + 1));
  870.                 if (spans == NULL)
  871.                         return false;
  872.  
  873.                 col_info->spans = spans;
  874.                 col_info->num_columns = cell_end_col;
  875.  
  876.                 /* Mark new final column as sentinel */
  877.                 col_info->spans[cell_end_col].row_span = 0;
  878.                 col_info->spans[cell_end_col].auto_row = false;
  879.         }
  880.  
  881.         /* This cell may span multiple columns. If it also wants to span
  882.          * multiple rows, temporarily assume it spans 1 row only. This will
  883.          * be fixed up in box_normalise_table_spans() */
  884.         for (i = cell_start_col; i < cell_end_col; i++) {
  885.                 col_info->spans[i].row_span = (row_span == 0) ? 1 : row_span;
  886.                 col_info->spans[i].auto_row = (row_span == 0);
  887.         }
  888.  
  889.         /* Update current column with calculated end. */
  890.         col_info->current_column = cell_end_col;
  891.  
  892.         *start_column = cell_start_col;
  893.  
  894.         return true;
  895. }
  896.  
  897.  
  898. bool box_normalise_inline_container(struct box *cont, html_content * c)
  899. {
  900.         struct box *child;
  901.         struct box *next_child;
  902.  
  903.         assert(cont != NULL);
  904.         assert(cont->type == BOX_INLINE_CONTAINER);
  905.  
  906. #ifdef BOX_NORMALISE_DEBUG
  907.         LOG(("cont %p", cont));
  908. #endif
  909.  
  910.         for (child = cont->children; child != NULL; child = next_child) {
  911.                 next_child = child->next;
  912.                 switch (child->type) {
  913.                 case BOX_INLINE:
  914.                 case BOX_INLINE_END:
  915.                 case BOX_BR:
  916.                 case BOX_TEXT:
  917.                         /* ok */
  918.                         break;
  919.                 case BOX_INLINE_BLOCK:
  920.                         /* ok */
  921.                         if (box_normalise_block(child, c) == false)
  922.                                 return false;
  923.                         break;
  924.                 case BOX_FLOAT_LEFT:
  925.                 case BOX_FLOAT_RIGHT:
  926.                         /* ok */
  927.                         assert(child->children != NULL);
  928.  
  929.                         switch (child->children->type) {
  930.                         case BOX_BLOCK:
  931.                                 if (box_normalise_block(child->children,
  932.                                                 c) == false)
  933.                                         return false;
  934.                                 break;
  935.                         case BOX_TABLE:
  936.                                 if (box_normalise_table(child->children,
  937.                                                 c) == false)
  938.                                         return false;
  939.                                 break;
  940.                         default:
  941.                                 assert(0);
  942.                         }
  943.  
  944.                         if (child->children == NULL) {
  945.                                 /* the child has destroyed itself: remove float */
  946.                                 if (child->prev == NULL)
  947.                                         child->parent->children = child->next;
  948.                                 else
  949.                                         child->prev->next = child->next;
  950.                                 if (child->next != NULL)
  951.                                         child->next->prev = child->prev;
  952.                                 else
  953.                                         child->parent->last = child->prev;
  954.  
  955.                                 box_free(child);
  956.                         }
  957.                         break;
  958.                 case BOX_BLOCK:
  959.                 case BOX_INLINE_CONTAINER:
  960.                 case BOX_TABLE:
  961.                 case BOX_TABLE_ROW_GROUP:
  962.                 case BOX_TABLE_ROW:
  963.                 case BOX_TABLE_CELL:
  964.                 default:
  965.                         assert(0);
  966.                 }
  967.         }
  968.  
  969. #ifdef BOX_NORMALISE_DEBUG
  970.         LOG(("cont %p done", cont));
  971. #endif
  972.  
  973.         return true;
  974. }
  975.