Bug 454376 imgLoader.cpp does not compile with Sun Studio 12 on Solaris r=joedraw...
[wine-gecko.git] / layout / tables / FixedTableLayoutStrategy.cpp
blob53e8338bd18954382f393a8f941f586b8101604d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 // vim:cindent:ts=4:et:sw=4:
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is Mozilla's table layout code.
18 * The Initial Developer of the Original Code is the Mozilla Foundation.
19 * Portions created by the Initial Developer are Copyright (C) 2006
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * L. David Baron <dbaron@dbaron.org> (original author)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 * Algorithms that determine column and table widths used for CSS2's
41 * 'table-layout: fixed'.
44 #include "FixedTableLayoutStrategy.h"
45 #include "nsTableFrame.h"
46 #include "nsTableColFrame.h"
47 #include "nsTableCellFrame.h"
49 FixedTableLayoutStrategy::FixedTableLayoutStrategy(nsTableFrame *aTableFrame)
50 : mTableFrame(aTableFrame)
52 MarkIntrinsicWidthsDirty();
55 /* virtual */
56 FixedTableLayoutStrategy::~FixedTableLayoutStrategy()
60 /* virtual */ nscoord
61 FixedTableLayoutStrategy::GetMinWidth(nsIRenderingContext* aRenderingContext)
63 DISPLAY_MIN_WIDTH(mTableFrame, mMinWidth);
64 if (mMinWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
65 return mMinWidth;
67 // It's theoretically possible to do something much better here that
68 // depends only on the columns and the first row, but it wouldn't be
69 // compatible with other browsers, or with the use of GetMinWidth by
70 // nsHTMLReflowState to determine the width of a fixed-layout table,
71 // since CSS2.1 says:
72 // The width of the table is then the greater of the value of the
73 // 'width' property for the table element and the sum of the
74 // column widths (plus cell spacing or borders).
76 // XXX Should we really ignore 'min-width' and 'max-width'?
78 nsTableCellMap *cellMap = mTableFrame->GetCellMap();
79 PRInt32 colCount = cellMap->GetColCount();
80 nscoord spacing = mTableFrame->GetCellSpacingX();
82 // XXX Should this code do any pixel rounding?
84 nscoord result = 0;
86 // XXX Consider widths on columns or column groups?
88 if (colCount > 0) {
89 // XXX Should only add columns that have cells originating in them!
90 result += spacing * (colCount + 1);
93 for (PRInt32 col = 0; col < colCount; ++col) {
94 nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
95 if (!colFrame) {
96 NS_ERROR("column frames out of sync with cell map");
97 continue;
99 const nsStyleCoord *styleWidth =
100 &colFrame->GetStylePosition()->mWidth;
101 if (styleWidth->GetUnit() == eStyleUnit_Coord) {
102 result += nsLayoutUtils::ComputeWidthValue(aRenderingContext,
103 colFrame, 0, 0, 0, *styleWidth);
104 } else if (styleWidth->GetUnit() == eStyleUnit_Percent) {
105 // do nothing
106 } else {
107 NS_ASSERTION(styleWidth->GetUnit() == eStyleUnit_Auto ||
108 styleWidth->GetUnit() == eStyleUnit_Enumerated,
109 "bad width");
111 // The 'table-layout: fixed' algorithm considers only cells
112 // in the first row.
113 PRBool originates;
114 PRInt32 colSpan;
115 nsTableCellFrame *cellFrame =
116 cellMap->GetCellInfoAt(0, col, &originates, &colSpan);
117 if (cellFrame) {
118 styleWidth = &cellFrame->GetStylePosition()->mWidth;
119 if (styleWidth->GetUnit() == eStyleUnit_Coord ||
120 (styleWidth->GetUnit() == eStyleUnit_Enumerated &&
121 (styleWidth->GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT ||
122 styleWidth->GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT))) {
123 nscoord cellWidth = nsLayoutUtils::IntrinsicForContainer(
124 aRenderingContext, cellFrame, nsLayoutUtils::MIN_WIDTH);
125 if (colSpan > 1) {
126 // If a column-spanning cell is in the first
127 // row, split up the space evenly. (XXX This
128 // isn't quite right if some of the columns it's
129 // in have specified widths. Should we care?)
130 cellWidth = ((cellWidth + spacing) / colSpan) - spacing;
132 result += cellWidth;
133 } else if (styleWidth->GetUnit() == eStyleUnit_Percent) {
134 if (colSpan > 1) {
135 // XXX Can this force columns to negative
136 // widths?
137 result -= spacing * (colSpan - 1);
140 // else, for 'auto', '-moz-available', and '-moz-fit-content'
141 // do nothing
146 return (mMinWidth = result);
149 /* virtual */ nscoord
150 FixedTableLayoutStrategy::GetPrefWidth(nsIRenderingContext* aRenderingContext,
151 PRBool aComputingSize)
153 // It's theoretically possible to do something much better here that
154 // depends only on the columns and the first row, but it wouldn't be
155 // compatible with other browsers.
156 nscoord result = nscoord_MAX;
157 DISPLAY_PREF_WIDTH(mTableFrame, result);
158 return result;
161 /* virtual */ void
162 FixedTableLayoutStrategy::MarkIntrinsicWidthsDirty()
164 mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
165 mLastCalcWidth = nscoord_MIN;
168 /* virtual */ void
169 FixedTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState& aReflowState)
171 nscoord tableWidth = aReflowState.ComputedWidth();
173 if (mLastCalcWidth == tableWidth)
174 return;
175 mLastCalcWidth = tableWidth;
177 nsTableCellMap *cellMap = mTableFrame->GetCellMap();
178 PRInt32 colCount = cellMap->GetColCount();
179 nscoord spacing = mTableFrame->GetCellSpacingX();
181 // XXX Should this code do any pixel rounding?
183 // border-spacing isn't part of the basis for percentages.
184 if (colCount > 0) {
185 // XXX Should only add columns that have cells originating in them!
186 nscoord subtract = spacing * (colCount + 1);
187 tableWidth -= subtract;
188 } else {
189 // No Columns - nothing to compute
190 return;
193 // XXX This ignores the 'min-width' and 'max-width' properties
194 // throughout. Then again, that's what the CSS spec says to do.
196 // XXX Consider widths on columns or column groups?
198 PRUint32 unassignedCount = 0;
199 nscoord unassignedSpace = tableWidth;
200 const nscoord unassignedMarker = nscoord_MIN;
202 // We use the PrefPercent on the columns to store the percentages
203 // used to compute column widths in case we need to reduce their
204 // basis.
205 float pctTotal = 0.0f;
207 for (PRInt32 col = 0; col < colCount; ++col) {
208 nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
209 if (!colFrame) {
210 NS_ERROR("column frames out of sync with cell map");
211 continue;
213 colFrame->ResetPrefPercent();
214 const nsStyleCoord *styleWidth =
215 &colFrame->GetStylePosition()->mWidth;
216 nscoord colWidth;
217 if (styleWidth->GetUnit() == eStyleUnit_Coord) {
218 colWidth = nsLayoutUtils::ComputeWidthValue(
219 aReflowState.rendContext,
220 colFrame, 0, 0, 0, *styleWidth);
221 } else if (styleWidth->GetUnit() == eStyleUnit_Percent) {
222 float pct = styleWidth->GetPercentValue();
223 colWidth = NSToCoordFloor(pct * float(tableWidth));
224 colFrame->AddPrefPercent(pct);
225 pctTotal += pct;
226 } else {
227 NS_ASSERTION(styleWidth->GetUnit() == eStyleUnit_Auto ||
228 styleWidth->GetUnit() == eStyleUnit_Enumerated,
229 "bad width");
231 // The 'table-layout: fixed' algorithm considers only cells
232 // in the first row.
233 PRBool originates;
234 PRInt32 colSpan;
235 nsTableCellFrame *cellFrame =
236 cellMap->GetCellInfoAt(0, col, &originates, &colSpan);
237 if (cellFrame) {
238 styleWidth = &cellFrame->GetStylePosition()->mWidth;
239 if (styleWidth->GetUnit() == eStyleUnit_Coord ||
240 (styleWidth->GetUnit() == eStyleUnit_Enumerated &&
241 (styleWidth->GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT ||
242 styleWidth->GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT))) {
243 // XXX This should use real percentage padding
244 // Note that the difference between MIN_WIDTH and
245 // PREF_WIDTH shouldn't matter for any of these
246 // values of styleWidth; use MIN_WIDTH for symmetry
247 // with GetMinWidth above, just in case there is a
248 // difference.
249 colWidth = nsLayoutUtils::IntrinsicForContainer(
250 aReflowState.rendContext,
251 cellFrame, nsLayoutUtils::MIN_WIDTH);
252 } else if (styleWidth->GetUnit() == eStyleUnit_Percent) {
253 // XXX This should use real percentage padding
254 nsIFrame::IntrinsicWidthOffsetData offsets =
255 cellFrame->IntrinsicWidthOffsets(aReflowState.rendContext);
256 float pct = styleWidth->GetPercentValue();
257 colWidth = NSToCoordFloor(pct * float(tableWidth)) +
258 offsets.hPadding + offsets.hBorder;
259 pct /= float(colSpan);
260 colFrame->AddPrefPercent(pct);
261 pctTotal += pct;
262 } else {
263 // 'auto', '-moz-available', and '-moz-fit-content'
264 colWidth = unassignedMarker;
266 if (colWidth != unassignedMarker) {
267 if (colSpan > 1) {
268 // If a column-spanning cell is in the first
269 // row, split up the space evenly. (XXX This
270 // isn't quite right if some of the columns it's
271 // in have specified widths. Should we care?)
272 colWidth = ((colWidth + spacing) / colSpan) - spacing;
273 if (colWidth < 0)
274 colWidth = 0;
277 } else {
278 colWidth = unassignedMarker;
282 colFrame->SetFinalWidth(colWidth);
284 if (colWidth == unassignedMarker) {
285 ++unassignedCount;
286 } else {
287 unassignedSpace -= colWidth;
291 if (unassignedSpace < 0) {
292 if (pctTotal > 0) {
293 // If the columns took up too much space, reduce those that
294 // had percentage widths. The spec doesn't say to do this,
295 // but we've always done it in the past, and so does WinIE6.
296 nscoord pctUsed = NSToCoordFloor(pctTotal * float(tableWidth));
297 nscoord reduce = PR_MIN(pctUsed, -unassignedSpace);
298 float reduceRatio = float(reduce) / pctTotal;
299 for (PRInt32 col = 0; col < colCount; ++col) {
300 nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
301 if (!colFrame) {
302 NS_ERROR("column frames out of sync with cell map");
303 continue;
305 nscoord colWidth = colFrame->GetFinalWidth();
306 colWidth -= NSToCoordFloor(colFrame->GetPrefPercent() *
307 reduceRatio);
308 if (colWidth < 0)
309 colWidth = 0;
310 colFrame->SetFinalWidth(colWidth);
313 unassignedSpace = 0;
316 if (unassignedCount > 0) {
317 nscoord toAssign = unassignedSpace / unassignedCount;
318 for (PRInt32 col = 0; col < colCount; ++col) {
319 nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
320 if (!colFrame) {
321 NS_ERROR("column frames out of sync with cell map");
322 continue;
324 if (colFrame->GetFinalWidth() == unassignedMarker)
325 colFrame->SetFinalWidth(toAssign);
327 } else if (unassignedSpace > 0) {
328 // The spec says to distribute extra space evenly. (That's not
329 // what WinIE6 does, though. It treats percentages and
330 // nonpercentages differently.)
331 nscoord toAdd = unassignedSpace / colCount;
332 for (PRInt32 col = 0; col < colCount; ++col) {
333 nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
334 if (!colFrame) {
335 NS_ERROR("column frames out of sync with cell map");
336 continue;
338 colFrame->SetFinalWidth(colFrame->GetFinalWidth() + toAdd);