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