Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 2002 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 2002 Dirk Mueller (mueller (at) kde.org)
      4  * Copyright (C) 2003, 2006 Apple Computer, 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 
     22 #include "config.h"
     23 #include "FixedTableLayout.h"
     24 
     25 #include "RenderTable.h"
     26 #include "RenderTableCell.h"
     27 #include "RenderTableCol.h"
     28 #include "RenderTableSection.h"
     29 
     30 /*
     31   The text below is from the CSS 2.1 specs.
     32 
     33   Fixed table layout
     34 
     35   With this (fast) algorithm, the horizontal layout of the table does
     36   not depend on the contents of the cells; it only depends on the
     37   table's width, the width of the columns, and borders or cell
     38   spacing.
     39 
     40   The table's width may be specified explicitly with the 'width'
     41   property. A value of 'auto' (for both 'display: table' and 'display:
     42   inline-table') means use the automatic table layout algorithm.
     43 
     44   In the fixed table layout algorithm, the width of each column is
     45   determined as follows:
     46 
     47     1. A column element with a value other than 'auto' for the 'width'
     48     property sets the width for that column.
     49 
     50     2. Otherwise, a cell in the first row with a value other than
     51     'auto' for the 'width' property sets the width for that column. If
     52     the cell spans more than one column, the width is divided over the
     53     columns.
     54 
     55     3. Any remaining columns equally divide the remaining horizontal
     56     table space (minus borders or cell spacing).
     57 
     58   The width of the table is then the greater of the value of the
     59   'width' property for the table element and the sum of the column
     60   widths (plus cell spacing or borders). If the table is wider than
     61   the columns, the extra space should be distributed over the columns.
     62 
     63 
     64   In this manner, the user agent can begin to lay out the table once
     65   the entire first row has been received. Cells in subsequent rows do
     66   not affect column widths. Any cell that has content that overflows
     67   uses the 'overflow' property to determine whether to clip the
     68   overflow content.
     69 */
     70 
     71 using namespace std;
     72 
     73 namespace WebCore {
     74 
     75 FixedTableLayout::FixedTableLayout(RenderTable* table)
     76     : TableLayout(table)
     77 {
     78 }
     79 
     80 int FixedTableLayout::calcWidthArray(int)
     81 {
     82     int usedWidth = 0;
     83 
     84     // iterate over all <col> elements
     85     RenderObject* child = m_table->firstChild();
     86     int nEffCols = m_table->numEffCols();
     87     m_width.resize(nEffCols);
     88     m_width.fill(Length(Auto));
     89 
     90     int currentEffectiveColumn = 0;
     91     Length grpWidth;
     92     while (child) {
     93         if (child->isTableCol()) {
     94             RenderTableCol* col = toRenderTableCol(child);
     95             if (col->firstChild())
     96                 grpWidth = col->style()->width();
     97             else {
     98                 Length w = col->style()->width();
     99                 if (w.isAuto())
    100                     w = grpWidth;
    101                 int effWidth = 0;
    102                 if (w.isFixed() && w.value() > 0)
    103                     effWidth = w.value();
    104 
    105                 int span = col->span();
    106                 while (span) {
    107                     int spanInCurrentEffectiveColumn;
    108                     if (currentEffectiveColumn >= nEffCols) {
    109                         m_table->appendColumn(span);
    110                         nEffCols++;
    111                         m_width.append(Length());
    112                         spanInCurrentEffectiveColumn = span;
    113                     } else {
    114                         if (span < m_table->spanOfEffCol(currentEffectiveColumn)) {
    115                             m_table->splitColumn(currentEffectiveColumn, span);
    116                             nEffCols++;
    117                             m_width.append(Length());
    118                         }
    119                         spanInCurrentEffectiveColumn = m_table->spanOfEffCol(currentEffectiveColumn);
    120                     }
    121                     if ((w.isFixed() || w.isPercent()) && w.isPositive()) {
    122                         m_width[currentEffectiveColumn].setRawValue(w.type(), w.rawValue() * spanInCurrentEffectiveColumn);
    123                         usedWidth += effWidth * spanInCurrentEffectiveColumn;
    124                     }
    125                     span -= spanInCurrentEffectiveColumn;
    126                     currentEffectiveColumn++;
    127                 }
    128             }
    129             toRenderTableCol(child)->calcPrefWidths();
    130         } else
    131             break;
    132 
    133         RenderObject* next = child->firstChild();
    134         if (!next)
    135             next = child->nextSibling();
    136         if (!next && child->parent()->isTableCol()) {
    137             next = child->parent()->nextSibling();
    138             grpWidth = Length();
    139         }
    140         child = next;
    141     }
    142 
    143     // Iterate over the first row in case some are unspecified.
    144     RenderTableSection* section = m_table->header();
    145     if (!section)
    146         section = m_table->firstBody();
    147     if (!section)
    148         section = m_table->footer();
    149     if (section && !section->numRows())
    150         section = m_table->sectionBelow(section, true);
    151     if (section) {
    152         int cCol = 0;
    153         RenderObject* firstRow = section->firstChild();
    154         child = firstRow->firstChild();
    155         while (child) {
    156             if (child->isTableCell()) {
    157                 RenderTableCell* cell = toRenderTableCell(child);
    158                 if (cell->prefWidthsDirty())
    159                     cell->calcPrefWidths();
    160 
    161                 Length w = cell->styleOrColWidth();
    162                 int span = cell->colSpan();
    163                 int effWidth = 0;
    164                 if (w.isFixed() && w.isPositive())
    165                     effWidth = w.value();
    166 
    167                 int usedSpan = 0;
    168                 int i = 0;
    169                 while (usedSpan < span) {
    170                     ASSERT(cCol + i < nEffCols);
    171                     int eSpan = m_table->spanOfEffCol(cCol + i);
    172                     // Only set if no col element has already set it.
    173                     if (m_width[cCol + i].isAuto() && w.type() != Auto) {
    174                         m_width[cCol + i].setRawValue(w.type(), w.rawValue() * eSpan / span);
    175                         usedWidth += effWidth * eSpan / span;
    176                     }
    177                     usedSpan += eSpan;
    178                     i++;
    179                 }
    180                 cCol += i;
    181             }
    182             child = child->nextSibling();
    183         }
    184     }
    185 
    186     return usedWidth;
    187 }
    188 
    189 // Use a very large value (in effect infinite). But not too large!
    190 // numeric_limits<int>::max() will too easily overflow widths.
    191 // Keep this in synch with BLOCK_MAX_WIDTH in RenderBlock.cpp
    192 #define TABLE_MAX_WIDTH 15000
    193 
    194 void FixedTableLayout::calcPrefWidths(int& minWidth, int& maxWidth)
    195 {
    196     // FIXME: This entire calculation is incorrect for both minwidth and maxwidth.
    197 
    198     // we might want to wait until we have all of the first row before
    199     // layouting for the first time.
    200 
    201     // only need to calculate the minimum width as the sum of the
    202     // cols/cells with a fixed width.
    203     //
    204     // The maximum width is max(minWidth, tableWidth).
    205     int bs = m_table->bordersPaddingAndSpacing();
    206 
    207     int tableWidth = m_table->style()->width().isFixed() ? m_table->style()->width().value() - bs : 0;
    208     int mw = calcWidthArray(tableWidth) + bs;
    209 
    210     minWidth = max(mw, tableWidth);
    211     maxWidth = minWidth;
    212 
    213     // This quirk is very similar to one that exists in RenderBlock::calcBlockPrefWidths().
    214     // Here's the example for this one:
    215     /*
    216         <table style="width:100%; background-color:red"><tr><td>
    217             <table style="background-color:blue"><tr><td>
    218                 <table style="width:100%; background-color:green; table-layout:fixed"><tr><td>
    219                     Content
    220                 </td></tr></table>
    221             </td></tr></table>
    222         </td></tr></table>
    223     */
    224     // In this example, the two inner tables should be as large as the outer table.
    225     // We can achieve this effect by making the maxwidth of fixed tables with percentage
    226     // widths be infinite.
    227     if (m_table->style()->htmlHacks() && m_table->style()->width().isPercent()
    228         && maxWidth < TABLE_MAX_WIDTH)
    229         maxWidth = TABLE_MAX_WIDTH;
    230 }
    231 
    232 void FixedTableLayout::layout()
    233 {
    234     int tableWidth = m_table->width() - m_table->bordersPaddingAndSpacing();
    235     int nEffCols = m_table->numEffCols();
    236     Vector<int> calcWidth(nEffCols, 0);
    237 
    238     int numAuto = 0;
    239     int autoSpan = 0;
    240     int totalFixedWidth = 0;
    241     int totalPercentWidth = 0;
    242     int totalRawPercent = 0;
    243 
    244     // Compute requirements and try to satisfy fixed and percent widths.
    245     // Percentages are of the table's width, so for example
    246     // for a table width of 100px with columns (40px, 10%), the 10% compute
    247     // to 10px here, and will scale up to 20px in the final (80px, 20px).
    248     for (int i = 0; i < nEffCols; i++) {
    249         if (m_width[i].isFixed()) {
    250             calcWidth[i] = m_width[i].value();
    251             totalFixedWidth += calcWidth[i];
    252         } else if (m_width[i].isPercent()) {
    253             calcWidth[i] = m_width[i].calcValue(tableWidth);
    254             totalPercentWidth += calcWidth[i];
    255             totalRawPercent += m_width[i].rawValue();
    256         } else if (m_width[i].isAuto()) {
    257             numAuto++;
    258             autoSpan += m_table->spanOfEffCol(i);
    259         }
    260     }
    261 
    262     int hspacing = m_table->hBorderSpacing();
    263     int totalWidth = totalFixedWidth + totalPercentWidth;
    264     if (!numAuto || totalWidth > tableWidth) {
    265         // If there are no auto columns, or if the total is too wide, take
    266         // what we have and scale it to fit as necessary.
    267         if (totalWidth != tableWidth) {
    268             // Fixed widths only scale up
    269             if (totalFixedWidth && totalWidth < tableWidth) {
    270                 totalFixedWidth = 0;
    271                 for (int i = 0; i < nEffCols; i++) {
    272                     if (m_width[i].isFixed()) {
    273                         calcWidth[i] = calcWidth[i] * tableWidth / totalWidth;
    274                         totalFixedWidth += calcWidth[i];
    275                     }
    276                 }
    277             }
    278             if (totalRawPercent) {
    279                 totalPercentWidth = 0;
    280                 for (int i = 0; i < nEffCols; i++) {
    281                     if (m_width[i].isPercent()) {
    282                         calcWidth[i] = m_width[i].rawValue() * (tableWidth - totalFixedWidth) / totalRawPercent;
    283                         totalPercentWidth += calcWidth[i];
    284                     }
    285                 }
    286             }
    287             totalWidth = totalFixedWidth + totalPercentWidth;
    288         }
    289     } else {
    290         // Divide the remaining width among the auto columns.
    291         int remainingWidth = tableWidth - totalFixedWidth - totalPercentWidth - hspacing * (autoSpan - numAuto);
    292         int lastAuto = 0;
    293         for (int i = 0; i < nEffCols; i++) {
    294             if (m_width[i].isAuto()) {
    295                 int span = m_table->spanOfEffCol(i);
    296                 int w = remainingWidth * span / autoSpan;
    297                 calcWidth[i] = w + hspacing * (span - 1);
    298                 remainingWidth -= w;
    299                 if (!remainingWidth)
    300                     break;
    301                 lastAuto = i;
    302                 numAuto--;
    303                 autoSpan -= span;
    304             }
    305         }
    306         // Last one gets the remainder.
    307         if (remainingWidth)
    308             calcWidth[lastAuto] += remainingWidth;
    309         totalWidth = tableWidth;
    310     }
    311 
    312     if (totalWidth < tableWidth) {
    313         // Spread extra space over columns.
    314         int remainingWidth = tableWidth - totalWidth;
    315         int total = nEffCols;
    316         while (total) {
    317             int w = remainingWidth / total;
    318             remainingWidth -= w;
    319             calcWidth[--total] += w;
    320         }
    321         if (nEffCols > 0)
    322             calcWidth[nEffCols - 1] += remainingWidth;
    323     }
    324 
    325     int pos = 0;
    326     for (int i = 0; i < nEffCols; i++) {
    327         m_table->columnPositions()[i] = pos;
    328         pos += calcWidth[i] + hspacing;
    329     }
    330     int colPositionsSize = m_table->columnPositions().size();
    331     if (colPositionsSize > 0)
    332         m_table->columnPositions()[colPositionsSize - 1] = pos;
    333 }
    334 
    335 } // namespace WebCore
    336