FixedTableLayout.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /*
  2. * Copyright (C) 2002 Lars Knoll (knoll@kde.org)
  3. * (C) 2002 Dirk Mueller (mueller@kde.org)
  4. * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc.
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Library General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2 of the License.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Library General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Library General Public License
  17. * along with this library; see the file COPYING.LIB. If not, write to
  18. * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  19. * Boston, MA 02110-1301, USA.
  20. */
  21. #include "config.h"
  22. #include "FixedTableLayout.h"
  23. #include "RenderTable.h"
  24. #include "RenderTableCell.h"
  25. #include "RenderTableCol.h"
  26. #include "RenderTableSection.h"
  27. /*
  28. The text below is from the CSS 2.1 specs.
  29. Fixed table layout
  30. With this (fast) algorithm, the horizontal layout of the table does
  31. not depend on the contents of the cells; it only depends on the
  32. table's width, the width of the columns, and borders or cell
  33. spacing.
  34. The table's width may be specified explicitly with the 'width'
  35. property. A value of 'auto' (for both 'display: table' and 'display:
  36. inline-table') means use the automatic table layout algorithm.
  37. In the fixed table layout algorithm, the width of each column is
  38. determined as follows:
  39. 1. A column element with a value other than 'auto' for the 'width'
  40. property sets the width for that column.
  41. 2. Otherwise, a cell in the first row with a value other than
  42. 'auto' for the 'width' property sets the width for that column. If
  43. the cell spans more than one column, the width is divided over the
  44. columns.
  45. 3. Any remaining columns equally divide the remaining horizontal
  46. table space (minus borders or cell spacing).
  47. The width of the table is then the greater of the value of the
  48. 'width' property for the table element and the sum of the column
  49. widths (plus cell spacing or borders). If the table is wider than
  50. the columns, the extra space should be distributed over the columns.
  51. In this manner, the user agent can begin to lay out the table once
  52. the entire first row has been received. Cells in subsequent rows do
  53. not affect column widths. Any cell that has content that overflows
  54. uses the 'overflow' property to determine whether to clip the
  55. overflow content.
  56. */
  57. using namespace std;
  58. namespace WebCore {
  59. FixedTableLayout::FixedTableLayout(RenderTable* table)
  60. : TableLayout(table)
  61. {
  62. }
  63. int FixedTableLayout::calcWidthArray()
  64. {
  65. // FIXME: We might want to wait until we have all of the first row before computing for the first time.
  66. int usedWidth = 0;
  67. // iterate over all <col> elements
  68. unsigned nEffCols = m_table->numEffCols();
  69. m_width.resize(nEffCols);
  70. m_width.fill(Length(Auto));
  71. unsigned currentEffectiveColumn = 0;
  72. for (RenderTableCol* col = m_table->firstColumn(); col; col = col->nextColumn()) {
  73. // RenderTableCols don't have the concept of preferred logical width, but we need to clear their dirty bits
  74. // so that if we call setPreferredWidthsDirty(true) on a col or one of its descendants, we'll mark it's
  75. // ancestors as dirty.
  76. col->clearPreferredLogicalWidthsDirtyBits();
  77. // Width specified by column-groups that have column child does not affect column width in fixed layout tables
  78. if (col->isTableColumnGroupWithColumnChildren())
  79. continue;
  80. Length colStyleLogicalWidth = col->style()->logicalWidth();
  81. int effectiveColWidth = 0;
  82. if (colStyleLogicalWidth.isFixed() && colStyleLogicalWidth.value() > 0)
  83. effectiveColWidth = colStyleLogicalWidth.value();
  84. unsigned span = col->span();
  85. while (span) {
  86. unsigned spanInCurrentEffectiveColumn;
  87. if (currentEffectiveColumn >= nEffCols) {
  88. m_table->appendColumn(span);
  89. nEffCols++;
  90. m_width.append(Length());
  91. spanInCurrentEffectiveColumn = span;
  92. } else {
  93. if (span < m_table->spanOfEffCol(currentEffectiveColumn)) {
  94. m_table->splitColumn(currentEffectiveColumn, span);
  95. nEffCols++;
  96. m_width.append(Length());
  97. }
  98. spanInCurrentEffectiveColumn = m_table->spanOfEffCol(currentEffectiveColumn);
  99. }
  100. if ((colStyleLogicalWidth.isFixed() || colStyleLogicalWidth.isPercent()) && colStyleLogicalWidth.isPositive()) {
  101. m_width[currentEffectiveColumn] = colStyleLogicalWidth;
  102. m_width[currentEffectiveColumn] *= spanInCurrentEffectiveColumn;
  103. usedWidth += effectiveColWidth * spanInCurrentEffectiveColumn;
  104. }
  105. span -= spanInCurrentEffectiveColumn;
  106. currentEffectiveColumn++;
  107. }
  108. }
  109. // Iterate over the first row in case some are unspecified.
  110. RenderTableSection* section = m_table->topNonEmptySection();
  111. if (!section)
  112. return usedWidth;
  113. unsigned currentColumn = 0;
  114. RenderObject* firstRow = section->firstChild();
  115. for (RenderObject* child = firstRow->firstChild(); child; child = child->nextSibling()) {
  116. if (!child->isTableCell())
  117. continue;
  118. RenderTableCell* cell = toRenderTableCell(child);
  119. Length logicalWidth = cell->styleOrColLogicalWidth();
  120. unsigned span = cell->colSpan();
  121. int fixedBorderBoxLogicalWidth = 0;
  122. // FIXME: Support other length types. If the width is non-auto, it should probably just use
  123. // RenderBox::computeLogicalWidthInRegionUsing to compute the width.
  124. if (logicalWidth.isFixed() && logicalWidth.isPositive()) {
  125. fixedBorderBoxLogicalWidth = cell->adjustBorderBoxLogicalWidthForBoxSizing(logicalWidth.value());
  126. logicalWidth.setValue(fixedBorderBoxLogicalWidth);
  127. }
  128. unsigned usedSpan = 0;
  129. while (usedSpan < span && currentColumn < nEffCols) {
  130. float eSpan = m_table->spanOfEffCol(currentColumn);
  131. // Only set if no col element has already set it.
  132. if (m_width[currentColumn].isAuto() && logicalWidth.type() != Auto) {
  133. m_width[currentColumn] = logicalWidth;
  134. m_width[currentColumn] *= eSpan / span;
  135. usedWidth += fixedBorderBoxLogicalWidth * eSpan / span;
  136. }
  137. usedSpan += eSpan;
  138. ++currentColumn;
  139. }
  140. // FixedTableLayout doesn't use min/maxPreferredLogicalWidths, but we need to clear the
  141. // dirty bit on the cell so that we'll correctly mark its ancestors dirty
  142. // in case we later call setPreferredLogicalWidthsDirty(true) on it later.
  143. if (cell->preferredLogicalWidthsDirty())
  144. cell->setPreferredLogicalWidthsDirty(false);
  145. }
  146. return usedWidth;
  147. }
  148. void FixedTableLayout::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth)
  149. {
  150. minWidth = maxWidth = calcWidthArray();
  151. }
  152. void FixedTableLayout::applyPreferredLogicalWidthQuirks(LayoutUnit& minWidth, LayoutUnit& maxWidth) const
  153. {
  154. Length tableLogicalWidth = m_table->style()->logicalWidth();
  155. if (tableLogicalWidth.isFixed() && tableLogicalWidth.isPositive())
  156. minWidth = maxWidth = max<int>(minWidth, tableLogicalWidth.value() - m_table->bordersPaddingAndSpacingInRowDirection());
  157. /*
  158. <table style="width:100%; background-color:red"><tr><td>
  159. <table style="background-color:blue"><tr><td>
  160. <table style="width:100%; background-color:green; table-layout:fixed"><tr><td>
  161. Content
  162. </td></tr></table>
  163. </td></tr></table>
  164. </td></tr></table>
  165. */
  166. // In this example, the two inner tables should be as large as the outer table.
  167. // We can achieve this effect by making the maxwidth of fixed tables with percentage
  168. // widths be infinite.
  169. if (m_table->style()->logicalWidth().isPercent() && maxWidth < tableMaxWidth)
  170. maxWidth = tableMaxWidth;
  171. }
  172. void FixedTableLayout::layout()
  173. {
  174. int tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection();
  175. unsigned nEffCols = m_table->numEffCols();
  176. // FIXME: It is possible to be called without having properly updated our internal representation.
  177. // This means that our preferred logical widths were not recomputed as expected.
  178. if (nEffCols != m_width.size()) {
  179. calcWidthArray();
  180. // FIXME: Table layout shouldn't modify our table structure (but does due to columns and column-groups).
  181. nEffCols = m_table->numEffCols();
  182. }
  183. Vector<int> calcWidth(nEffCols, 0);
  184. unsigned numAuto = 0;
  185. unsigned autoSpan = 0;
  186. int totalFixedWidth = 0;
  187. int totalPercentWidth = 0;
  188. float totalPercent = 0;
  189. // Compute requirements and try to satisfy fixed and percent widths.
  190. // Percentages are of the table's width, so for example
  191. // for a table width of 100px with columns (40px, 10%), the 10% compute
  192. // to 10px here, and will scale up to 20px in the final (80px, 20px).
  193. for (unsigned i = 0; i < nEffCols; i++) {
  194. if (m_width[i].isFixed()) {
  195. calcWidth[i] = m_width[i].value();
  196. totalFixedWidth += calcWidth[i];
  197. } else if (m_width[i].isPercent()) {
  198. calcWidth[i] = valueForLength(m_width[i], tableLogicalWidth);
  199. totalPercentWidth += calcWidth[i];
  200. totalPercent += m_width[i].percent();
  201. } else if (m_width[i].isAuto()) {
  202. numAuto++;
  203. autoSpan += m_table->spanOfEffCol(i);
  204. }
  205. }
  206. int hspacing = m_table->hBorderSpacing();
  207. int totalWidth = totalFixedWidth + totalPercentWidth;
  208. if (!numAuto || totalWidth > tableLogicalWidth) {
  209. // If there are no auto columns, or if the total is too wide, take
  210. // what we have and scale it to fit as necessary.
  211. if (totalWidth != tableLogicalWidth) {
  212. // Fixed widths only scale up
  213. if (totalFixedWidth && totalWidth < tableLogicalWidth) {
  214. totalFixedWidth = 0;
  215. for (unsigned i = 0; i < nEffCols; i++) {
  216. if (m_width[i].isFixed()) {
  217. calcWidth[i] = calcWidth[i] * tableLogicalWidth / totalWidth;
  218. totalFixedWidth += calcWidth[i];
  219. }
  220. }
  221. }
  222. if (totalPercent) {
  223. totalPercentWidth = 0;
  224. for (unsigned i = 0; i < nEffCols; i++) {
  225. if (m_width[i].isPercent()) {
  226. calcWidth[i] = m_width[i].percent() * (tableLogicalWidth - totalFixedWidth) / totalPercent;
  227. totalPercentWidth += calcWidth[i];
  228. }
  229. }
  230. }
  231. totalWidth = totalFixedWidth + totalPercentWidth;
  232. }
  233. } else {
  234. // Divide the remaining width among the auto columns.
  235. ASSERT(autoSpan >= numAuto);
  236. int remainingWidth = tableLogicalWidth - totalFixedWidth - totalPercentWidth - hspacing * (autoSpan - numAuto);
  237. int lastAuto = 0;
  238. for (unsigned i = 0; i < nEffCols; i++) {
  239. if (m_width[i].isAuto()) {
  240. unsigned span = m_table->spanOfEffCol(i);
  241. int w = remainingWidth * span / autoSpan;
  242. calcWidth[i] = w + hspacing * (span - 1);
  243. remainingWidth -= w;
  244. if (!remainingWidth)
  245. break;
  246. lastAuto = i;
  247. numAuto--;
  248. ASSERT(autoSpan >= span);
  249. autoSpan -= span;
  250. }
  251. }
  252. // Last one gets the remainder.
  253. if (remainingWidth)
  254. calcWidth[lastAuto] += remainingWidth;
  255. totalWidth = tableLogicalWidth;
  256. }
  257. if (totalWidth < tableLogicalWidth) {
  258. // Spread extra space over columns.
  259. int remainingWidth = tableLogicalWidth - totalWidth;
  260. int total = nEffCols;
  261. while (total) {
  262. int w = remainingWidth / total;
  263. remainingWidth -= w;
  264. calcWidth[--total] += w;
  265. }
  266. if (nEffCols > 0)
  267. calcWidth[nEffCols - 1] += remainingWidth;
  268. }
  269. int pos = 0;
  270. for (unsigned i = 0; i < nEffCols; i++) {
  271. m_table->setColumnPosition(i, pos);
  272. pos += calcWidth[i] + hspacing;
  273. }
  274. int colPositionsSize = m_table->columnPositions().size();
  275. if (colPositionsSize > 0)
  276. m_table->setColumnPosition(colPositionsSize - 1, pos);
  277. }
  278. } // namespace WebCore