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
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.
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();
56 FixedTableLayoutStrategy::~FixedTableLayoutStrategy()
61 FixedTableLayoutStrategy::GetMinWidth(nsIRenderingContext
* aRenderingContext
)
63 DISPLAY_MIN_WIDTH(mTableFrame
, mMinWidth
);
64 if (mMinWidth
!= NS_INTRINSIC_WIDTH_UNKNOWN
)
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,
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?
86 // XXX Consider widths on columns or column groups?
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
);
96 NS_ERROR("column frames out of sync with cell map");
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
) {
107 NS_ASSERTION(styleWidth
->GetUnit() == eStyleUnit_Auto
||
108 styleWidth
->GetUnit() == eStyleUnit_Enumerated
,
111 // The 'table-layout: fixed' algorithm considers only cells
115 nsTableCellFrame
*cellFrame
=
116 cellMap
->GetCellInfoAt(0, col
, &originates
, &colSpan
);
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
);
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
;
133 } else if (styleWidth
->GetUnit() == eStyleUnit_Percent
) {
135 // XXX Can this force columns to negative
137 result
-= spacing
* (colSpan
- 1);
140 // else, for 'auto', '-moz-available', and '-moz-fit-content'
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
);
162 FixedTableLayoutStrategy::MarkIntrinsicWidthsDirty()
164 mMinWidth
= NS_INTRINSIC_WIDTH_UNKNOWN
;
165 mLastCalcWidth
= nscoord_MIN
;
169 FixedTableLayoutStrategy::ComputeColumnWidths(const nsHTMLReflowState
& aReflowState
)
171 nscoord tableWidth
= aReflowState
.ComputedWidth();
173 if (mLastCalcWidth
== tableWidth
)
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.
185 // XXX Should only add columns that have cells originating in them!
186 nscoord subtract
= spacing
* (colCount
+ 1);
187 tableWidth
-= subtract
;
189 // No Columns - nothing to compute
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
205 float pctTotal
= 0.0f
;
207 for (PRInt32 col
= 0; col
< colCount
; ++col
) {
208 nsTableColFrame
*colFrame
= mTableFrame
->GetColFrame(col
);
210 NS_ERROR("column frames out of sync with cell map");
213 colFrame
->ResetPrefPercent();
214 const nsStyleCoord
*styleWidth
=
215 &colFrame
->GetStylePosition()->mWidth
;
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
);
227 NS_ASSERTION(styleWidth
->GetUnit() == eStyleUnit_Auto
||
228 styleWidth
->GetUnit() == eStyleUnit_Enumerated
,
231 // The 'table-layout: fixed' algorithm considers only cells
235 nsTableCellFrame
*cellFrame
=
236 cellMap
->GetCellInfoAt(0, col
, &originates
, &colSpan
);
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
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
);
263 // 'auto', '-moz-available', and '-moz-fit-content'
264 colWidth
= unassignedMarker
;
266 if (colWidth
!= unassignedMarker
) {
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
;
278 colWidth
= unassignedMarker
;
282 colFrame
->SetFinalWidth(colWidth
);
284 if (colWidth
== unassignedMarker
) {
287 unassignedSpace
-= colWidth
;
291 if (unassignedSpace
< 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
);
302 NS_ERROR("column frames out of sync with cell map");
305 nscoord colWidth
= colFrame
->GetFinalWidth();
306 colWidth
-= NSToCoordFloor(colFrame
->GetPrefPercent() *
310 colFrame
->SetFinalWidth(colWidth
);
316 if (unassignedCount
> 0) {
317 nscoord toAssign
= unassignedSpace
/ unassignedCount
;
318 for (PRInt32 col
= 0; col
< colCount
; ++col
) {
319 nsTableColFrame
*colFrame
= mTableFrame
->GetColFrame(col
);
321 NS_ERROR("column frames out of sync with cell map");
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
);
335 NS_ERROR("column frames out of sync with cell map");
338 colFrame
->SetFinalWidth(colFrame
->GetFinalWidth() + toAdd
);