layout/tables/BasicTableLayoutStrategy.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 // vim:cindent:ts=4:et:sw=4:
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * Web-compatible algorithms that determine column and table widths,
michael@0 9 * used for CSS2's 'table-layout: auto'.
michael@0 10 */
michael@0 11
michael@0 12 #include "BasicTableLayoutStrategy.h"
michael@0 13 #include "nsTableFrame.h"
michael@0 14 #include "nsTableCellFrame.h"
michael@0 15 #include "nsLayoutUtils.h"
michael@0 16 #include "nsGkAtoms.h"
michael@0 17 #include "SpanningCellSorter.h"
michael@0 18 #include <algorithm>
michael@0 19
michael@0 20 using namespace mozilla;
michael@0 21 using namespace mozilla::layout;
michael@0 22
michael@0 23 namespace css = mozilla::css;
michael@0 24
michael@0 25 #undef DEBUG_TABLE_STRATEGY
michael@0 26
michael@0 27 BasicTableLayoutStrategy::BasicTableLayoutStrategy(nsTableFrame *aTableFrame)
michael@0 28 : nsITableLayoutStrategy(nsITableLayoutStrategy::Auto)
michael@0 29 , mTableFrame(aTableFrame)
michael@0 30 {
michael@0 31 MarkIntrinsicWidthsDirty();
michael@0 32 }
michael@0 33
michael@0 34 /* virtual */
michael@0 35 BasicTableLayoutStrategy::~BasicTableLayoutStrategy()
michael@0 36 {
michael@0 37 }
michael@0 38
michael@0 39 /* virtual */ nscoord
michael@0 40 BasicTableLayoutStrategy::GetMinWidth(nsRenderingContext* aRenderingContext)
michael@0 41 {
michael@0 42 DISPLAY_MIN_WIDTH(mTableFrame, mMinWidth);
michael@0 43 if (mMinWidth == NS_INTRINSIC_WIDTH_UNKNOWN)
michael@0 44 ComputeIntrinsicWidths(aRenderingContext);
michael@0 45 return mMinWidth;
michael@0 46 }
michael@0 47
michael@0 48 /* virtual */ nscoord
michael@0 49 BasicTableLayoutStrategy::GetPrefWidth(nsRenderingContext* aRenderingContext,
michael@0 50 bool aComputingSize)
michael@0 51 {
michael@0 52 DISPLAY_PREF_WIDTH(mTableFrame, mPrefWidth);
michael@0 53 NS_ASSERTION((mPrefWidth == NS_INTRINSIC_WIDTH_UNKNOWN) ==
michael@0 54 (mPrefWidthPctExpand == NS_INTRINSIC_WIDTH_UNKNOWN),
michael@0 55 "dirtyness out of sync");
michael@0 56 if (mPrefWidth == NS_INTRINSIC_WIDTH_UNKNOWN)
michael@0 57 ComputeIntrinsicWidths(aRenderingContext);
michael@0 58 return aComputingSize ? mPrefWidthPctExpand : mPrefWidth;
michael@0 59 }
michael@0 60
michael@0 61 struct CellWidthInfo {
michael@0 62 CellWidthInfo(nscoord aMinCoord, nscoord aPrefCoord,
michael@0 63 float aPrefPercent, bool aHasSpecifiedWidth)
michael@0 64 : hasSpecifiedWidth(aHasSpecifiedWidth)
michael@0 65 , minCoord(aMinCoord)
michael@0 66 , prefCoord(aPrefCoord)
michael@0 67 , prefPercent(aPrefPercent)
michael@0 68 {
michael@0 69 }
michael@0 70
michael@0 71 bool hasSpecifiedWidth;
michael@0 72 nscoord minCoord;
michael@0 73 nscoord prefCoord;
michael@0 74 float prefPercent;
michael@0 75 };
michael@0 76
michael@0 77 // Used for both column and cell calculations. The parts needed only
michael@0 78 // for cells are skipped when aIsCell is false.
michael@0 79 static CellWidthInfo
michael@0 80 GetWidthInfo(nsRenderingContext *aRenderingContext,
michael@0 81 nsIFrame *aFrame, bool aIsCell)
michael@0 82 {
michael@0 83 nscoord minCoord, prefCoord;
michael@0 84 const nsStylePosition *stylePos = aFrame->StylePosition();
michael@0 85 bool isQuirks = aFrame->PresContext()->CompatibilityMode() ==
michael@0 86 eCompatibility_NavQuirks;
michael@0 87 nscoord boxSizingToBorderEdge = 0;
michael@0 88 if (aIsCell) {
michael@0 89 // If aFrame is a container for font size inflation, then shrink
michael@0 90 // wrapping inside of it should not apply font size inflation.
michael@0 91 AutoMaybeDisableFontInflation an(aFrame);
michael@0 92
michael@0 93 minCoord = aFrame->GetMinWidth(aRenderingContext);
michael@0 94 prefCoord = aFrame->GetPrefWidth(aRenderingContext);
michael@0 95 // Until almost the end of this function, minCoord and prefCoord
michael@0 96 // represent the box-sizing based width values (which mean they
michael@0 97 // should include horizontal padding and border width when
michael@0 98 // box-sizing is set to border-box).
michael@0 99 // Note that this function returns border-box width, we add the
michael@0 100 // outer edges near the end of this function.
michael@0 101
michael@0 102 // XXX Should we ignore percentage padding?
michael@0 103 nsIFrame::IntrinsicWidthOffsetData offsets = aFrame->IntrinsicWidthOffsets(aRenderingContext);
michael@0 104
michael@0 105 // In quirks mode, table cell width should be content-box,
michael@0 106 // but height should be border box.
michael@0 107 // Because of this historic anomaly, we do not use quirk.css.
michael@0 108 // (We can't specify one value of box-sizing for width and another
michael@0 109 // for height).
michael@0 110 // For this reason, we also do not use box-sizing for just one of
michael@0 111 // them, as this may be confusing.
michael@0 112 if (isQuirks) {
michael@0 113 boxSizingToBorderEdge = offsets.hPadding + offsets.hBorder;
michael@0 114 }
michael@0 115 else {
michael@0 116 switch (stylePos->mBoxSizing) {
michael@0 117 case NS_STYLE_BOX_SIZING_CONTENT:
michael@0 118 boxSizingToBorderEdge = offsets.hPadding + offsets.hBorder;
michael@0 119 break;
michael@0 120 case NS_STYLE_BOX_SIZING_PADDING:
michael@0 121 minCoord += offsets.hPadding;
michael@0 122 prefCoord += offsets.hPadding;
michael@0 123 boxSizingToBorderEdge = offsets.hBorder;
michael@0 124 break;
michael@0 125 default:
michael@0 126 // NS_STYLE_BOX_SIZING_BORDER
michael@0 127 minCoord += offsets.hPadding + offsets.hBorder;
michael@0 128 prefCoord += offsets.hPadding + offsets.hBorder;
michael@0 129 break;
michael@0 130 }
michael@0 131 }
michael@0 132 } else {
michael@0 133 minCoord = 0;
michael@0 134 prefCoord = 0;
michael@0 135 }
michael@0 136 float prefPercent = 0.0f;
michael@0 137 bool hasSpecifiedWidth = false;
michael@0 138
michael@0 139 const nsStyleCoord &width = stylePos->mWidth;
michael@0 140 nsStyleUnit unit = width.GetUnit();
michael@0 141 // NOTE: We're ignoring calc() units with percentages here, for lack of a
michael@0 142 // sensible idea for what to do with them. This means calc() with
michael@0 143 // percentages is basically handled like 'auto' for table cells and
michael@0 144 // columns.
michael@0 145 if (width.ConvertsToLength()) {
michael@0 146 hasSpecifiedWidth = true;
michael@0 147 // Note: since ComputeWidthValue was designed to return content-box
michael@0 148 // width, it will (in some cases) subtract the box-sizing edges.
michael@0 149 // We prevent this unwanted behavior by calling it with
michael@0 150 // aContentEdgeToBoxSizing and aBoxSizingToMarginEdge set to 0.
michael@0 151 nscoord w = nsLayoutUtils::ComputeWidthValue(aRenderingContext,
michael@0 152 aFrame, 0, 0, 0, width);
michael@0 153 // Quirk: A cell with "nowrap" set and a coord value for the
michael@0 154 // width which is bigger than the intrinsic minimum width uses
michael@0 155 // that coord value as the minimum width.
michael@0 156 // This is kept up-to-date with dynamic changes to nowrap by code in
michael@0 157 // nsTableCellFrame::AttributeChanged
michael@0 158 if (aIsCell && w > minCoord && isQuirks &&
michael@0 159 aFrame->GetContent()->HasAttr(kNameSpaceID_None,
michael@0 160 nsGkAtoms::nowrap)) {
michael@0 161 minCoord = w;
michael@0 162 }
michael@0 163 prefCoord = std::max(w, minCoord);
michael@0 164 } else if (unit == eStyleUnit_Percent) {
michael@0 165 prefPercent = width.GetPercentValue();
michael@0 166 } else if (unit == eStyleUnit_Enumerated && aIsCell) {
michael@0 167 switch (width.GetIntValue()) {
michael@0 168 case NS_STYLE_WIDTH_MAX_CONTENT:
michael@0 169 // 'width' only affects pref width, not min
michael@0 170 // width, so don't change anything
michael@0 171 break;
michael@0 172 case NS_STYLE_WIDTH_MIN_CONTENT:
michael@0 173 prefCoord = minCoord;
michael@0 174 break;
michael@0 175 case NS_STYLE_WIDTH_FIT_CONTENT:
michael@0 176 case NS_STYLE_WIDTH_AVAILABLE:
michael@0 177 // act just like 'width: auto'
michael@0 178 break;
michael@0 179 default:
michael@0 180 NS_NOTREACHED("unexpected enumerated value");
michael@0 181 }
michael@0 182 }
michael@0 183
michael@0 184 nsStyleCoord maxWidth(stylePos->mMaxWidth);
michael@0 185 if (maxWidth.GetUnit() == eStyleUnit_Enumerated) {
michael@0 186 if (!aIsCell || maxWidth.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE)
michael@0 187 maxWidth.SetNoneValue();
michael@0 188 else if (maxWidth.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT)
michael@0 189 // for 'max-width', '-moz-fit-content' is like
michael@0 190 // '-moz-max-content'
michael@0 191 maxWidth.SetIntValue(NS_STYLE_WIDTH_MAX_CONTENT,
michael@0 192 eStyleUnit_Enumerated);
michael@0 193 }
michael@0 194 unit = maxWidth.GetUnit();
michael@0 195 // XXX To really implement 'max-width' well, we'd need to store
michael@0 196 // it separately on the columns.
michael@0 197 if (maxWidth.ConvertsToLength() || unit == eStyleUnit_Enumerated) {
michael@0 198 nscoord w =
michael@0 199 nsLayoutUtils::ComputeWidthValue(aRenderingContext, aFrame,
michael@0 200 0, 0, 0, maxWidth);
michael@0 201 if (w < minCoord)
michael@0 202 minCoord = w;
michael@0 203 if (w < prefCoord)
michael@0 204 prefCoord = w;
michael@0 205 } else if (unit == eStyleUnit_Percent) {
michael@0 206 float p = stylePos->mMaxWidth.GetPercentValue();
michael@0 207 if (p < prefPercent)
michael@0 208 prefPercent = p;
michael@0 209 }
michael@0 210 // treat calc() with percentages on max-width just like 'none'.
michael@0 211
michael@0 212 nsStyleCoord minWidth(stylePos->mMinWidth);
michael@0 213 if (minWidth.GetUnit() == eStyleUnit_Enumerated) {
michael@0 214 if (!aIsCell || minWidth.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE)
michael@0 215 minWidth.SetCoordValue(0);
michael@0 216 else if (minWidth.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT)
michael@0 217 // for 'min-width', '-moz-fit-content' is like
michael@0 218 // '-moz-min-content'
michael@0 219 minWidth.SetIntValue(NS_STYLE_WIDTH_MIN_CONTENT,
michael@0 220 eStyleUnit_Enumerated);
michael@0 221 }
michael@0 222 unit = minWidth.GetUnit();
michael@0 223 if (minWidth.ConvertsToLength() || unit == eStyleUnit_Enumerated) {
michael@0 224 nscoord w =
michael@0 225 nsLayoutUtils::ComputeWidthValue(aRenderingContext, aFrame,
michael@0 226 0, 0, 0, minWidth);
michael@0 227 if (w > minCoord)
michael@0 228 minCoord = w;
michael@0 229 if (w > prefCoord)
michael@0 230 prefCoord = w;
michael@0 231 } else if (unit == eStyleUnit_Percent) {
michael@0 232 float p = stylePos->mMinWidth.GetPercentValue();
michael@0 233 if (p > prefPercent)
michael@0 234 prefPercent = p;
michael@0 235 }
michael@0 236 // treat calc() with percentages on min-width just like '0'.
michael@0 237
michael@0 238 // XXX Should col frame have border/padding considered?
michael@0 239 if (aIsCell) {
michael@0 240 minCoord += boxSizingToBorderEdge;
michael@0 241 prefCoord = NSCoordSaturatingAdd(prefCoord, boxSizingToBorderEdge);
michael@0 242 }
michael@0 243
michael@0 244 return CellWidthInfo(minCoord, prefCoord, prefPercent, hasSpecifiedWidth);
michael@0 245 }
michael@0 246
michael@0 247 static inline CellWidthInfo
michael@0 248 GetCellWidthInfo(nsRenderingContext *aRenderingContext,
michael@0 249 nsTableCellFrame *aCellFrame)
michael@0 250 {
michael@0 251 return GetWidthInfo(aRenderingContext, aCellFrame, true);
michael@0 252 }
michael@0 253
michael@0 254 static inline CellWidthInfo
michael@0 255 GetColWidthInfo(nsRenderingContext *aRenderingContext,
michael@0 256 nsIFrame *aFrame)
michael@0 257 {
michael@0 258 return GetWidthInfo(aRenderingContext, aFrame, false);
michael@0 259 }
michael@0 260
michael@0 261
michael@0 262 /**
michael@0 263 * The algorithm in this function, in addition to meeting the
michael@0 264 * requirements of Web-compatibility, is also invariant under reordering
michael@0 265 * of the rows within a table (something that most, but not all, other
michael@0 266 * browsers are).
michael@0 267 */
michael@0 268 void
michael@0 269 BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsRenderingContext* aRenderingContext)
michael@0 270 {
michael@0 271 nsTableFrame *tableFrame = mTableFrame;
michael@0 272 nsTableCellMap *cellMap = tableFrame->GetCellMap();
michael@0 273
michael@0 274 mozilla::AutoStackArena arena;
michael@0 275 SpanningCellSorter spanningCells;
michael@0 276
michael@0 277 // Loop over the columns to consider the columns and cells *without*
michael@0 278 // a colspan.
michael@0 279 int32_t col, col_end;
michael@0 280 for (col = 0, col_end = cellMap->GetColCount(); col < col_end; ++col) {
michael@0 281 nsTableColFrame *colFrame = tableFrame->GetColFrame(col);
michael@0 282 if (!colFrame) {
michael@0 283 NS_ERROR("column frames out of sync with cell map");
michael@0 284 continue;
michael@0 285 }
michael@0 286 colFrame->ResetIntrinsics();
michael@0 287 colFrame->ResetSpanIntrinsics();
michael@0 288
michael@0 289 // Consider the widths on the column.
michael@0 290 CellWidthInfo colInfo = GetColWidthInfo(aRenderingContext, colFrame);
michael@0 291 colFrame->AddCoords(colInfo.minCoord, colInfo.prefCoord,
michael@0 292 colInfo.hasSpecifiedWidth);
michael@0 293 colFrame->AddPrefPercent(colInfo.prefPercent);
michael@0 294
michael@0 295 // Consider the widths on the column-group. Note that we follow
michael@0 296 // what the HTML spec says here, and make the width apply to
michael@0 297 // each column in the group, not the group as a whole.
michael@0 298
michael@0 299 // If column has width, column-group doesn't override width.
michael@0 300 if (colInfo.minCoord == 0 && colInfo.prefCoord == 0 &&
michael@0 301 colInfo.prefPercent == 0.0f) {
michael@0 302 NS_ASSERTION(colFrame->GetParent()->GetType() ==
michael@0 303 nsGkAtoms::tableColGroupFrame,
michael@0 304 "expected a column-group");
michael@0 305 colInfo = GetColWidthInfo(aRenderingContext, colFrame->GetParent());
michael@0 306 colFrame->AddCoords(colInfo.minCoord, colInfo.prefCoord,
michael@0 307 colInfo.hasSpecifiedWidth);
michael@0 308 colFrame->AddPrefPercent(colInfo.prefPercent);
michael@0 309 }
michael@0 310
michael@0 311 // Consider the contents of and the widths on the cells without
michael@0 312 // colspans.
michael@0 313 nsCellMapColumnIterator columnIter(cellMap, col);
michael@0 314 int32_t row, colSpan;
michael@0 315 nsTableCellFrame* cellFrame;
michael@0 316 while ((cellFrame = columnIter.GetNextFrame(&row, &colSpan))) {
michael@0 317 if (colSpan > 1) {
michael@0 318 spanningCells.AddCell(colSpan, row, col);
michael@0 319 continue;
michael@0 320 }
michael@0 321
michael@0 322 CellWidthInfo info = GetCellWidthInfo(aRenderingContext, cellFrame);
michael@0 323
michael@0 324 colFrame->AddCoords(info.minCoord, info.prefCoord,
michael@0 325 info.hasSpecifiedWidth);
michael@0 326 colFrame->AddPrefPercent(info.prefPercent);
michael@0 327 }
michael@0 328 #ifdef DEBUG_dbaron_off
michael@0 329 printf("table %p col %d nonspan: min=%d pref=%d spec=%d pct=%f\n",
michael@0 330 mTableFrame, col, colFrame->GetMinCoord(),
michael@0 331 colFrame->GetPrefCoord(), colFrame->GetHasSpecifiedCoord(),
michael@0 332 colFrame->GetPrefPercent());
michael@0 333 #endif
michael@0 334 }
michael@0 335 #ifdef DEBUG_TABLE_STRATEGY
michael@0 336 printf("ComputeColumnIntrinsicWidths single\n");
michael@0 337 mTableFrame->Dump(false, true, false);
michael@0 338 #endif
michael@0 339
michael@0 340 // Consider the cells with a colspan that we saved in the loop above
michael@0 341 // into the spanning cell sorter. We consider these cells by seeing
michael@0 342 // if they require adding to the widths resulting only from cells
michael@0 343 // with a smaller colspan, and therefore we must process them sorted
michael@0 344 // in increasing order by colspan. For each colspan group, we
michael@0 345 // accumulate new values to accumulate in the column frame's Span*
michael@0 346 // members.
michael@0 347 //
michael@0 348 // Considering things only relative to the widths resulting from
michael@0 349 // cells with smaller colspans (rather than incrementally including
michael@0 350 // the results from spanning cells, or doing spanning and
michael@0 351 // non-spanning cells in a single pass) means that layout remains
michael@0 352 // row-order-invariant and (except for percentage widths that add to
michael@0 353 // more than 100%) column-order invariant.
michael@0 354 //
michael@0 355 // Starting with smaller colspans makes it more likely that we
michael@0 356 // satisfy all the constraints given and don't distribute space to
michael@0 357 // columns where we don't need it.
michael@0 358 SpanningCellSorter::Item *item;
michael@0 359 int32_t colSpan;
michael@0 360 while ((item = spanningCells.GetNext(&colSpan))) {
michael@0 361 NS_ASSERTION(colSpan > 1,
michael@0 362 "cell should not have been put in spanning cell sorter");
michael@0 363 do {
michael@0 364 int32_t row = item->row;
michael@0 365 col = item->col;
michael@0 366 CellData *cellData = cellMap->GetDataAt(row, col);
michael@0 367 NS_ASSERTION(cellData && cellData->IsOrig(),
michael@0 368 "bogus result from spanning cell sorter");
michael@0 369
michael@0 370 nsTableCellFrame *cellFrame = cellData->GetCellFrame();
michael@0 371 NS_ASSERTION(cellFrame, "bogus result from spanning cell sorter");
michael@0 372
michael@0 373 CellWidthInfo info = GetCellWidthInfo(aRenderingContext, cellFrame);
michael@0 374
michael@0 375 if (info.prefPercent > 0.0f) {
michael@0 376 DistributePctWidthToColumns(info.prefPercent,
michael@0 377 col, colSpan);
michael@0 378 }
michael@0 379 DistributeWidthToColumns(info.minCoord, col, colSpan,
michael@0 380 BTLS_MIN_WIDTH, info.hasSpecifiedWidth);
michael@0 381 DistributeWidthToColumns(info.prefCoord, col, colSpan,
michael@0 382 BTLS_PREF_WIDTH, info.hasSpecifiedWidth);
michael@0 383 } while ((item = item->next));
michael@0 384
michael@0 385 // Combine the results of the span analysis into the main results,
michael@0 386 // for each increment of colspan.
michael@0 387
michael@0 388 for (col = 0, col_end = cellMap->GetColCount(); col < col_end; ++col) {
michael@0 389 nsTableColFrame *colFrame = tableFrame->GetColFrame(col);
michael@0 390 if (!colFrame) {
michael@0 391 NS_ERROR("column frames out of sync with cell map");
michael@0 392 continue;
michael@0 393 }
michael@0 394
michael@0 395 colFrame->AccumulateSpanIntrinsics();
michael@0 396 colFrame->ResetSpanIntrinsics();
michael@0 397
michael@0 398 #ifdef DEBUG_dbaron_off
michael@0 399 printf("table %p col %d span %d: min=%d pref=%d spec=%d pct=%f\n",
michael@0 400 mTableFrame, col, colSpan, colFrame->GetMinCoord(),
michael@0 401 colFrame->GetPrefCoord(), colFrame->GetHasSpecifiedCoord(),
michael@0 402 colFrame->GetPrefPercent());
michael@0 403 #endif
michael@0 404 }
michael@0 405 }
michael@0 406
michael@0 407 // Prevent percentages from adding to more than 100% by (to be
michael@0 408 // compatible with other browsers) treating any percentages that would
michael@0 409 // increase the total percentage to more than 100% as the number that
michael@0 410 // would increase it to only 100% (which is 0% if we've already hit
michael@0 411 // 100%). This means layout depends on the order of columns.
michael@0 412 float pct_used = 0.0f;
michael@0 413 for (col = 0, col_end = cellMap->GetColCount(); col < col_end; ++col) {
michael@0 414 nsTableColFrame *colFrame = tableFrame->GetColFrame(col);
michael@0 415 if (!colFrame) {
michael@0 416 NS_ERROR("column frames out of sync with cell map");
michael@0 417 continue;
michael@0 418 }
michael@0 419
michael@0 420 colFrame->AdjustPrefPercent(&pct_used);
michael@0 421 }
michael@0 422
michael@0 423 #ifdef DEBUG_TABLE_STRATEGY
michael@0 424 printf("ComputeColumnIntrinsicWidths spanning\n");
michael@0 425 mTableFrame->Dump(false, true, false);
michael@0 426 #endif
michael@0 427 }
michael@0 428
michael@0 429 void
michael@0 430 BasicTableLayoutStrategy::ComputeIntrinsicWidths(nsRenderingContext* aRenderingContext)
michael@0 431 {
michael@0 432 ComputeColumnIntrinsicWidths(aRenderingContext);
michael@0 433
michael@0 434 nsTableCellMap *cellMap = mTableFrame->GetCellMap();
michael@0 435 nscoord min = 0, pref = 0, max_small_pct_pref = 0, nonpct_pref_total = 0;
michael@0 436 float pct_total = 0.0f; // always from 0.0f - 1.0f
michael@0 437 int32_t colCount = cellMap->GetColCount();
michael@0 438 nscoord spacing = mTableFrame->GetCellSpacingX();
michael@0 439 nscoord add = spacing; // add (colcount + 1) * spacing for columns
michael@0 440 // where a cell originates
michael@0 441
michael@0 442 for (int32_t col = 0; col < colCount; ++col) {
michael@0 443 nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
michael@0 444 if (!colFrame) {
michael@0 445 NS_ERROR("column frames out of sync with cell map");
michael@0 446 continue;
michael@0 447 }
michael@0 448 if (mTableFrame->ColumnHasCellSpacingBefore(col)) {
michael@0 449 add += spacing;
michael@0 450 }
michael@0 451 min += colFrame->GetMinCoord();
michael@0 452 pref = NSCoordSaturatingAdd(pref, colFrame->GetPrefCoord());
michael@0 453
michael@0 454 // Percentages are of the table, so we have to reverse them for
michael@0 455 // intrinsic widths.
michael@0 456 float p = colFrame->GetPrefPercent();
michael@0 457 if (p > 0.0f) {
michael@0 458 nscoord colPref = colFrame->GetPrefCoord();
michael@0 459 nscoord new_small_pct_expand =
michael@0 460 (colPref == nscoord_MAX ?
michael@0 461 nscoord_MAX : nscoord(float(colPref) / p));
michael@0 462 if (new_small_pct_expand > max_small_pct_pref) {
michael@0 463 max_small_pct_pref = new_small_pct_expand;
michael@0 464 }
michael@0 465 pct_total += p;
michael@0 466 } else {
michael@0 467 nonpct_pref_total = NSCoordSaturatingAdd(nonpct_pref_total,
michael@0 468 colFrame->GetPrefCoord());
michael@0 469 }
michael@0 470 }
michael@0 471
michael@0 472 nscoord pref_pct_expand = pref;
michael@0 473
michael@0 474 // Account for small percentages expanding the preferred width of
michael@0 475 // *other* columns.
michael@0 476 if (max_small_pct_pref > pref_pct_expand) {
michael@0 477 pref_pct_expand = max_small_pct_pref;
michael@0 478 }
michael@0 479
michael@0 480 // Account for large percentages expanding the preferred width of
michael@0 481 // themselves. There's no need to iterate over the columns multiple
michael@0 482 // times, since when there is such a need, the small percentage
michael@0 483 // effect is bigger anyway. (I think!)
michael@0 484 NS_ASSERTION(0.0f <= pct_total && pct_total <= 1.0f,
michael@0 485 "column percentage widths not adjusted down to 100%");
michael@0 486 if (pct_total == 1.0f) {
michael@0 487 if (nonpct_pref_total > 0) {
michael@0 488 pref_pct_expand = nscoord_MAX;
michael@0 489 // XXX Or should I use some smaller value? (Test this using
michael@0 490 // nested tables!)
michael@0 491 }
michael@0 492 } else {
michael@0 493 nscoord large_pct_pref =
michael@0 494 (nonpct_pref_total == nscoord_MAX ?
michael@0 495 nscoord_MAX :
michael@0 496 nscoord(float(nonpct_pref_total) / (1.0f - pct_total)));
michael@0 497 if (large_pct_pref > pref_pct_expand)
michael@0 498 pref_pct_expand = large_pct_pref;
michael@0 499 }
michael@0 500
michael@0 501 // border-spacing isn't part of the basis for percentages
michael@0 502 if (colCount > 0) {
michael@0 503 min += add;
michael@0 504 pref = NSCoordSaturatingAdd(pref, add);
michael@0 505 pref_pct_expand = NSCoordSaturatingAdd(pref_pct_expand, add);
michael@0 506 }
michael@0 507
michael@0 508 mMinWidth = min;
michael@0 509 mPrefWidth = pref;
michael@0 510 mPrefWidthPctExpand = pref_pct_expand;
michael@0 511 }
michael@0 512
michael@0 513 /* virtual */ void
michael@0 514 BasicTableLayoutStrategy::MarkIntrinsicWidthsDirty()
michael@0 515 {
michael@0 516 mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
michael@0 517 mPrefWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
michael@0 518 mPrefWidthPctExpand = NS_INTRINSIC_WIDTH_UNKNOWN;
michael@0 519 mLastCalcWidth = nscoord_MIN;
michael@0 520 }
michael@0 521
michael@0 522 /* virtual */ void
michael@0 523 BasicTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowState)
michael@0 524 {
michael@0 525 nscoord width = aReflowState.ComputedWidth();
michael@0 526
michael@0 527 if (mLastCalcWidth == width)
michael@0 528 return;
michael@0 529 mLastCalcWidth = width;
michael@0 530
michael@0 531 NS_ASSERTION((mMinWidth == NS_INTRINSIC_WIDTH_UNKNOWN) ==
michael@0 532 (mPrefWidth == NS_INTRINSIC_WIDTH_UNKNOWN),
michael@0 533 "dirtyness out of sync");
michael@0 534 NS_ASSERTION((mMinWidth == NS_INTRINSIC_WIDTH_UNKNOWN) ==
michael@0 535 (mPrefWidthPctExpand == NS_INTRINSIC_WIDTH_UNKNOWN),
michael@0 536 "dirtyness out of sync");
michael@0 537 // XXX Is this needed?
michael@0 538 if (mMinWidth == NS_INTRINSIC_WIDTH_UNKNOWN)
michael@0 539 ComputeIntrinsicWidths(aReflowState.rendContext);
michael@0 540
michael@0 541 nsTableCellMap *cellMap = mTableFrame->GetCellMap();
michael@0 542 int32_t colCount = cellMap->GetColCount();
michael@0 543 if (colCount <= 0)
michael@0 544 return; // nothing to do
michael@0 545
michael@0 546 DistributeWidthToColumns(width, 0, colCount, BTLS_FINAL_WIDTH, false);
michael@0 547
michael@0 548 #ifdef DEBUG_TABLE_STRATEGY
michael@0 549 printf("ComputeColumnWidths final\n");
michael@0 550 mTableFrame->Dump(false, true, false);
michael@0 551 #endif
michael@0 552 }
michael@0 553
michael@0 554 void
michael@0 555 BasicTableLayoutStrategy::DistributePctWidthToColumns(float aSpanPrefPct,
michael@0 556 int32_t aFirstCol,
michael@0 557 int32_t aColCount)
michael@0 558 {
michael@0 559 // First loop to determine:
michael@0 560 int32_t nonPctColCount = 0; // number of spanned columns without % width
michael@0 561 nscoord nonPctTotalPrefWidth = 0; // total pref width of those columns
michael@0 562 // and to reduce aSpanPrefPct by columns that already have % width
michael@0 563
michael@0 564 int32_t scol, scol_end;
michael@0 565 nsTableCellMap *cellMap = mTableFrame->GetCellMap();
michael@0 566 for (scol = aFirstCol, scol_end = aFirstCol + aColCount;
michael@0 567 scol < scol_end; ++scol) {
michael@0 568 nsTableColFrame *scolFrame = mTableFrame->GetColFrame(scol);
michael@0 569 if (!scolFrame) {
michael@0 570 NS_ERROR("column frames out of sync with cell map");
michael@0 571 continue;
michael@0 572 }
michael@0 573 float scolPct = scolFrame->GetPrefPercent();
michael@0 574 if (scolPct == 0.0f) {
michael@0 575 nonPctTotalPrefWidth += scolFrame->GetPrefCoord();
michael@0 576 if (cellMap->GetNumCellsOriginatingInCol(scol) > 0) {
michael@0 577 ++nonPctColCount;
michael@0 578 }
michael@0 579 } else {
michael@0 580 aSpanPrefPct -= scolPct;
michael@0 581 }
michael@0 582 }
michael@0 583
michael@0 584 if (aSpanPrefPct <= 0.0f || nonPctColCount == 0) {
michael@0 585 // There's no %-width on the colspan left over to distribute,
michael@0 586 // or there are no columns to which we could distribute %-width
michael@0 587 return;
michael@0 588 }
michael@0 589
michael@0 590 // Second loop, to distribute what remains of aSpanPrefPct
michael@0 591 // between the non-percent-width spanned columns
michael@0 592 const bool spanHasNonPctPref = nonPctTotalPrefWidth > 0; // Loop invariant
michael@0 593 for (scol = aFirstCol, scol_end = aFirstCol + aColCount;
michael@0 594 scol < scol_end; ++scol) {
michael@0 595 nsTableColFrame *scolFrame = mTableFrame->GetColFrame(scol);
michael@0 596 if (!scolFrame) {
michael@0 597 NS_ERROR("column frames out of sync with cell map");
michael@0 598 continue;
michael@0 599 }
michael@0 600
michael@0 601 if (scolFrame->GetPrefPercent() == 0.0f) {
michael@0 602 NS_ASSERTION((!spanHasNonPctPref ||
michael@0 603 nonPctTotalPrefWidth != 0) &&
michael@0 604 nonPctColCount != 0,
michael@0 605 "should not be zero if we haven't allocated "
michael@0 606 "all pref percent");
michael@0 607
michael@0 608 float allocatedPct; // % width to be given to this column
michael@0 609 if (spanHasNonPctPref) {
michael@0 610 // Group so we're multiplying by 1.0f when we need
michael@0 611 // to use up aSpanPrefPct.
michael@0 612 allocatedPct = aSpanPrefPct *
michael@0 613 (float(scolFrame->GetPrefCoord()) /
michael@0 614 float(nonPctTotalPrefWidth));
michael@0 615 } else if (cellMap->GetNumCellsOriginatingInCol(scol) > 0) {
michael@0 616 // distribute equally when all pref widths are 0
michael@0 617 allocatedPct = aSpanPrefPct / float(nonPctColCount);
michael@0 618 } else {
michael@0 619 allocatedPct = 0.0f;
michael@0 620 }
michael@0 621 // Allocate the percent
michael@0 622 scolFrame->AddSpanPrefPercent(allocatedPct);
michael@0 623
michael@0 624 // To avoid accumulating rounding error from division,
michael@0 625 // subtract this column's values from the totals.
michael@0 626 aSpanPrefPct -= allocatedPct;
michael@0 627 nonPctTotalPrefWidth -= scolFrame->GetPrefCoord();
michael@0 628 if (cellMap->GetNumCellsOriginatingInCol(scol) > 0) {
michael@0 629 --nonPctColCount;
michael@0 630 }
michael@0 631
michael@0 632 if (!aSpanPrefPct) {
michael@0 633 // No more span-percent-width to distribute --> we're done.
michael@0 634 NS_ASSERTION(spanHasNonPctPref ?
michael@0 635 nonPctTotalPrefWidth == 0 :
michael@0 636 nonPctColCount == 0,
michael@0 637 "No more pct width to distribute, but there are "
michael@0 638 "still cols that need some.");
michael@0 639 return;
michael@0 640 }
michael@0 641 }
michael@0 642 }
michael@0 643 }
michael@0 644
michael@0 645 void
michael@0 646 BasicTableLayoutStrategy::DistributeWidthToColumns(nscoord aWidth,
michael@0 647 int32_t aFirstCol,
michael@0 648 int32_t aColCount,
michael@0 649 BtlsWidthType aWidthType,
michael@0 650 bool aSpanHasSpecifiedWidth)
michael@0 651 {
michael@0 652 NS_ASSERTION(aWidthType != BTLS_FINAL_WIDTH ||
michael@0 653 (aFirstCol == 0 &&
michael@0 654 aColCount == mTableFrame->GetCellMap()->GetColCount()),
michael@0 655 "Computing final column widths, but didn't get full column range");
michael@0 656
michael@0 657 // border-spacing isn't part of the basis for percentages.
michael@0 658 nscoord spacing = mTableFrame->GetCellSpacingX();
michael@0 659 nscoord subtract = 0;
michael@0 660 // aWidth initially includes border-spacing for the boundaries in between
michael@0 661 // each of the columns. We start at aFirstCol + 1 because the first
michael@0 662 // in-between boundary would be at the left edge of column aFirstCol + 1
michael@0 663 for (int32_t col = aFirstCol + 1; col < aFirstCol + aColCount; ++col) {
michael@0 664 if (mTableFrame->ColumnHasCellSpacingBefore(col)) {
michael@0 665 subtract += spacing;
michael@0 666 }
michael@0 667 }
michael@0 668 if (aWidthType == BTLS_FINAL_WIDTH) {
michael@0 669 // If we're computing final col-width, then aWidth initially includes
michael@0 670 // border spacing on the table's far left + far right edge, too. Need
michael@0 671 // to subtract those out, too.
michael@0 672 subtract += spacing * 2;
michael@0 673 }
michael@0 674 aWidth = NSCoordSaturatingSubtract(aWidth, subtract, nscoord_MAX);
michael@0 675
michael@0 676 /*
michael@0 677 * The goal of this function is to distribute |aWidth| between the
michael@0 678 * columns by making an appropriate AddSpanCoords or SetFinalWidth
michael@0 679 * call for each column. (We call AddSpanCoords if we're
michael@0 680 * distributing a column-spanning cell's minimum or preferred width
michael@0 681 * to its spanned columns. We call SetFinalWidth if we're
michael@0 682 * distributing a table's final width to its columns.)
michael@0 683 *
michael@0 684 * The idea is to either assign one of the following sets of widths
michael@0 685 * or a weighted average of two adjacent sets of widths. It is not
michael@0 686 * possible to assign values smaller than the smallest set of
michael@0 687 * widths. However, see below for handling the case of assigning
michael@0 688 * values larger than the largest set of widths. From smallest to
michael@0 689 * largest, these are:
michael@0 690 *
michael@0 691 * 1. [guess_min] Assign all columns their min width.
michael@0 692 *
michael@0 693 * 2. [guess_min_pct] Assign all columns with percentage widths
michael@0 694 * their percentage width, and all other columns their min width.
michael@0 695 *
michael@0 696 * 3. [guess_min_spec] Assign all columns with percentage widths
michael@0 697 * their percentage width, all columns with specified coordinate
michael@0 698 * widths their pref width (since it doesn't matter whether it's the
michael@0 699 * largest contributor to the pref width that was the specified
michael@0 700 * contributor), and all other columns their min width.
michael@0 701 *
michael@0 702 * 4. [guess_pref] Assign all columns with percentage widths their
michael@0 703 * specified width, and all other columns their pref width.
michael@0 704 *
michael@0 705 * If |aWidth| is *larger* than what we would assign in (4), then we
michael@0 706 * expand the columns:
michael@0 707 *
michael@0 708 * a. if any columns without a specified coordinate width or
michael@0 709 * percent width have nonzero pref width, in proportion to pref
michael@0 710 * width [total_flex_pref]
michael@0 711 *
michael@0 712 * b. otherwise, if any columns without a specified coordinate
michael@0 713 * width or percent width, but with cells originating in them,
michael@0 714 * have zero pref width, equally between these
michael@0 715 * [numNonSpecZeroWidthCols]
michael@0 716 *
michael@0 717 * c. otherwise, if any columns without percent width have nonzero
michael@0 718 * pref width, in proportion to pref width [total_fixed_pref]
michael@0 719 *
michael@0 720 * d. otherwise, if any columns have nonzero percentage widths, in
michael@0 721 * proportion to the percentage widths [total_pct]
michael@0 722 *
michael@0 723 * e. otherwise, equally.
michael@0 724 */
michael@0 725
michael@0 726 // Loop #1 over the columns, to figure out the four values above so
michael@0 727 // we know which case we're dealing with.
michael@0 728
michael@0 729 nscoord guess_min = 0,
michael@0 730 guess_min_pct = 0,
michael@0 731 guess_min_spec = 0,
michael@0 732 guess_pref = 0,
michael@0 733 total_flex_pref = 0,
michael@0 734 total_fixed_pref = 0;
michael@0 735 float total_pct = 0.0f; // 0.0f to 1.0f
michael@0 736 int32_t numInfiniteWidthCols = 0;
michael@0 737 int32_t numNonSpecZeroWidthCols = 0;
michael@0 738
michael@0 739 int32_t col;
michael@0 740 nsTableCellMap *cellMap = mTableFrame->GetCellMap();
michael@0 741 for (col = aFirstCol; col < aFirstCol + aColCount; ++col) {
michael@0 742 nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
michael@0 743 if (!colFrame) {
michael@0 744 NS_ERROR("column frames out of sync with cell map");
michael@0 745 continue;
michael@0 746 }
michael@0 747 nscoord min_width = colFrame->GetMinCoord();
michael@0 748 guess_min += min_width;
michael@0 749 if (colFrame->GetPrefPercent() != 0.0f) {
michael@0 750 float pct = colFrame->GetPrefPercent();
michael@0 751 total_pct += pct;
michael@0 752 nscoord val = nscoord(float(aWidth) * pct);
michael@0 753 if (val < min_width)
michael@0 754 val = min_width;
michael@0 755 guess_min_pct += val;
michael@0 756 guess_pref = NSCoordSaturatingAdd(guess_pref, val);
michael@0 757 } else {
michael@0 758 nscoord pref_width = colFrame->GetPrefCoord();
michael@0 759 if (pref_width == nscoord_MAX) {
michael@0 760 ++numInfiniteWidthCols;
michael@0 761 }
michael@0 762 guess_pref = NSCoordSaturatingAdd(guess_pref, pref_width);
michael@0 763 guess_min_pct += min_width;
michael@0 764 if (colFrame->GetHasSpecifiedCoord()) {
michael@0 765 // we'll add on the rest of guess_min_spec outside the
michael@0 766 // loop
michael@0 767 nscoord delta = NSCoordSaturatingSubtract(pref_width,
michael@0 768 min_width, 0);
michael@0 769 guess_min_spec = NSCoordSaturatingAdd(guess_min_spec, delta);
michael@0 770 total_fixed_pref = NSCoordSaturatingAdd(total_fixed_pref,
michael@0 771 pref_width);
michael@0 772 } else if (pref_width == 0) {
michael@0 773 if (cellMap->GetNumCellsOriginatingInCol(col) > 0) {
michael@0 774 ++numNonSpecZeroWidthCols;
michael@0 775 }
michael@0 776 } else {
michael@0 777 total_flex_pref = NSCoordSaturatingAdd(total_flex_pref,
michael@0 778 pref_width);
michael@0 779 }
michael@0 780 }
michael@0 781 }
michael@0 782 guess_min_spec = NSCoordSaturatingAdd(guess_min_spec, guess_min_pct);
michael@0 783
michael@0 784 // Determine what we're flexing:
michael@0 785 enum Loop2Type {
michael@0 786 FLEX_PCT_SMALL, // between (1) and (2) above
michael@0 787 FLEX_FIXED_SMALL, // between (2) and (3) above
michael@0 788 FLEX_FLEX_SMALL, // between (3) and (4) above
michael@0 789 FLEX_FLEX_LARGE, // greater than (4) above, case (a)
michael@0 790 FLEX_FLEX_LARGE_ZERO, // greater than (4) above, case (b)
michael@0 791 FLEX_FIXED_LARGE, // greater than (4) above, case (c)
michael@0 792 FLEX_PCT_LARGE, // greater than (4) above, case (d)
michael@0 793 FLEX_ALL_LARGE // greater than (4) above, case (e)
michael@0 794 };
michael@0 795
michael@0 796 Loop2Type l2t;
michael@0 797 // These are constants (over columns) for each case's math. We use
michael@0 798 // a pair of nscoords rather than a float so that we can subtract
michael@0 799 // each column's allocation so we avoid accumulating rounding error.
michael@0 800 nscoord space; // the amount of extra width to allocate
michael@0 801 union {
michael@0 802 nscoord c;
michael@0 803 float f;
michael@0 804 } basis; // the sum of the statistic over columns to divide it
michael@0 805 if (aWidth < guess_pref) {
michael@0 806 if (aWidthType != BTLS_FINAL_WIDTH && aWidth <= guess_min) {
michael@0 807 // Return early -- we don't have any extra space to distribute.
michael@0 808 return;
michael@0 809 }
michael@0 810 NS_ASSERTION(!(aWidthType == BTLS_FINAL_WIDTH && aWidth < guess_min),
michael@0 811 "Table width is less than the "
michael@0 812 "sum of its columns' min widths");
michael@0 813 if (aWidth < guess_min_pct) {
michael@0 814 l2t = FLEX_PCT_SMALL;
michael@0 815 space = aWidth - guess_min;
michael@0 816 basis.c = guess_min_pct - guess_min;
michael@0 817 } else if (aWidth < guess_min_spec) {
michael@0 818 l2t = FLEX_FIXED_SMALL;
michael@0 819 space = aWidth - guess_min_pct;
michael@0 820 basis.c = NSCoordSaturatingSubtract(guess_min_spec, guess_min_pct,
michael@0 821 nscoord_MAX);
michael@0 822 } else {
michael@0 823 l2t = FLEX_FLEX_SMALL;
michael@0 824 space = aWidth - guess_min_spec;
michael@0 825 basis.c = NSCoordSaturatingSubtract(guess_pref, guess_min_spec,
michael@0 826 nscoord_MAX);
michael@0 827 }
michael@0 828 } else {
michael@0 829 space = NSCoordSaturatingSubtract(aWidth, guess_pref, nscoord_MAX);
michael@0 830 if (total_flex_pref > 0) {
michael@0 831 l2t = FLEX_FLEX_LARGE;
michael@0 832 basis.c = total_flex_pref;
michael@0 833 } else if (numNonSpecZeroWidthCols > 0) {
michael@0 834 l2t = FLEX_FLEX_LARGE_ZERO;
michael@0 835 basis.c = numNonSpecZeroWidthCols;
michael@0 836 } else if (total_fixed_pref > 0) {
michael@0 837 l2t = FLEX_FIXED_LARGE;
michael@0 838 basis.c = total_fixed_pref;
michael@0 839 } else if (total_pct > 0.0f) {
michael@0 840 l2t = FLEX_PCT_LARGE;
michael@0 841 basis.f = total_pct;
michael@0 842 } else {
michael@0 843 l2t = FLEX_ALL_LARGE;
michael@0 844 basis.c = aColCount;
michael@0 845 }
michael@0 846 }
michael@0 847
michael@0 848 #ifdef DEBUG_dbaron_off
michael@0 849 printf("ComputeColumnWidths: %d columns in width %d,\n"
michael@0 850 " guesses=[%d,%d,%d,%d], totals=[%d,%d,%f],\n"
michael@0 851 " l2t=%d, space=%d, basis.c=%d\n",
michael@0 852 aColCount, aWidth,
michael@0 853 guess_min, guess_min_pct, guess_min_spec, guess_pref,
michael@0 854 total_flex_pref, total_fixed_pref, total_pct,
michael@0 855 l2t, space, basis.c);
michael@0 856 #endif
michael@0 857
michael@0 858 for (col = aFirstCol; col < aFirstCol + aColCount; ++col) {
michael@0 859 nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
michael@0 860 if (!colFrame) {
michael@0 861 NS_ERROR("column frames out of sync with cell map");
michael@0 862 continue;
michael@0 863 }
michael@0 864 nscoord col_width;
michael@0 865
michael@0 866 float pct = colFrame->GetPrefPercent();
michael@0 867 if (pct != 0.0f) {
michael@0 868 col_width = nscoord(float(aWidth) * pct);
michael@0 869 nscoord col_min = colFrame->GetMinCoord();
michael@0 870 if (col_width < col_min)
michael@0 871 col_width = col_min;
michael@0 872 } else {
michael@0 873 col_width = colFrame->GetPrefCoord();
michael@0 874 }
michael@0 875
michael@0 876 nscoord col_width_before_adjust = col_width;
michael@0 877
michael@0 878 switch (l2t) {
michael@0 879 case FLEX_PCT_SMALL:
michael@0 880 col_width = col_width_before_adjust = colFrame->GetMinCoord();
michael@0 881 if (pct != 0.0f) {
michael@0 882 nscoord pct_minus_min =
michael@0 883 nscoord(float(aWidth) * pct) - col_width;
michael@0 884 if (pct_minus_min > 0) {
michael@0 885 float c = float(space) / float(basis.c);
michael@0 886 basis.c -= pct_minus_min;
michael@0 887 col_width += NSToCoordRound(float(pct_minus_min) * c);
michael@0 888 }
michael@0 889 }
michael@0 890 break;
michael@0 891 case FLEX_FIXED_SMALL:
michael@0 892 if (pct == 0.0f) {
michael@0 893 NS_ASSERTION(col_width == colFrame->GetPrefCoord(),
michael@0 894 "wrong width assigned");
michael@0 895 if (colFrame->GetHasSpecifiedCoord()) {
michael@0 896 nscoord col_min = colFrame->GetMinCoord();
michael@0 897 nscoord pref_minus_min = col_width - col_min;
michael@0 898 col_width = col_width_before_adjust = col_min;
michael@0 899 if (pref_minus_min != 0) {
michael@0 900 float c = float(space) / float(basis.c);
michael@0 901 basis.c -= pref_minus_min;
michael@0 902 col_width += NSToCoordRound(
michael@0 903 float(pref_minus_min) * c);
michael@0 904 }
michael@0 905 } else
michael@0 906 col_width = col_width_before_adjust =
michael@0 907 colFrame->GetMinCoord();
michael@0 908 }
michael@0 909 break;
michael@0 910 case FLEX_FLEX_SMALL:
michael@0 911 if (pct == 0.0f &&
michael@0 912 !colFrame->GetHasSpecifiedCoord()) {
michael@0 913 NS_ASSERTION(col_width == colFrame->GetPrefCoord(),
michael@0 914 "wrong width assigned");
michael@0 915 nscoord col_min = colFrame->GetMinCoord();
michael@0 916 nscoord pref_minus_min =
michael@0 917 NSCoordSaturatingSubtract(col_width, col_min, 0);
michael@0 918 col_width = col_width_before_adjust = col_min;
michael@0 919 if (pref_minus_min != 0) {
michael@0 920 float c = float(space) / float(basis.c);
michael@0 921 // If we have infinite-width cols, then the standard
michael@0 922 // adjustment to col_width using 'c' won't work,
michael@0 923 // because basis.c and pref_minus_min are both
michael@0 924 // nscoord_MAX and will cancel each other out in the
michael@0 925 // col_width adjustment (making us assign all the
michael@0 926 // space to the first inf-width col). To correct for
michael@0 927 // this, we'll also divide by numInfiniteWidthCols to
michael@0 928 // spread the space equally among the inf-width cols.
michael@0 929 if (numInfiniteWidthCols) {
michael@0 930 if (colFrame->GetPrefCoord() == nscoord_MAX) {
michael@0 931 c = c / float(numInfiniteWidthCols);
michael@0 932 --numInfiniteWidthCols;
michael@0 933 } else {
michael@0 934 c = 0.0f;
michael@0 935 }
michael@0 936 }
michael@0 937 basis.c = NSCoordSaturatingSubtract(basis.c,
michael@0 938 pref_minus_min,
michael@0 939 nscoord_MAX);
michael@0 940 col_width += NSToCoordRound(
michael@0 941 float(pref_minus_min) * c);
michael@0 942 }
michael@0 943 }
michael@0 944 break;
michael@0 945 case FLEX_FLEX_LARGE:
michael@0 946 if (pct == 0.0f &&
michael@0 947 !colFrame->GetHasSpecifiedCoord()) {
michael@0 948 NS_ASSERTION(col_width == colFrame->GetPrefCoord(),
michael@0 949 "wrong width assigned");
michael@0 950 if (col_width != 0) {
michael@0 951 if (space == nscoord_MAX) {
michael@0 952 basis.c -= col_width;
michael@0 953 col_width = nscoord_MAX;
michael@0 954 } else {
michael@0 955 float c = float(space) / float(basis.c);
michael@0 956 basis.c -= col_width;
michael@0 957 col_width += NSToCoordRound(float(col_width) * c);
michael@0 958 }
michael@0 959 }
michael@0 960 }
michael@0 961 break;
michael@0 962 case FLEX_FLEX_LARGE_ZERO:
michael@0 963 if (pct == 0.0f &&
michael@0 964 !colFrame->GetHasSpecifiedCoord() &&
michael@0 965 cellMap->GetNumCellsOriginatingInCol(col) > 0) {
michael@0 966
michael@0 967 NS_ASSERTION(col_width == 0 &&
michael@0 968 colFrame->GetPrefCoord() == 0,
michael@0 969 "Since we're in FLEX_FLEX_LARGE_ZERO case, "
michael@0 970 "all auto-width cols should have zero pref "
michael@0 971 "width.");
michael@0 972 float c = float(space) / float(basis.c);
michael@0 973 col_width += NSToCoordRound(c);
michael@0 974 --basis.c;
michael@0 975 }
michael@0 976 break;
michael@0 977 case FLEX_FIXED_LARGE:
michael@0 978 if (pct == 0.0f) {
michael@0 979 NS_ASSERTION(col_width == colFrame->GetPrefCoord(),
michael@0 980 "wrong width assigned");
michael@0 981 NS_ASSERTION(colFrame->GetHasSpecifiedCoord() ||
michael@0 982 colFrame->GetPrefCoord() == 0,
michael@0 983 "wrong case");
michael@0 984 if (col_width != 0) {
michael@0 985 float c = float(space) / float(basis.c);
michael@0 986 basis.c -= col_width;
michael@0 987 col_width += NSToCoordRound(float(col_width) * c);
michael@0 988 }
michael@0 989 }
michael@0 990 break;
michael@0 991 case FLEX_PCT_LARGE:
michael@0 992 NS_ASSERTION(pct != 0.0f || colFrame->GetPrefCoord() == 0,
michael@0 993 "wrong case");
michael@0 994 if (pct != 0.0f) {
michael@0 995 float c = float(space) / basis.f;
michael@0 996 col_width += NSToCoordRound(pct * c);
michael@0 997 basis.f -= pct;
michael@0 998 }
michael@0 999 break;
michael@0 1000 case FLEX_ALL_LARGE:
michael@0 1001 {
michael@0 1002 float c = float(space) / float(basis.c);
michael@0 1003 col_width += NSToCoordRound(c);
michael@0 1004 --basis.c;
michael@0 1005 }
michael@0 1006 break;
michael@0 1007 }
michael@0 1008
michael@0 1009 // Only subtract from space if it's a real number.
michael@0 1010 if (space != nscoord_MAX) {
michael@0 1011 NS_ASSERTION(col_width != nscoord_MAX,
michael@0 1012 "How is col_width nscoord_MAX if space isn't?");
michael@0 1013 NS_ASSERTION(col_width_before_adjust != nscoord_MAX,
michael@0 1014 "How is col_width_before_adjust nscoord_MAX if space isn't?");
michael@0 1015 space -= col_width - col_width_before_adjust;
michael@0 1016 }
michael@0 1017
michael@0 1018 NS_ASSERTION(col_width >= colFrame->GetMinCoord(),
michael@0 1019 "assigned width smaller than min");
michael@0 1020
michael@0 1021 // Apply the new width
michael@0 1022 switch (aWidthType) {
michael@0 1023 case BTLS_MIN_WIDTH:
michael@0 1024 {
michael@0 1025 // Note: AddSpanCoords requires both a min and pref width.
michael@0 1026 // For the pref width, we'll just pass in our computed
michael@0 1027 // min width, because the real pref width will be at least
michael@0 1028 // as big
michael@0 1029 colFrame->AddSpanCoords(col_width, col_width,
michael@0 1030 aSpanHasSpecifiedWidth);
michael@0 1031 }
michael@0 1032 break;
michael@0 1033 case BTLS_PREF_WIDTH:
michael@0 1034 {
michael@0 1035 // Note: AddSpanCoords requires both a min and pref width.
michael@0 1036 // For the min width, we'll just pass in 0, because
michael@0 1037 // the real min width will be at least 0
michael@0 1038 colFrame->AddSpanCoords(0, col_width,
michael@0 1039 aSpanHasSpecifiedWidth);
michael@0 1040 }
michael@0 1041 break;
michael@0 1042 case BTLS_FINAL_WIDTH:
michael@0 1043 {
michael@0 1044 nscoord old_final = colFrame->GetFinalWidth();
michael@0 1045 colFrame->SetFinalWidth(col_width);
michael@0 1046
michael@0 1047 if (old_final != col_width)
michael@0 1048 mTableFrame->DidResizeColumns();
michael@0 1049 }
michael@0 1050 break;
michael@0 1051 }
michael@0 1052 }
michael@0 1053 NS_ASSERTION((space == 0 || space == nscoord_MAX) &&
michael@0 1054 ((l2t == FLEX_PCT_LARGE)
michael@0 1055 ? (-0.001f < basis.f && basis.f < 0.001f)
michael@0 1056 : (basis.c == 0 || basis.c == nscoord_MAX)),
michael@0 1057 "didn't subtract all that we added");
michael@0 1058 }

mercurial