12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061 |
- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- // vim:cindent:ts=4:et:sw=4:
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- /*
- * Web-compatible algorithms that determine column and table widths,
- * used for CSS2's 'table-layout: auto'.
- */
- #include "BasicTableLayoutStrategy.h"
- #include <algorithm>
- #include "nsTableFrame.h"
- #include "nsTableColFrame.h"
- #include "nsTableCellFrame.h"
- #include "nsLayoutUtils.h"
- #include "nsGkAtoms.h"
- #include "SpanningCellSorter.h"
- #include "nsIContent.h"
- using namespace mozilla;
- using namespace mozilla::layout;
- namespace css = mozilla::css;
- #undef DEBUG_TABLE_STRATEGY
- BasicTableLayoutStrategy::BasicTableLayoutStrategy(nsTableFrame *aTableFrame)
- : nsITableLayoutStrategy(nsITableLayoutStrategy::Auto)
- , mTableFrame(aTableFrame)
- {
- MarkIntrinsicISizesDirty();
- }
- /* virtual */
- BasicTableLayoutStrategy::~BasicTableLayoutStrategy()
- {
- }
- /* virtual */ nscoord
- BasicTableLayoutStrategy::GetMinISize(nsRenderingContext* aRenderingContext)
- {
- DISPLAY_MIN_WIDTH(mTableFrame, mMinISize);
- if (mMinISize == NS_INTRINSIC_WIDTH_UNKNOWN) {
- ComputeIntrinsicISizes(aRenderingContext);
- }
- return mMinISize;
- }
- /* virtual */ nscoord
- BasicTableLayoutStrategy::GetPrefISize(nsRenderingContext* aRenderingContext,
- bool aComputingSize)
- {
- DISPLAY_PREF_WIDTH(mTableFrame, mPrefISize);
- NS_ASSERTION((mPrefISize == NS_INTRINSIC_WIDTH_UNKNOWN) ==
- (mPrefISizePctExpand == NS_INTRINSIC_WIDTH_UNKNOWN),
- "dirtyness out of sync");
- if (mPrefISize == NS_INTRINSIC_WIDTH_UNKNOWN) {
- ComputeIntrinsicISizes(aRenderingContext);
- }
- return aComputingSize ? mPrefISizePctExpand : mPrefISize;
- }
- struct CellISizeInfo {
- CellISizeInfo(nscoord aMinCoord, nscoord aPrefCoord,
- float aPrefPercent, bool aHasSpecifiedISize)
- : hasSpecifiedISize(aHasSpecifiedISize)
- , minCoord(aMinCoord)
- , prefCoord(aPrefCoord)
- , prefPercent(aPrefPercent)
- {
- }
- bool hasSpecifiedISize;
- nscoord minCoord;
- nscoord prefCoord;
- float prefPercent;
- };
- // Used for both column and cell calculations. The parts needed only
- // for cells are skipped when aIsCell is false.
- static CellISizeInfo
- GetISizeInfo(nsRenderingContext *aRenderingContext,
- nsIFrame *aFrame, WritingMode aWM, bool aIsCell)
- {
- nscoord minCoord, prefCoord;
- const nsStylePosition *stylePos = aFrame->StylePosition();
- bool isQuirks = aFrame->PresContext()->CompatibilityMode() ==
- eCompatibility_NavQuirks;
- nscoord boxSizingToBorderEdge = 0;
- if (aIsCell) {
- // If aFrame is a container for font size inflation, then shrink
- // wrapping inside of it should not apply font size inflation.
- AutoMaybeDisableFontInflation an(aFrame);
- minCoord = aFrame->GetMinISize(aRenderingContext);
- prefCoord = aFrame->GetPrefISize(aRenderingContext);
- // Until almost the end of this function, minCoord and prefCoord
- // represent the box-sizing based isize values (which mean they
- // should include inline padding and border width when
- // box-sizing is set to border-box).
- // Note that this function returns border-box isize, we add the
- // outer edges near the end of this function.
- // XXX Should we ignore percentage padding?
- nsIFrame::IntrinsicISizeOffsetData offsets =
- aFrame->IntrinsicISizeOffsets();
- // In quirks mode, table cell isize should be content-box,
- // but bsize should be border box.
- // Because of this historic anomaly, we do not use quirk.css.
- // (We can't specify one value of box-sizing for isize and another
- // for bsize).
- // For this reason, we also do not use box-sizing for just one of
- // them, as this may be confusing.
- if (isQuirks || stylePos->mBoxSizing == StyleBoxSizing::Content) {
- boxSizingToBorderEdge = offsets.hPadding + offsets.hBorder;
- }
- else {
- // StyleBoxSizing::Border and standards-mode
- minCoord += offsets.hPadding + offsets.hBorder;
- prefCoord += offsets.hPadding + offsets.hBorder;
- }
- } else {
- minCoord = 0;
- prefCoord = 0;
- }
- float prefPercent = 0.0f;
- bool hasSpecifiedISize = false;
- const nsStyleCoord& iSize = stylePos->ISize(aWM);
- nsStyleUnit unit = iSize.GetUnit();
- // NOTE: We're ignoring calc() units with percentages here, for lack of a
- // sensible idea for what to do with them. This means calc() with
- // percentages is basically handled like 'auto' for table cells and
- // columns.
- if (iSize.ConvertsToLength()) {
- hasSpecifiedISize = true;
- // Note: since ComputeISizeValue was designed to return content-box
- // isize, it will (in some cases) subtract the box-sizing edges.
- // We prevent this unwanted behavior by calling it with
- // aContentEdgeToBoxSizing and aBoxSizingToMarginEdge set to 0.
- nscoord c = aFrame->ComputeISizeValue(aRenderingContext, 0, 0, 0, iSize);
- // Quirk: A cell with "nowrap" set and a coord value for the
- // isize which is bigger than the intrinsic minimum isize uses
- // that coord value as the minimum isize.
- // This is kept up-to-date with dynamic changes to nowrap by code in
- // nsTableCellFrame::AttributeChanged
- if (aIsCell && c > minCoord && isQuirks &&
- aFrame->GetContent()->HasAttr(kNameSpaceID_None,
- nsGkAtoms::nowrap)) {
- minCoord = c;
- }
- prefCoord = std::max(c, minCoord);
- } else if (unit == eStyleUnit_Percent) {
- prefPercent = iSize.GetPercentValue();
- } else if (unit == eStyleUnit_Enumerated && aIsCell) {
- switch (iSize.GetIntValue()) {
- case NS_STYLE_WIDTH_MAX_CONTENT:
- // 'inline-size' only affects pref isize, not min
- // isize, so don't change anything
- break;
- case NS_STYLE_WIDTH_MIN_CONTENT:
- prefCoord = minCoord;
- break;
- case NS_STYLE_WIDTH_FIT_CONTENT:
- case NS_STYLE_WIDTH_AVAILABLE:
- // act just like 'inline-size: auto'
- break;
- default:
- NS_NOTREACHED("unexpected enumerated value");
- }
- }
- nsStyleCoord maxISize(stylePos->MaxISize(aWM));
- if (maxISize.GetUnit() == eStyleUnit_Enumerated) {
- if (!aIsCell || maxISize.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE) {
- maxISize.SetNoneValue();
- } else if (maxISize.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT) {
- // for 'max-inline-size', '-moz-fit-content' is like
- // '-moz-max-content'
- maxISize.SetIntValue(NS_STYLE_WIDTH_MAX_CONTENT,
- eStyleUnit_Enumerated);
- }
- }
- unit = maxISize.GetUnit();
- // XXX To really implement 'max-inline-size' well, we'd need to store
- // it separately on the columns.
- if (maxISize.ConvertsToLength() || unit == eStyleUnit_Enumerated) {
- nscoord c = aFrame->ComputeISizeValue(aRenderingContext,
- 0, 0, 0, maxISize);
- minCoord = std::min(c, minCoord);
- prefCoord = std::min(c, prefCoord);
- } else if (unit == eStyleUnit_Percent) {
- float p = stylePos->MaxISize(aWM).GetPercentValue();
- if (p < prefPercent) {
- prefPercent = p;
- }
- }
- // treat calc() with percentages on max-inline-size just like 'none'.
- nsStyleCoord minISize(stylePos->MinISize(aWM));
- if (minISize.GetUnit() == eStyleUnit_Enumerated) {
- if (!aIsCell || minISize.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE) {
- minISize.SetCoordValue(0);
- } else if (minISize.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT) {
- // for 'min-inline-size', '-moz-fit-content' is like
- // '-moz-min-content'
- minISize.SetIntValue(NS_STYLE_WIDTH_MIN_CONTENT,
- eStyleUnit_Enumerated);
- }
- }
- unit = minISize.GetUnit();
- if (minISize.ConvertsToLength() || unit == eStyleUnit_Enumerated) {
- nscoord c = aFrame->ComputeISizeValue(aRenderingContext,
- 0, 0, 0, minISize);
- minCoord = std::max(c, minCoord);
- prefCoord = std::max(c, prefCoord);
- } else if (unit == eStyleUnit_Percent) {
- float p = stylePos->MinISize(aWM).GetPercentValue();
- if (p > prefPercent) {
- prefPercent = p;
- }
- }
- // treat calc() with percentages on min-inline-size just like '0'.
- // XXX Should col frame have border/padding considered?
- if (aIsCell) {
- minCoord += boxSizingToBorderEdge;
- prefCoord = NSCoordSaturatingAdd(prefCoord, boxSizingToBorderEdge);
- }
- return CellISizeInfo(minCoord, prefCoord, prefPercent, hasSpecifiedISize);
- }
- static inline CellISizeInfo
- GetCellISizeInfo(nsRenderingContext *aRenderingContext,
- nsTableCellFrame *aCellFrame, WritingMode aWM)
- {
- return GetISizeInfo(aRenderingContext, aCellFrame, aWM, true);
- }
- static inline CellISizeInfo
- GetColISizeInfo(nsRenderingContext *aRenderingContext,
- nsIFrame *aFrame, WritingMode aWM)
- {
- return GetISizeInfo(aRenderingContext, aFrame, aWM, false);
- }
- /**
- * The algorithm in this function, in addition to meeting the
- * requirements of Web-compatibility, is also invariant under reordering
- * of the rows within a table (something that most, but not all, other
- * browsers are).
- */
- void
- BasicTableLayoutStrategy::ComputeColumnIntrinsicISizes(nsRenderingContext* aRenderingContext)
- {
- nsTableFrame *tableFrame = mTableFrame;
- nsTableCellMap *cellMap = tableFrame->GetCellMap();
- WritingMode wm = tableFrame->GetWritingMode();
- mozilla::AutoStackArena arena;
- SpanningCellSorter spanningCells;
- // Loop over the columns to consider the columns and cells *without*
- // a colspan.
- int32_t col, col_end;
- for (col = 0, col_end = cellMap->GetColCount(); col < col_end; ++col) {
- nsTableColFrame *colFrame = tableFrame->GetColFrame(col);
- if (!colFrame) {
- NS_ERROR("column frames out of sync with cell map");
- continue;
- }
- colFrame->ResetIntrinsics();
- colFrame->ResetSpanIntrinsics();
- // Consider the isizes on the column.
- CellISizeInfo colInfo = GetColISizeInfo(aRenderingContext,
- colFrame, wm);
- colFrame->AddCoords(colInfo.minCoord, colInfo.prefCoord,
- colInfo.hasSpecifiedISize);
- colFrame->AddPrefPercent(colInfo.prefPercent);
- // Consider the isizes on the column-group. Note that we follow
- // what the HTML spec says here, and make the isize apply to
- // each column in the group, not the group as a whole.
- // If column has isize, column-group doesn't override isize.
- if (colInfo.minCoord == 0 && colInfo.prefCoord == 0 &&
- colInfo.prefPercent == 0.0f) {
- NS_ASSERTION(colFrame->GetParent()->GetType() ==
- nsGkAtoms::tableColGroupFrame,
- "expected a column-group");
- colInfo = GetColISizeInfo(aRenderingContext,
- colFrame->GetParent(), wm);
- colFrame->AddCoords(colInfo.minCoord, colInfo.prefCoord,
- colInfo.hasSpecifiedISize);
- colFrame->AddPrefPercent(colInfo.prefPercent);
- }
- // Consider the contents of and the isizes on the cells without
- // colspans.
- nsCellMapColumnIterator columnIter(cellMap, col);
- int32_t row, colSpan;
- nsTableCellFrame* cellFrame;
- while ((cellFrame = columnIter.GetNextFrame(&row, &colSpan))) {
- if (colSpan > 1) {
- spanningCells.AddCell(colSpan, row, col);
- continue;
- }
- CellISizeInfo info = GetCellISizeInfo(aRenderingContext,
- cellFrame, wm);
- colFrame->AddCoords(info.minCoord, info.prefCoord,
- info.hasSpecifiedISize);
- colFrame->AddPrefPercent(info.prefPercent);
- }
- #ifdef DEBUG_dbaron_off
- printf("table %p col %d nonspan: min=%d pref=%d spec=%d pct=%f\n",
- mTableFrame, col, colFrame->GetMinCoord(),
- colFrame->GetPrefCoord(), colFrame->GetHasSpecifiedCoord(),
- colFrame->GetPrefPercent());
- #endif
- }
- #ifdef DEBUG_TABLE_STRATEGY
- printf("ComputeColumnIntrinsicISizes single\n");
- mTableFrame->Dump(false, true, false);
- #endif
- // Consider the cells with a colspan that we saved in the loop above
- // into the spanning cell sorter. We consider these cells by seeing
- // if they require adding to the isizes resulting only from cells
- // with a smaller colspan, and therefore we must process them sorted
- // in increasing order by colspan. For each colspan group, we
- // accumulate new values to accumulate in the column frame's Span*
- // members.
- //
- // Considering things only relative to the isizes resulting from
- // cells with smaller colspans (rather than incrementally including
- // the results from spanning cells, or doing spanning and
- // non-spanning cells in a single pass) means that layout remains
- // row-order-invariant and (except for percentage isizes that add to
- // more than 100%) column-order invariant.
- //
- // Starting with smaller colspans makes it more likely that we
- // satisfy all the constraints given and don't distribute space to
- // columns where we don't need it.
- SpanningCellSorter::Item *item;
- int32_t colSpan;
- while ((item = spanningCells.GetNext(&colSpan))) {
- NS_ASSERTION(colSpan > 1,
- "cell should not have been put in spanning cell sorter");
- do {
- int32_t row = item->row;
- col = item->col;
- CellData *cellData = cellMap->GetDataAt(row, col);
- NS_ASSERTION(cellData && cellData->IsOrig(),
- "bogus result from spanning cell sorter");
- nsTableCellFrame *cellFrame = cellData->GetCellFrame();
- NS_ASSERTION(cellFrame, "bogus result from spanning cell sorter");
- CellISizeInfo info =
- GetCellISizeInfo(aRenderingContext, cellFrame, wm);
- if (info.prefPercent > 0.0f) {
- DistributePctISizeToColumns(info.prefPercent,
- col, colSpan);
- }
- DistributeISizeToColumns(info.minCoord, col, colSpan,
- BTLS_MIN_ISIZE, info.hasSpecifiedISize);
- DistributeISizeToColumns(info.prefCoord, col, colSpan,
- BTLS_PREF_ISIZE, info.hasSpecifiedISize);
- } while ((item = item->next));
- // Combine the results of the span analysis into the main results,
- // for each increment of colspan.
- for (col = 0, col_end = cellMap->GetColCount(); col < col_end; ++col) {
- nsTableColFrame *colFrame = tableFrame->GetColFrame(col);
- if (!colFrame) {
- NS_ERROR("column frames out of sync with cell map");
- continue;
- }
- colFrame->AccumulateSpanIntrinsics();
- colFrame->ResetSpanIntrinsics();
- #ifdef DEBUG_dbaron_off
- printf("table %p col %d span %d: min=%d pref=%d spec=%d pct=%f\n",
- mTableFrame, col, colSpan, colFrame->GetMinCoord(),
- colFrame->GetPrefCoord(), colFrame->GetHasSpecifiedCoord(),
- colFrame->GetPrefPercent());
- #endif
- }
- }
- // Prevent percentages from adding to more than 100% by (to be
- // compatible with other browsers) treating any percentages that would
- // increase the total percentage to more than 100% as the number that
- // would increase it to only 100% (which is 0% if we've already hit
- // 100%). This means layout depends on the order of columns.
- float pct_used = 0.0f;
- for (col = 0, col_end = cellMap->GetColCount(); col < col_end; ++col) {
- nsTableColFrame *colFrame = tableFrame->GetColFrame(col);
- if (!colFrame) {
- NS_ERROR("column frames out of sync with cell map");
- continue;
- }
- colFrame->AdjustPrefPercent(&pct_used);
- }
- #ifdef DEBUG_TABLE_STRATEGY
- printf("ComputeColumnIntrinsicISizes spanning\n");
- mTableFrame->Dump(false, true, false);
- #endif
- }
- void
- BasicTableLayoutStrategy::ComputeIntrinsicISizes(nsRenderingContext* aRenderingContext)
- {
- ComputeColumnIntrinsicISizes(aRenderingContext);
- nsTableCellMap *cellMap = mTableFrame->GetCellMap();
- nscoord min = 0, pref = 0, max_small_pct_pref = 0, nonpct_pref_total = 0;
- float pct_total = 0.0f; // always from 0.0f - 1.0f
- int32_t colCount = cellMap->GetColCount();
- // add a total of (colcount + 1) lots of cellSpacingX for columns where a
- // cell originates
- nscoord add = mTableFrame->GetColSpacing(colCount);
- for (int32_t col = 0; col < colCount; ++col) {
- nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
- if (!colFrame) {
- NS_ERROR("column frames out of sync with cell map");
- continue;
- }
- if (mTableFrame->ColumnHasCellSpacingBefore(col)) {
- add += mTableFrame->GetColSpacing(col - 1);
- }
- min += colFrame->GetMinCoord();
- pref = NSCoordSaturatingAdd(pref, colFrame->GetPrefCoord());
- // Percentages are of the table, so we have to reverse them for
- // intrinsic isizes.
- float p = colFrame->GetPrefPercent();
- if (p > 0.0f) {
- nscoord colPref = colFrame->GetPrefCoord();
- nscoord new_small_pct_expand =
- (colPref == nscoord_MAX ?
- nscoord_MAX : nscoord(float(colPref) / p));
- if (new_small_pct_expand > max_small_pct_pref) {
- max_small_pct_pref = new_small_pct_expand;
- }
- pct_total += p;
- } else {
- nonpct_pref_total = NSCoordSaturatingAdd(nonpct_pref_total,
- colFrame->GetPrefCoord());
- }
- }
- nscoord pref_pct_expand = pref;
- // Account for small percentages expanding the preferred isize of
- // *other* columns.
- if (max_small_pct_pref > pref_pct_expand) {
- pref_pct_expand = max_small_pct_pref;
- }
- // Account for large percentages expanding the preferred isize of
- // themselves. There's no need to iterate over the columns multiple
- // times, since when there is such a need, the small percentage
- // effect is bigger anyway. (I think!)
- NS_ASSERTION(0.0f <= pct_total && pct_total <= 1.0f,
- "column percentage inline-sizes not adjusted down to 100%");
- if (pct_total == 1.0f) {
- if (nonpct_pref_total > 0) {
- pref_pct_expand = nscoord_MAX;
- // XXX Or should I use some smaller value? (Test this using
- // nested tables!)
- }
- } else {
- nscoord large_pct_pref =
- (nonpct_pref_total == nscoord_MAX ?
- nscoord_MAX :
- nscoord(float(nonpct_pref_total) / (1.0f - pct_total)));
- if (large_pct_pref > pref_pct_expand)
- pref_pct_expand = large_pct_pref;
- }
- // border-spacing isn't part of the basis for percentages
- if (colCount > 0) {
- min += add;
- pref = NSCoordSaturatingAdd(pref, add);
- pref_pct_expand = NSCoordSaturatingAdd(pref_pct_expand, add);
- }
- mMinISize = min;
- mPrefISize = pref;
- mPrefISizePctExpand = pref_pct_expand;
- }
- /* virtual */ void
- BasicTableLayoutStrategy::MarkIntrinsicISizesDirty()
- {
- mMinISize = NS_INTRINSIC_WIDTH_UNKNOWN;
- mPrefISize = NS_INTRINSIC_WIDTH_UNKNOWN;
- mPrefISizePctExpand = NS_INTRINSIC_WIDTH_UNKNOWN;
- mLastCalcISize = nscoord_MIN;
- }
- /* virtual */ void
- BasicTableLayoutStrategy::ComputeColumnISizes(const ReflowInput& aReflowInput)
- {
- nscoord iSize = aReflowInput.ComputedISize();
- if (mLastCalcISize == iSize) {
- return;
- }
- mLastCalcISize = iSize;
- NS_ASSERTION((mMinISize == NS_INTRINSIC_WIDTH_UNKNOWN) ==
- (mPrefISize == NS_INTRINSIC_WIDTH_UNKNOWN),
- "dirtyness out of sync");
- NS_ASSERTION((mMinISize == NS_INTRINSIC_WIDTH_UNKNOWN) ==
- (mPrefISizePctExpand == NS_INTRINSIC_WIDTH_UNKNOWN),
- "dirtyness out of sync");
- // XXX Is this needed?
- if (mMinISize == NS_INTRINSIC_WIDTH_UNKNOWN) {
- ComputeIntrinsicISizes(aReflowInput.mRenderingContext);
- }
- nsTableCellMap *cellMap = mTableFrame->GetCellMap();
- int32_t colCount = cellMap->GetColCount();
- if (colCount <= 0)
- return; // nothing to do
- DistributeISizeToColumns(iSize, 0, colCount, BTLS_FINAL_ISIZE, false);
- #ifdef DEBUG_TABLE_STRATEGY
- printf("ComputeColumnISizes final\n");
- mTableFrame->Dump(false, true, false);
- #endif
- }
- void
- BasicTableLayoutStrategy::DistributePctISizeToColumns(float aSpanPrefPct,
- int32_t aFirstCol,
- int32_t aColCount)
- {
- // First loop to determine:
- int32_t nonPctColCount = 0; // number of spanned columns without % isize
- nscoord nonPctTotalPrefISize = 0; // total pref isize of those columns
- // and to reduce aSpanPrefPct by columns that already have % isize
- int32_t scol, scol_end;
- nsTableCellMap *cellMap = mTableFrame->GetCellMap();
- for (scol = aFirstCol, scol_end = aFirstCol + aColCount;
- scol < scol_end; ++scol) {
- nsTableColFrame *scolFrame = mTableFrame->GetColFrame(scol);
- if (!scolFrame) {
- NS_ERROR("column frames out of sync with cell map");
- continue;
- }
- float scolPct = scolFrame->GetPrefPercent();
- if (scolPct == 0.0f) {
- nonPctTotalPrefISize += scolFrame->GetPrefCoord();
- if (cellMap->GetNumCellsOriginatingInCol(scol) > 0) {
- ++nonPctColCount;
- }
- } else {
- aSpanPrefPct -= scolPct;
- }
- }
- if (aSpanPrefPct <= 0.0f || nonPctColCount == 0) {
- // There's no %-isize on the colspan left over to distribute,
- // or there are no columns to which we could distribute %-isize
- return;
- }
- // Second loop, to distribute what remains of aSpanPrefPct
- // between the non-percent-isize spanned columns
- const bool spanHasNonPctPref = nonPctTotalPrefISize > 0; // Loop invariant
- for (scol = aFirstCol, scol_end = aFirstCol + aColCount;
- scol < scol_end; ++scol) {
- nsTableColFrame *scolFrame = mTableFrame->GetColFrame(scol);
- if (!scolFrame) {
- NS_ERROR("column frames out of sync with cell map");
- continue;
- }
- if (scolFrame->GetPrefPercent() == 0.0f) {
- NS_ASSERTION((!spanHasNonPctPref ||
- nonPctTotalPrefISize != 0) &&
- nonPctColCount != 0,
- "should not be zero if we haven't allocated "
- "all pref percent");
- float allocatedPct; // % isize to be given to this column
- if (spanHasNonPctPref) {
- // Group so we're multiplying by 1.0f when we need
- // to use up aSpanPrefPct.
- allocatedPct = aSpanPrefPct *
- (float(scolFrame->GetPrefCoord()) /
- float(nonPctTotalPrefISize));
- } else if (cellMap->GetNumCellsOriginatingInCol(scol) > 0) {
- // distribute equally when all pref isizes are 0
- allocatedPct = aSpanPrefPct / float(nonPctColCount);
- } else {
- allocatedPct = 0.0f;
- }
- // Allocate the percent
- scolFrame->AddSpanPrefPercent(allocatedPct);
- // To avoid accumulating rounding error from division,
- // subtract this column's values from the totals.
- aSpanPrefPct -= allocatedPct;
- nonPctTotalPrefISize -= scolFrame->GetPrefCoord();
- if (cellMap->GetNumCellsOriginatingInCol(scol) > 0) {
- --nonPctColCount;
- }
- if (!aSpanPrefPct) {
- // No more span-percent-isize to distribute --> we're done.
- NS_ASSERTION(spanHasNonPctPref ?
- nonPctTotalPrefISize == 0 :
- nonPctColCount == 0,
- "No more pct inline-size to distribute, "
- "but there are still cols that need some.");
- return;
- }
- }
- }
- }
- void
- BasicTableLayoutStrategy::DistributeISizeToColumns(nscoord aISize,
- int32_t aFirstCol,
- int32_t aColCount,
- BtlsISizeType aISizeType,
- bool aSpanHasSpecifiedISize)
- {
- NS_ASSERTION(aISizeType != BTLS_FINAL_ISIZE ||
- (aFirstCol == 0 &&
- aColCount == mTableFrame->GetCellMap()->GetColCount()),
- "Computing final column isizes, but didn't get full column range");
- nscoord subtract = 0;
- // aISize initially includes border-spacing for the boundaries in between
- // each of the columns. We start at aFirstCol + 1 because the first
- // in-between boundary would be at the left edge of column aFirstCol + 1
- for (int32_t col = aFirstCol + 1; col < aFirstCol + aColCount; ++col) {
- if (mTableFrame->ColumnHasCellSpacingBefore(col)) {
- // border-spacing isn't part of the basis for percentages.
- subtract += mTableFrame->GetColSpacing(col - 1);
- }
- }
- if (aISizeType == BTLS_FINAL_ISIZE) {
- // If we're computing final col-isize, then aISize initially includes
- // border spacing on the table's far istart + far iend edge, too. Need
- // to subtract those out, too.
- subtract += (mTableFrame->GetColSpacing(-1) +
- mTableFrame->GetColSpacing(aColCount));
- }
- aISize = NSCoordSaturatingSubtract(aISize, subtract, nscoord_MAX);
- /*
- * The goal of this function is to distribute |aISize| between the
- * columns by making an appropriate AddSpanCoords or SetFinalISize
- * call for each column. (We call AddSpanCoords if we're
- * distributing a column-spanning cell's minimum or preferred isize
- * to its spanned columns. We call SetFinalISize if we're
- * distributing a table's final isize to its columns.)
- *
- * The idea is to either assign one of the following sets of isizes
- * or a weighted average of two adjacent sets of isizes. It is not
- * possible to assign values smaller than the smallest set of
- * isizes. However, see below for handling the case of assigning
- * values larger than the largest set of isizes. From smallest to
- * largest, these are:
- *
- * 1. [guess_min] Assign all columns their min isize.
- *
- * 2. [guess_min_pct] Assign all columns with percentage isizes
- * their percentage isize, and all other columns their min isize.
- *
- * 3. [guess_min_spec] Assign all columns with percentage isizes
- * their percentage isize, all columns with specified coordinate
- * isizes their pref isize (since it doesn't matter whether it's the
- * largest contributor to the pref isize that was the specified
- * contributor), and all other columns their min isize.
- *
- * 4. [guess_pref] Assign all columns with percentage isizes their
- * specified isize, and all other columns their pref isize.
- *
- * If |aISize| is *larger* than what we would assign in (4), then we
- * expand the columns:
- *
- * a. if any columns without a specified coordinate isize or
- * percent isize have nonzero pref isize, in proportion to pref
- * isize [total_flex_pref]
- *
- * b. otherwise, if any columns without a specified coordinate
- * isize or percent isize, but with cells originating in them,
- * have zero pref isize, equally between these
- * [numNonSpecZeroISizeCols]
- *
- * c. otherwise, if any columns without percent isize have nonzero
- * pref isize, in proportion to pref isize [total_fixed_pref]
- *
- * d. otherwise, if any columns have nonzero percentage isizes, in
- * proportion to the percentage isizes [total_pct]
- *
- * e. otherwise, equally.
- */
- // Loop #1 over the columns, to figure out the four values above so
- // we know which case we're dealing with.
- nscoord guess_min = 0,
- guess_min_pct = 0,
- guess_min_spec = 0,
- guess_pref = 0,
- total_flex_pref = 0,
- total_fixed_pref = 0;
- float total_pct = 0.0f; // 0.0f to 1.0f
- int32_t numInfiniteISizeCols = 0;
- int32_t numNonSpecZeroISizeCols = 0;
- int32_t col;
- nsTableCellMap *cellMap = mTableFrame->GetCellMap();
- for (col = aFirstCol; col < aFirstCol + aColCount; ++col) {
- nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
- if (!colFrame) {
- NS_ERROR("column frames out of sync with cell map");
- continue;
- }
- nscoord min_iSize = colFrame->GetMinCoord();
- guess_min += min_iSize;
- if (colFrame->GetPrefPercent() != 0.0f) {
- float pct = colFrame->GetPrefPercent();
- total_pct += pct;
- nscoord val = nscoord(float(aISize) * pct);
- if (val < min_iSize) {
- val = min_iSize;
- }
- guess_min_pct += val;
- guess_pref = NSCoordSaturatingAdd(guess_pref, val);
- } else {
- nscoord pref_iSize = colFrame->GetPrefCoord();
- if (pref_iSize == nscoord_MAX) {
- ++numInfiniteISizeCols;
- }
- guess_pref = NSCoordSaturatingAdd(guess_pref, pref_iSize);
- guess_min_pct += min_iSize;
- if (colFrame->GetHasSpecifiedCoord()) {
- // we'll add on the rest of guess_min_spec outside the
- // loop
- nscoord delta = NSCoordSaturatingSubtract(pref_iSize,
- min_iSize, 0);
- guess_min_spec = NSCoordSaturatingAdd(guess_min_spec, delta);
- total_fixed_pref = NSCoordSaturatingAdd(total_fixed_pref,
- pref_iSize);
- } else if (pref_iSize == 0) {
- if (cellMap->GetNumCellsOriginatingInCol(col) > 0) {
- ++numNonSpecZeroISizeCols;
- }
- } else {
- total_flex_pref = NSCoordSaturatingAdd(total_flex_pref,
- pref_iSize);
- }
- }
- }
- guess_min_spec = NSCoordSaturatingAdd(guess_min_spec, guess_min_pct);
- // Determine what we're flexing:
- enum Loop2Type {
- FLEX_PCT_SMALL, // between (1) and (2) above
- FLEX_FIXED_SMALL, // between (2) and (3) above
- FLEX_FLEX_SMALL, // between (3) and (4) above
- FLEX_FLEX_LARGE, // greater than (4) above, case (a)
- FLEX_FLEX_LARGE_ZERO, // greater than (4) above, case (b)
- FLEX_FIXED_LARGE, // greater than (4) above, case (c)
- FLEX_PCT_LARGE, // greater than (4) above, case (d)
- FLEX_ALL_LARGE // greater than (4) above, case (e)
- };
- Loop2Type l2t;
- // These are constants (over columns) for each case's math. We use
- // a pair of nscoords rather than a float so that we can subtract
- // each column's allocation so we avoid accumulating rounding error.
- nscoord space; // the amount of extra isize to allocate
- union {
- nscoord c;
- float f;
- } basis; // the sum of the statistic over columns to divide it
- if (aISize < guess_pref) {
- if (aISizeType != BTLS_FINAL_ISIZE && aISize <= guess_min) {
- // Return early -- we don't have any extra space to distribute.
- return;
- }
- NS_ASSERTION(!(aISizeType == BTLS_FINAL_ISIZE && aISize < guess_min),
- "Table inline-size is less than the "
- "sum of its columns' min inline-sizes");
- if (aISize < guess_min_pct) {
- l2t = FLEX_PCT_SMALL;
- space = aISize - guess_min;
- basis.c = guess_min_pct - guess_min;
- } else if (aISize < guess_min_spec) {
- l2t = FLEX_FIXED_SMALL;
- space = aISize - guess_min_pct;
- basis.c = NSCoordSaturatingSubtract(guess_min_spec, guess_min_pct,
- nscoord_MAX);
- } else {
- l2t = FLEX_FLEX_SMALL;
- space = aISize - guess_min_spec;
- basis.c = NSCoordSaturatingSubtract(guess_pref, guess_min_spec,
- nscoord_MAX);
- }
- } else {
- space = NSCoordSaturatingSubtract(aISize, guess_pref, nscoord_MAX);
- if (total_flex_pref > 0) {
- l2t = FLEX_FLEX_LARGE;
- basis.c = total_flex_pref;
- } else if (numNonSpecZeroISizeCols > 0) {
- l2t = FLEX_FLEX_LARGE_ZERO;
- basis.c = numNonSpecZeroISizeCols;
- } else if (total_fixed_pref > 0) {
- l2t = FLEX_FIXED_LARGE;
- basis.c = total_fixed_pref;
- } else if (total_pct > 0.0f) {
- l2t = FLEX_PCT_LARGE;
- basis.f = total_pct;
- } else {
- l2t = FLEX_ALL_LARGE;
- basis.c = aColCount;
- }
- }
- #ifdef DEBUG_dbaron_off
- printf("ComputeColumnISizes: %d columns in isize %d,\n"
- " guesses=[%d,%d,%d,%d], totals=[%d,%d,%f],\n"
- " l2t=%d, space=%d, basis.c=%d\n",
- aColCount, aISize,
- guess_min, guess_min_pct, guess_min_spec, guess_pref,
- total_flex_pref, total_fixed_pref, total_pct,
- l2t, space, basis.c);
- #endif
- for (col = aFirstCol; col < aFirstCol + aColCount; ++col) {
- nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
- if (!colFrame) {
- NS_ERROR("column frames out of sync with cell map");
- continue;
- }
- nscoord col_iSize;
- float pct = colFrame->GetPrefPercent();
- if (pct != 0.0f) {
- col_iSize = nscoord(float(aISize) * pct);
- nscoord col_min = colFrame->GetMinCoord();
- if (col_iSize < col_min) {
- col_iSize = col_min;
- }
- } else {
- col_iSize = colFrame->GetPrefCoord();
- }
- nscoord col_iSize_before_adjust = col_iSize;
- switch (l2t) {
- case FLEX_PCT_SMALL:
- col_iSize = col_iSize_before_adjust = colFrame->GetMinCoord();
- if (pct != 0.0f) {
- nscoord pct_minus_min =
- nscoord(float(aISize) * pct) - col_iSize;
- if (pct_minus_min > 0) {
- float c = float(space) / float(basis.c);
- basis.c -= pct_minus_min;
- col_iSize += NSToCoordRound(float(pct_minus_min) * c);
- }
- }
- break;
- case FLEX_FIXED_SMALL:
- if (pct == 0.0f) {
- NS_ASSERTION(col_iSize == colFrame->GetPrefCoord(),
- "wrong inline-size assigned");
- if (colFrame->GetHasSpecifiedCoord()) {
- nscoord col_min = colFrame->GetMinCoord();
- nscoord pref_minus_min = col_iSize - col_min;
- col_iSize = col_iSize_before_adjust = col_min;
- if (pref_minus_min != 0) {
- float c = float(space) / float(basis.c);
- basis.c -= pref_minus_min;
- col_iSize += NSToCoordRound(
- float(pref_minus_min) * c);
- }
- } else
- col_iSize = col_iSize_before_adjust =
- colFrame->GetMinCoord();
- }
- break;
- case FLEX_FLEX_SMALL:
- if (pct == 0.0f &&
- !colFrame->GetHasSpecifiedCoord()) {
- NS_ASSERTION(col_iSize == colFrame->GetPrefCoord(),
- "wrong inline-size assigned");
- nscoord col_min = colFrame->GetMinCoord();
- nscoord pref_minus_min =
- NSCoordSaturatingSubtract(col_iSize, col_min, 0);
- col_iSize = col_iSize_before_adjust = col_min;
- if (pref_minus_min != 0) {
- float c = float(space) / float(basis.c);
- // If we have infinite-isize cols, then the standard
- // adjustment to col_iSize using 'c' won't work,
- // because basis.c and pref_minus_min are both
- // nscoord_MAX and will cancel each other out in the
- // col_iSize adjustment (making us assign all the
- // space to the first inf-isize col). To correct for
- // this, we'll also divide by numInfiniteISizeCols to
- // spread the space equally among the inf-isize cols.
- if (numInfiniteISizeCols) {
- if (colFrame->GetPrefCoord() == nscoord_MAX) {
- c = c / float(numInfiniteISizeCols);
- --numInfiniteISizeCols;
- } else {
- c = 0.0f;
- }
- }
- basis.c = NSCoordSaturatingSubtract(basis.c,
- pref_minus_min,
- nscoord_MAX);
- col_iSize += NSToCoordRound(
- float(pref_minus_min) * c);
- }
- }
- break;
- case FLEX_FLEX_LARGE:
- if (pct == 0.0f &&
- !colFrame->GetHasSpecifiedCoord()) {
- NS_ASSERTION(col_iSize == colFrame->GetPrefCoord(),
- "wrong inline-size assigned");
- if (col_iSize != 0) {
- if (space == nscoord_MAX) {
- basis.c -= col_iSize;
- col_iSize = nscoord_MAX;
- } else {
- float c = float(space) / float(basis.c);
- basis.c -= col_iSize;
- col_iSize += NSToCoordRound(float(col_iSize) * c);
- }
- }
- }
- break;
- case FLEX_FLEX_LARGE_ZERO:
- if (pct == 0.0f &&
- !colFrame->GetHasSpecifiedCoord() &&
- cellMap->GetNumCellsOriginatingInCol(col) > 0) {
- NS_ASSERTION(col_iSize == 0 &&
- colFrame->GetPrefCoord() == 0,
- "Since we're in FLEX_FLEX_LARGE_ZERO case, "
- "all auto-inline-size cols should have zero "
- "pref inline-size.");
- float c = float(space) / float(basis.c);
- col_iSize += NSToCoordRound(c);
- --basis.c;
- }
- break;
- case FLEX_FIXED_LARGE:
- if (pct == 0.0f) {
- NS_ASSERTION(col_iSize == colFrame->GetPrefCoord(),
- "wrong inline-size assigned");
- NS_ASSERTION(colFrame->GetHasSpecifiedCoord() ||
- colFrame->GetPrefCoord() == 0,
- "wrong case");
- if (col_iSize != 0) {
- float c = float(space) / float(basis.c);
- basis.c -= col_iSize;
- col_iSize += NSToCoordRound(float(col_iSize) * c);
- }
- }
- break;
- case FLEX_PCT_LARGE:
- NS_ASSERTION(pct != 0.0f || colFrame->GetPrefCoord() == 0,
- "wrong case");
- if (pct != 0.0f) {
- float c = float(space) / basis.f;
- col_iSize += NSToCoordRound(pct * c);
- basis.f -= pct;
- }
- break;
- case FLEX_ALL_LARGE:
- {
- float c = float(space) / float(basis.c);
- col_iSize += NSToCoordRound(c);
- --basis.c;
- }
- break;
- }
- // Only subtract from space if it's a real number.
- if (space != nscoord_MAX) {
- NS_ASSERTION(col_iSize != nscoord_MAX,
- "How is col_iSize nscoord_MAX if space isn't?");
- NS_ASSERTION(col_iSize_before_adjust != nscoord_MAX,
- "How is col_iSize_before_adjust nscoord_MAX if space isn't?");
- space -= col_iSize - col_iSize_before_adjust;
- }
- NS_ASSERTION(col_iSize >= colFrame->GetMinCoord(),
- "assigned inline-size smaller than min");
-
- // Apply the new isize
- switch (aISizeType) {
- case BTLS_MIN_ISIZE:
- {
- // Note: AddSpanCoords requires both a min and pref isize.
- // For the pref isize, we'll just pass in our computed
- // min isize, because the real pref isize will be at least
- // as big
- colFrame->AddSpanCoords(col_iSize, col_iSize,
- aSpanHasSpecifiedISize);
- }
- break;
- case BTLS_PREF_ISIZE:
- {
- // Note: AddSpanCoords requires both a min and pref isize.
- // For the min isize, we'll just pass in 0, because
- // the real min isize will be at least 0
- colFrame->AddSpanCoords(0, col_iSize,
- aSpanHasSpecifiedISize);
- }
- break;
- case BTLS_FINAL_ISIZE:
- {
- nscoord old_final = colFrame->GetFinalISize();
- colFrame->SetFinalISize(col_iSize);
-
- if (old_final != col_iSize) {
- mTableFrame->DidResizeColumns();
- }
- }
- break;
- }
- }
- NS_ASSERTION((space == 0 || space == nscoord_MAX) &&
- ((l2t == FLEX_PCT_LARGE)
- ? (-0.001f < basis.f && basis.f < 0.001f)
- : (basis.c == 0 || basis.c == nscoord_MAX)),
- "didn't subtract all that we added");
- }
|