LCOV - code coverage report
Current view: directory - layout/tables - FixedTableLayoutStrategy.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 198 0 0.0 %
Date: 2012-06-02 Functions: 8 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : // vim:cindent:ts=4:et:sw=4:
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla's table layout code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is the Mozilla Foundation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2006
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   L. David Baron <dbaron@dbaron.org> (original author)
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : /*
      40                 :  * Algorithms that determine column and table widths used for CSS2's
      41                 :  * 'table-layout: fixed'.
      42                 :  */
      43                 : 
      44                 : #include "FixedTableLayoutStrategy.h"
      45                 : #include "nsTableFrame.h"
      46                 : #include "nsTableColFrame.h"
      47                 : #include "nsTableCellFrame.h"
      48                 : 
      49               0 : FixedTableLayoutStrategy::FixedTableLayoutStrategy(nsTableFrame *aTableFrame)
      50                 :   : nsITableLayoutStrategy(nsITableLayoutStrategy::Fixed)
      51               0 :   , mTableFrame(aTableFrame)
      52                 : {
      53               0 :     MarkIntrinsicWidthsDirty();
      54               0 : }
      55                 : 
      56                 : /* virtual */
      57               0 : FixedTableLayoutStrategy::~FixedTableLayoutStrategy()
      58                 : {
      59               0 : }
      60                 : 
      61                 : /* virtual */ nscoord
      62               0 : FixedTableLayoutStrategy::GetMinWidth(nsRenderingContext* aRenderingContext)
      63                 : {
      64               0 :     DISPLAY_MIN_WIDTH(mTableFrame, mMinWidth);
      65               0 :     if (mMinWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
      66               0 :         return mMinWidth;
      67                 : 
      68                 :     // It's theoretically possible to do something much better here that
      69                 :     // depends only on the columns and the first row (where we look at
      70                 :     // intrinsic widths inside the first row and then reverse the
      71                 :     // algorithm to find the narrowest width that would hold all of
      72                 :     // those intrinsic widths), but it wouldn't be compatible with other
      73                 :     // browsers, or with the use of GetMinWidth by
      74                 :     // nsTableFrame::ComputeSize to determine the width of a fixed
      75                 :     // layout table, since CSS2.1 says:
      76                 :     //   The width of the table is then the greater of the value of the
      77                 :     //   'width' property for the table element and the sum of the
      78                 :     //   column widths (plus cell spacing or borders).
      79                 : 
      80                 :     // XXX Should we really ignore 'min-width' and 'max-width'?
      81                 :     // XXX Should we really ignore widths on column groups?
      82                 : 
      83               0 :     nsTableCellMap *cellMap = mTableFrame->GetCellMap();
      84               0 :     PRInt32 colCount = cellMap->GetColCount();
      85               0 :     nscoord spacing = mTableFrame->GetCellSpacingX();
      86                 : 
      87               0 :     nscoord result = 0;
      88                 : 
      89               0 :     if (colCount > 0) {
      90               0 :         result += spacing * (colCount + 1);
      91                 :     }
      92                 : 
      93               0 :     for (PRInt32 col = 0; col < colCount; ++col) {
      94               0 :         nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
      95               0 :         if (!colFrame) {
      96               0 :             NS_ERROR("column frames out of sync with cell map");
      97               0 :             continue;
      98                 :         }
      99                 :         const nsStyleCoord *styleWidth =
     100               0 :             &colFrame->GetStylePosition()->mWidth;
     101               0 :         if (styleWidth->GetUnit() == eStyleUnit_Coord) {
     102                 :             result += nsLayoutUtils::ComputeWidthValue(aRenderingContext,
     103               0 :                         colFrame, 0, 0, 0, *styleWidth);
     104               0 :         } else if (styleWidth->GetUnit() == eStyleUnit_Percent) {
     105                 :             // do nothing
     106                 :         } else {
     107               0 :             NS_ASSERTION(styleWidth->GetUnit() == eStyleUnit_Auto ||
     108                 :                          styleWidth->GetUnit() == eStyleUnit_Enumerated ||
     109                 :                          styleWidth->IsCalcUnit(),
     110                 :                          "bad width");
     111                 : 
     112                 :             // The 'table-layout: fixed' algorithm considers only cells
     113                 :             // in the first row.
     114                 :             bool originates;
     115                 :             PRInt32 colSpan;
     116                 :             nsTableCellFrame *cellFrame =
     117               0 :                 cellMap->GetCellInfoAt(0, col, &originates, &colSpan);
     118               0 :             if (cellFrame) {
     119               0 :                 styleWidth = &cellFrame->GetStylePosition()->mWidth;
     120               0 :                 if (styleWidth->GetUnit() == eStyleUnit_Coord ||
     121               0 :                     (styleWidth->GetUnit() == eStyleUnit_Enumerated &&
     122               0 :                      (styleWidth->GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT ||
     123               0 :                       styleWidth->GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT))) {
     124                 :                     nscoord cellWidth = nsLayoutUtils::IntrinsicForContainer(
     125               0 :                         aRenderingContext, cellFrame, nsLayoutUtils::MIN_WIDTH);
     126               0 :                     if (colSpan > 1) {
     127                 :                         // If a column-spanning cell is in the first
     128                 :                         // row, split up the space evenly.  (XXX This
     129                 :                         // isn't quite right if some of the columns it's
     130                 :                         // in have specified widths.  Should we care?)
     131               0 :                         cellWidth = ((cellWidth + spacing) / colSpan) - spacing;
     132                 :                     }
     133               0 :                     result += cellWidth;
     134               0 :                 } else if (styleWidth->GetUnit() == eStyleUnit_Percent) {
     135               0 :                     if (colSpan > 1) {
     136                 :                         // XXX Can this force columns to negative
     137                 :                         // widths?
     138               0 :                         result -= spacing * (colSpan - 1);
     139                 :                     }
     140                 :                 }
     141                 :                 // else, for 'auto', '-moz-available', '-moz-fit-content',
     142                 :                 // and 'calc()', do nothing
     143                 :             }
     144                 :         }
     145                 :     }
     146                 : 
     147               0 :     return (mMinWidth = result);
     148                 : }
     149                 : 
     150                 : /* virtual */ nscoord
     151               0 : FixedTableLayoutStrategy::GetPrefWidth(nsRenderingContext* aRenderingContext,
     152                 :                                        bool aComputingSize)
     153                 : {
     154                 :     // It's theoretically possible to do something much better here that
     155                 :     // depends only on the columns and the first row (where we look at
     156                 :     // intrinsic widths inside the first row and then reverse the
     157                 :     // algorithm to find the narrowest width that would hold all of
     158                 :     // those intrinsic widths), but it wouldn't be compatible with other
     159                 :     // browsers.
     160               0 :     nscoord result = nscoord_MAX;
     161               0 :     DISPLAY_PREF_WIDTH(mTableFrame, result);
     162               0 :     return result;
     163                 : }
     164                 : 
     165                 : /* virtual */ void
     166               0 : FixedTableLayoutStrategy::MarkIntrinsicWidthsDirty()
     167                 : {
     168               0 :     mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
     169               0 :     mLastCalcWidth = nscoord_MIN;
     170               0 : }
     171                 : 
     172                 : static inline nscoord
     173               0 : AllocateUnassigned(nscoord aUnassignedSpace, float aShare)
     174                 : {
     175               0 :     if (aShare == 1.0f) {
     176                 :         // This happens when the numbers we're dividing to get aShare
     177                 :         // are equal.  We want to return unassignedSpace exactly, even
     178                 :         // if it can't be precisely round-tripped through float.
     179               0 :         return aUnassignedSpace;
     180                 :     }
     181               0 :     return NSToCoordRound(float(aUnassignedSpace) * aShare);
     182                 : }
     183                 : 
     184                 : /* virtual */ void
     185               0 : FixedTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowState)
     186                 : {
     187               0 :     nscoord tableWidth = aReflowState.ComputedWidth();
     188                 : 
     189               0 :     if (mLastCalcWidth == tableWidth)
     190               0 :         return;
     191               0 :     mLastCalcWidth = tableWidth;
     192                 : 
     193               0 :     nsTableCellMap *cellMap = mTableFrame->GetCellMap();
     194               0 :     PRInt32 colCount = cellMap->GetColCount();
     195               0 :     nscoord spacing = mTableFrame->GetCellSpacingX();
     196                 : 
     197               0 :     if (colCount == 0) {
     198                 :         // No Columns - nothing to compute
     199               0 :         return;
     200                 :     }
     201                 : 
     202                 :     // border-spacing isn't part of the basis for percentages.
     203               0 :     tableWidth -= spacing * (colCount + 1);
     204                 :     
     205                 :     // store the old column widths. We might call multiple times SetFinalWidth
     206                 :     // on the columns, due to this we can't compare at the last call that the
     207                 :     // width has changed with the respect to the last call to
     208                 :     // ComputeColumnWidths. In order to overcome this we store the old values
     209                 :     // in this array. A single call to SetFinalWidth would make it possible to
     210                 :     // call GetFinalWidth before and to compare when setting the final width.
     211               0 :     nsTArray<nscoord> oldColWidths;
     212                 : 
     213                 :     // XXX This ignores the 'min-width' and 'max-width' properties
     214                 :     // throughout.  Then again, that's what the CSS spec says to do.
     215                 : 
     216                 :     // XXX Should we really ignore widths on column groups?
     217                 : 
     218               0 :     PRUint32 unassignedCount = 0;
     219               0 :     nscoord unassignedSpace = tableWidth;
     220               0 :     const nscoord unassignedMarker = nscoord_MIN;
     221                 : 
     222                 :     // We use the PrefPercent on the columns to store the percentages
     223                 :     // used to compute column widths in case we need to shrink or expand
     224                 :     // the columns.
     225               0 :     float pctTotal = 0.0f;
     226                 : 
     227                 :     // Accumulate the total specified (non-percent) on the columns for
     228                 :     // distributing excess width to the columns.
     229               0 :     nscoord specTotal = 0;
     230                 : 
     231               0 :     for (PRInt32 col = 0; col < colCount; ++col) {
     232               0 :         nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
     233               0 :         if (!colFrame) {
     234               0 :             oldColWidths.AppendElement(0);
     235               0 :             NS_ERROR("column frames out of sync with cell map");
     236               0 :             continue;
     237                 :         }
     238               0 :         oldColWidths.AppendElement(colFrame->GetFinalWidth());
     239               0 :         colFrame->ResetPrefPercent();
     240                 :         const nsStyleCoord *styleWidth =
     241               0 :             &colFrame->GetStylePosition()->mWidth;
     242                 :         nscoord colWidth;
     243               0 :         if (styleWidth->GetUnit() == eStyleUnit_Coord) {
     244                 :             colWidth = nsLayoutUtils::ComputeWidthValue(
     245                 :                          aReflowState.rendContext,
     246               0 :                          colFrame, 0, 0, 0, *styleWidth);
     247               0 :             specTotal += colWidth;
     248               0 :         } else if (styleWidth->GetUnit() == eStyleUnit_Percent) {
     249               0 :             float pct = styleWidth->GetPercentValue();
     250               0 :             colWidth = NSToCoordFloor(pct * float(tableWidth));
     251               0 :             colFrame->AddPrefPercent(pct);
     252               0 :             pctTotal += pct;
     253                 :         } else {
     254               0 :             NS_ASSERTION(styleWidth->GetUnit() == eStyleUnit_Auto ||
     255                 :                          styleWidth->GetUnit() == eStyleUnit_Enumerated ||
     256                 :                          styleWidth->IsCalcUnit(),
     257                 :                          "bad width");
     258                 : 
     259                 :             // The 'table-layout: fixed' algorithm considers only cells
     260                 :             // in the first row.
     261                 :             bool originates;
     262                 :             PRInt32 colSpan;
     263                 :             nsTableCellFrame *cellFrame =
     264               0 :                 cellMap->GetCellInfoAt(0, col, &originates, &colSpan);
     265               0 :             if (cellFrame) {
     266               0 :                 styleWidth = &cellFrame->GetStylePosition()->mWidth;
     267               0 :                 if (styleWidth->GetUnit() == eStyleUnit_Coord ||
     268               0 :                     (styleWidth->GetUnit() == eStyleUnit_Enumerated &&
     269               0 :                      (styleWidth->GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT ||
     270               0 :                       styleWidth->GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT))) {
     271                 :                     // XXX This should use real percentage padding
     272                 :                     // Note that the difference between MIN_WIDTH and
     273                 :                     // PREF_WIDTH shouldn't matter for any of these
     274                 :                     // values of styleWidth; use MIN_WIDTH for symmetry
     275                 :                     // with GetMinWidth above, just in case there is a
     276                 :                     // difference.
     277                 :                     colWidth = nsLayoutUtils::IntrinsicForContainer(
     278                 :                                  aReflowState.rendContext,
     279               0 :                                  cellFrame, nsLayoutUtils::MIN_WIDTH);
     280               0 :                 } else if (styleWidth->GetUnit() == eStyleUnit_Percent) {
     281                 :                     // XXX This should use real percentage padding
     282                 :                     nsIFrame::IntrinsicWidthOffsetData offsets =
     283               0 :                         cellFrame->IntrinsicWidthOffsets(aReflowState.rendContext);
     284               0 :                     float pct = styleWidth->GetPercentValue();
     285               0 :                     colWidth = NSToCoordFloor(pct * float(tableWidth)) +
     286               0 :                                offsets.hPadding + offsets.hBorder;
     287               0 :                     pct /= float(colSpan);
     288               0 :                     colFrame->AddPrefPercent(pct);
     289               0 :                     pctTotal += pct;
     290                 :                 } else {
     291                 :                     // 'auto', '-moz-available', '-moz-fit-content', and
     292                 :                     // 'calc()'
     293               0 :                     colWidth = unassignedMarker;
     294                 :                 }
     295               0 :                 if (colWidth != unassignedMarker) {
     296               0 :                     if (colSpan > 1) {
     297                 :                         // If a column-spanning cell is in the first
     298                 :                         // row, split up the space evenly.  (XXX This
     299                 :                         // isn't quite right if some of the columns it's
     300                 :                         // in have specified widths.  Should we care?)
     301               0 :                         colWidth = ((colWidth + spacing) / colSpan) - spacing;
     302               0 :                         if (colWidth < 0)
     303               0 :                             colWidth = 0;
     304                 :                     }
     305               0 :                     if (styleWidth->GetUnit() != eStyleUnit_Percent) {
     306               0 :                         specTotal += colWidth;
     307                 :                     }
     308                 :                 }
     309                 :             } else {
     310               0 :                 colWidth = unassignedMarker;
     311                 :             }
     312                 :         }
     313                 : 
     314               0 :         colFrame->SetFinalWidth(colWidth);
     315                 : 
     316               0 :         if (colWidth == unassignedMarker) {
     317               0 :             ++unassignedCount;
     318                 :         } else {
     319               0 :             unassignedSpace -= colWidth;
     320                 :         }
     321                 :     }
     322                 : 
     323               0 :     if (unassignedSpace < 0) {
     324               0 :         if (pctTotal > 0) {
     325                 :             // If the columns took up too much space, reduce those that
     326                 :             // had percentage widths.  The spec doesn't say to do this,
     327                 :             // but we've always done it in the past, and so does WinIE6.
     328               0 :             nscoord pctUsed = NSToCoordFloor(pctTotal * float(tableWidth));
     329               0 :             nscoord reduce = NS_MIN(pctUsed, -unassignedSpace);
     330               0 :             float reduceRatio = float(reduce) / pctTotal;
     331               0 :             for (PRInt32 col = 0; col < colCount; ++col) {
     332               0 :                 nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
     333               0 :                 if (!colFrame) {
     334               0 :                     NS_ERROR("column frames out of sync with cell map");
     335               0 :                     continue;
     336                 :                 }
     337               0 :                 nscoord colWidth = colFrame->GetFinalWidth();
     338               0 :                 colWidth -= NSToCoordFloor(colFrame->GetPrefPercent() *
     339               0 :                                            reduceRatio);
     340               0 :                 if (colWidth < 0)
     341               0 :                     colWidth = 0;
     342               0 :                 colFrame->SetFinalWidth(colWidth);
     343                 :             }
     344                 :         }
     345               0 :         unassignedSpace = 0;
     346                 :     }
     347                 : 
     348               0 :     if (unassignedCount > 0) {
     349                 :         // The spec says to distribute the remaining space evenly among
     350                 :         // the columns.
     351               0 :         nscoord toAssign = unassignedSpace / unassignedCount;
     352               0 :         for (PRInt32 col = 0; col < colCount; ++col) {
     353               0 :             nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
     354               0 :             if (!colFrame) {
     355               0 :                 NS_ERROR("column frames out of sync with cell map");
     356               0 :                 continue;
     357                 :             }
     358               0 :             if (colFrame->GetFinalWidth() == unassignedMarker)
     359               0 :                 colFrame->SetFinalWidth(toAssign);
     360                 :         }
     361               0 :     } else if (unassignedSpace > 0) {
     362                 :         // The spec doesn't say how to distribute the unassigned space.
     363               0 :         if (specTotal > 0) {
     364                 :             // Distribute proportionally to non-percentage columns.
     365               0 :             nscoord specUndist = specTotal;
     366               0 :             for (PRInt32 col = 0; col < colCount; ++col) {
     367               0 :                 nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
     368               0 :                 if (!colFrame) {
     369               0 :                     NS_ERROR("column frames out of sync with cell map");
     370               0 :                     continue;
     371                 :                 }
     372               0 :                 if (colFrame->GetPrefPercent() == 0.0f) {
     373               0 :                     NS_ASSERTION(colFrame->GetFinalWidth() <= specUndist,
     374                 :                                  "widths don't add up");
     375                 :                     nscoord toAdd = AllocateUnassigned(unassignedSpace,
     376               0 :                        float(colFrame->GetFinalWidth()) / float(specUndist));
     377               0 :                     specUndist -= colFrame->GetFinalWidth();
     378               0 :                     colFrame->SetFinalWidth(colFrame->GetFinalWidth() + toAdd);
     379               0 :                     unassignedSpace -= toAdd;
     380               0 :                     if (specUndist <= 0) {
     381               0 :                         NS_ASSERTION(specUndist == 0,
     382                 :                                      "math should be exact");
     383               0 :                         break;
     384                 :                     }
     385                 :                 }
     386                 :             }
     387               0 :             NS_ASSERTION(unassignedSpace == 0, "failed to redistribute");
     388               0 :         } else if (pctTotal > 0) {
     389                 :             // Distribute proportionally to percentage columns.
     390               0 :             float pctUndist = pctTotal;
     391               0 :             for (PRInt32 col = 0; col < colCount; ++col) {
     392               0 :                 nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
     393               0 :                 if (!colFrame) {
     394               0 :                     NS_ERROR("column frames out of sync with cell map");
     395               0 :                     continue;
     396                 :                 }
     397               0 :                 if (pctUndist < colFrame->GetPrefPercent()) {
     398                 :                     // This can happen with floating-point math.
     399               0 :                     NS_ASSERTION(colFrame->GetPrefPercent() - pctUndist
     400                 :                                    < 0.0001,
     401                 :                                  "widths don't add up");
     402               0 :                     pctUndist = colFrame->GetPrefPercent();
     403                 :                 }
     404                 :                 nscoord toAdd = AllocateUnassigned(unassignedSpace,
     405               0 :                     colFrame->GetPrefPercent() / pctUndist);
     406               0 :                 colFrame->SetFinalWidth(colFrame->GetFinalWidth() + toAdd);
     407               0 :                 unassignedSpace -= toAdd;
     408               0 :                 pctUndist -= colFrame->GetPrefPercent();
     409               0 :                 if (pctUndist <= 0.0f) {
     410               0 :                     break;
     411                 :                 }
     412                 :             }
     413               0 :             NS_ASSERTION(unassignedSpace == 0, "failed to redistribute");
     414                 :         } else {
     415                 :             // Distribute equally to the zero-width columns.
     416               0 :             PRInt32 colsLeft = colCount;
     417               0 :             for (PRInt32 col = 0; col < colCount; ++col) {
     418               0 :                 nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
     419               0 :                 if (!colFrame) {
     420               0 :                     NS_ERROR("column frames out of sync with cell map");
     421               0 :                     continue;
     422                 :                 }
     423               0 :                 NS_ASSERTION(colFrame->GetFinalWidth() == 0, "yikes");
     424                 :                 nscoord toAdd = AllocateUnassigned(unassignedSpace,
     425               0 :                                                    1.0f / float(colsLeft));
     426               0 :                 colFrame->SetFinalWidth(toAdd);
     427               0 :                 unassignedSpace -= toAdd;
     428               0 :                 --colsLeft;
     429                 :             }
     430               0 :             NS_ASSERTION(unassignedSpace == 0, "failed to redistribute");
     431                 :         }
     432                 :     }
     433               0 :     for (PRInt32 col = 0; col < colCount; ++col) {
     434               0 :         nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
     435               0 :         if (!colFrame) {
     436               0 :             NS_ERROR("column frames out of sync with cell map");
     437               0 :             continue;
     438                 :         }
     439               0 :         if (oldColWidths.ElementAt(col) != colFrame->GetFinalWidth()) {
     440               0 :             mTableFrame->DidResizeColumns();
     441               0 :             break;
     442                 :         }
     443                 :     }
     444                 : }

Generated by: LCOV version 1.7