LCOV - code coverage report
Current view: top level - layout/tables - FixedTableLayoutStrategy.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 208 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 8 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13