Back out bug 459443 for now, until the patch in bug 474389 gets reviewed
[mozilla-central.git] / layout / tables / nsTableColGroupFrame.cpp
blobc6b162b70f62d21e0f4d5c7edf49456f53948091
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
37 #include "nsTableColGroupFrame.h"
38 #include "nsTableColFrame.h"
39 #include "nsTableFrame.h"
40 #include "nsIDOMHTMLTableColElement.h"
41 #include "nsStyleContext.h"
42 #include "nsStyleConsts.h"
43 #include "nsPresContext.h"
44 #include "nsHTMLParts.h"
45 #include "nsGkAtoms.h"
46 #include "nsCOMPtr.h"
47 #include "nsCSSRendering.h"
48 #include "nsIPresShell.h"
50 #define COL_GROUP_TYPE_BITS 0xC0000000 // uses bits 31-32 from mState
51 #define COL_GROUP_TYPE_OFFSET 30
53 nsTableColGroupType
54 nsTableColGroupFrame::GetColType() const
56 return (nsTableColGroupType)((mState & COL_GROUP_TYPE_BITS) >> COL_GROUP_TYPE_OFFSET);
59 void nsTableColGroupFrame::SetColType(nsTableColGroupType aType)
61 PRUint32 type = aType - eColGroupContent;
62 mState |= (type << COL_GROUP_TYPE_OFFSET);
65 void nsTableColGroupFrame::ResetColIndices(nsIFrame* aFirstColGroup,
66 PRInt32 aFirstColIndex,
67 nsIFrame* aStartColFrame)
69 nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame*)aFirstColGroup;
70 PRInt32 colIndex = aFirstColIndex;
71 while (colGroupFrame) {
72 if (nsGkAtoms::tableColGroupFrame == colGroupFrame->GetType()) {
73 // reset the starting col index for the first cg only if we should reset
74 // the whole colgroup (aStartColFrame defaults to nsnull) or if
75 // aFirstColIndex is smaller than the existing starting col index
76 if ((colIndex != aFirstColIndex) ||
77 (colIndex < colGroupFrame->GetStartColumnIndex()) ||
78 !aStartColFrame) {
79 colGroupFrame->SetStartColumnIndex(colIndex);
81 nsIFrame* colFrame = aStartColFrame;
82 if (!colFrame || (colIndex != aFirstColIndex)) {
83 colFrame = colGroupFrame->GetFirstChild(nsnull);
85 while (colFrame) {
86 if (nsGkAtoms::tableColFrame == colFrame->GetType()) {
87 ((nsTableColFrame*)colFrame)->SetColIndex(colIndex);
88 colIndex++;
90 colFrame = colFrame->GetNextSibling();
93 colGroupFrame = static_cast<nsTableColGroupFrame*>
94 (colGroupFrame->GetNextSibling());
99 nsresult
100 nsTableColGroupFrame::AddColsToTable(PRInt32 aFirstColIndex,
101 PRBool aResetSubsequentColIndices,
102 nsIFrame* aFirstFrame,
103 nsIFrame* aLastFrame)
105 nsresult rv = NS_OK;
106 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
107 if (!tableFrame || !aFirstFrame)
108 return NS_ERROR_NULL_POINTER;
110 // set the col indices of the col frames and and add col info to the table
111 PRInt32 colIndex = aFirstColIndex;
112 nsIFrame* kidFrame = aFirstFrame;
113 PRBool foundLastFrame = PR_FALSE;
114 while (kidFrame) {
115 if (nsGkAtoms::tableColFrame == kidFrame->GetType()) {
116 ((nsTableColFrame*)kidFrame)->SetColIndex(colIndex);
117 if (!foundLastFrame) {
118 mColCount++;
119 tableFrame->InsertCol((nsTableColFrame &)*kidFrame, colIndex);
121 colIndex++;
123 if (kidFrame == aLastFrame) {
124 foundLastFrame = PR_TRUE;
126 kidFrame = kidFrame->GetNextSibling();
128 // We have already set the colindex for all the colframes in this
129 // colgroup that come after the first inserted colframe, but there could
130 // be other colgroups following this one and their colframes need
131 // correct colindices too.
132 if (aResetSubsequentColIndices && GetNextSibling()) {
133 ResetColIndices(GetNextSibling(), colIndex);
136 return rv;
140 PRBool
141 nsTableColGroupFrame::GetLastRealColGroup(nsTableFrame* aTableFrame,
142 nsIFrame** aLastColGroup)
144 *aLastColGroup = nsnull;
145 nsFrameList colGroups = aTableFrame->GetColGroups();
147 nsIFrame* nextToLastColGroup = nsnull;
148 nsIFrame* lastColGroup = colGroups.FirstChild();
149 while(lastColGroup) {
150 nsIFrame* next = lastColGroup->GetNextSibling();
151 if (next) {
152 nextToLastColGroup = lastColGroup;
153 lastColGroup = next;
155 else {
156 break;
160 if (!lastColGroup) return PR_TRUE; // there are no col group frames
162 nsTableColGroupType lastColGroupType =
163 ((nsTableColGroupFrame *)lastColGroup)->GetColType();
164 if (eColGroupAnonymousCell == lastColGroupType) {
165 *aLastColGroup = nextToLastColGroup;
166 return PR_FALSE;
168 else {
169 *aLastColGroup = lastColGroup;
170 return PR_TRUE;
174 // don't set mColCount here, it is done in AddColsToTable
175 NS_IMETHODIMP
176 nsTableColGroupFrame::SetInitialChildList(nsIAtom* aListName,
177 nsIFrame* aChildList)
179 if (!mFrames.IsEmpty()) {
180 // We already have child frames which means we've already been
181 // initialized
182 NS_NOTREACHED("unexpected second call to SetInitialChildList");
183 return NS_ERROR_UNEXPECTED;
185 if (aListName) {
186 // All we know about is the unnamed principal child list
187 NS_NOTREACHED("unknown frame list");
188 return NS_ERROR_INVALID_ARG;
190 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
191 if (!tableFrame)
192 return NS_ERROR_NULL_POINTER;
194 if (!aChildList) {
195 nsIFrame* firstChild;
196 tableFrame->CreateAnonymousColFrames(this, GetSpan(), eColAnonymousColGroup,
197 PR_FALSE, nsnull, &firstChild);
198 if (firstChild) {
199 SetInitialChildList(aListName, firstChild);
201 return NS_OK;
204 mFrames.AppendFrames(this, aChildList);
205 return NS_OK;
208 /* virtual */ void
209 nsTableColGroupFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
211 if (!aOldStyleContext) //avoid this on init
212 return;
214 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
216 if (tableFrame->IsBorderCollapse() &&
217 tableFrame->BCRecalcNeeded(aOldStyleContext, GetStyleContext())) {
218 PRInt32 colCount = GetColCount();
219 if (!colCount)
220 return; // this is a degenerated colgroup
221 nsRect damageArea(GetFirstColumn()->GetColIndex(), 0, colCount,
222 tableFrame->GetRowCount());
223 tableFrame->SetBCDamageArea(damageArea);
225 return;
228 NS_IMETHODIMP
229 nsTableColGroupFrame::AppendFrames(nsIAtom* aListName,
230 nsIFrame* aFrameList)
232 NS_ASSERTION(!aListName, "unexpected child list");
234 nsTableColFrame* col = GetFirstColumn();
235 nsTableColFrame* nextCol;
236 while (col && col->GetColType() == eColAnonymousColGroup) {
237 // this colgroup spans one or more columns but now that there is a
238 // real column below, spanned anonymous columns should be removed,
239 // since the HTML spec says to ignore the span of a colgroup if it
240 // has content columns in it.
241 nextCol = col->GetNextCol();
242 RemoveFrame(nsnull, col);
243 col = nextCol;
246 mFrames.AppendFrames(this, aFrameList);
247 InsertColsReflow(GetStartColumnIndex() + mColCount, aFrameList);
248 return NS_OK;
251 NS_IMETHODIMP
252 nsTableColGroupFrame::InsertFrames(nsIAtom* aListName,
253 nsIFrame* aPrevFrame,
254 nsIFrame* aFrameList)
256 NS_ASSERTION(!aListName, "unexpected child list");
257 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
258 "inserting after sibling frame with different parent");
260 nsFrameList frames(aFrameList); // convience for getting last frame
261 nsIFrame* lastFrame = frames.LastChild();
263 nsTableColFrame* col = GetFirstColumn();
264 nsTableColFrame* nextCol;
265 while (col && col->GetColType() == eColAnonymousColGroup) {
266 // this colgroup spans one or more columns but now that there is a
267 // real column below, spanned anonymous columns should be removed,
268 // since the HTML spec says to ignore the span of a colgroup if it
269 // has content columns in it.
270 NS_ASSERTION(col != aPrevFrame, "Bad aPrevFrame");
271 nextCol = col->GetNextCol();
272 RemoveFrame(nsnull, col);
273 col = nextCol;
276 NS_ASSERTION(!aPrevFrame || aPrevFrame == aPrevFrame->GetLastContinuation(),
277 "Prev frame should be last in continuation chain");
278 NS_ASSERTION(!aPrevFrame || !GetNextColumn(aPrevFrame) ||
279 GetNextColumn(aPrevFrame)->GetColType() != eColAnonymousCol,
280 "Shouldn't be inserting before a spanned colframe");
282 mFrames.InsertFrames(this, aPrevFrame, aFrameList);
283 nsIFrame* prevFrame = nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame,
284 nsGkAtoms::tableColFrame);
286 PRInt32 colIndex = (prevFrame) ? ((nsTableColFrame*)prevFrame)->GetColIndex() + 1 : GetStartColumnIndex();
287 InsertColsReflow(colIndex, aFrameList, lastFrame);
289 return NS_OK;
292 void
293 nsTableColGroupFrame::InsertColsReflow(PRInt32 aColIndex,
294 nsIFrame* aFirstFrame,
295 nsIFrame* aLastFrame)
297 AddColsToTable(aColIndex, PR_TRUE, aFirstFrame, aLastFrame);
299 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
300 if (!tableFrame)
301 return;
303 PresContext()->PresShell()->FrameNeedsReflow(tableFrame,
304 nsIPresShell::eTreeChange,
305 NS_FRAME_HAS_DIRTY_CHILDREN);
308 void
309 nsTableColGroupFrame::RemoveChild(nsTableColFrame& aChild,
310 PRBool aResetSubsequentColIndices)
312 PRInt32 colIndex = 0;
313 nsIFrame* nextChild = nsnull;
314 if (aResetSubsequentColIndices) {
315 colIndex = aChild.GetColIndex();
316 nextChild = aChild.GetNextSibling();
318 if (mFrames.DestroyFrame((nsIFrame*)&aChild)) {
319 mColCount--;
320 if (aResetSubsequentColIndices) {
321 if (nextChild) { // reset inside this and all following colgroups
322 ResetColIndices(this, colIndex, nextChild);
324 else {
325 nsIFrame* nextGroup = GetNextSibling();
326 if (nextGroup) // reset next and all following colgroups
327 ResetColIndices(nextGroup, colIndex);
331 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
332 if (!tableFrame)
333 return;
335 PresContext()->PresShell()->FrameNeedsReflow(tableFrame,
336 nsIPresShell::eTreeChange,
337 NS_FRAME_HAS_DIRTY_CHILDREN);
340 NS_IMETHODIMP
341 nsTableColGroupFrame::RemoveFrame(nsIAtom* aListName,
342 nsIFrame* aOldFrame)
344 NS_ASSERTION(!aListName, "unexpected child list");
346 if (!aOldFrame) return NS_OK;
348 if (nsGkAtoms::tableColFrame == aOldFrame->GetType()) {
349 nsTableColFrame* colFrame = (nsTableColFrame*)aOldFrame;
350 if (colFrame->GetColType() == eColContent) {
351 // Remove any anonymous column frames this <col> produced via a colspan
352 nsTableColFrame* col = colFrame->GetNextCol();
353 nsTableColFrame* nextCol;
354 while (col && col->GetColType() == eColAnonymousCol) {
355 NS_ASSERTION(col->GetStyleContext() == colFrame->GetStyleContext() &&
356 col->GetContent() == colFrame->GetContent(),
357 "How did that happen??");
358 nextCol = col->GetNextCol();
359 RemoveFrame(nsnull, col);
360 col = nextCol;
364 PRInt32 colIndex = colFrame->GetColIndex();
365 RemoveChild(*colFrame, PR_TRUE);
367 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
368 if (!tableFrame)
369 return NS_ERROR_NULL_POINTER;
371 tableFrame->RemoveCol(this, colIndex, PR_TRUE, PR_TRUE);
373 PresContext()->PresShell()->FrameNeedsReflow(tableFrame,
374 nsIPresShell::eTreeChange,
375 NS_FRAME_HAS_DIRTY_CHILDREN);
377 else {
378 mFrames.DestroyFrame(aOldFrame);
381 return NS_OK;
384 PRIntn
385 nsTableColGroupFrame::GetSkipSides() const
387 PRIntn skip = 0;
388 if (nsnull != GetPrevInFlow()) {
389 skip |= 1 << NS_SIDE_TOP;
391 if (nsnull != GetNextInFlow()) {
392 skip |= 1 << NS_SIDE_BOTTOM;
394 return skip;
397 NS_METHOD nsTableColGroupFrame::Reflow(nsPresContext* aPresContext,
398 nsHTMLReflowMetrics& aDesiredSize,
399 const nsHTMLReflowState& aReflowState,
400 nsReflowStatus& aStatus)
402 DO_GLOBAL_REFLOW_COUNT("nsTableColGroupFrame");
403 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
404 NS_ASSERTION(nsnull!=mContent, "bad state -- null content for frame");
405 nsresult rv=NS_OK;
407 const nsStyleVisibility* groupVis = GetStyleVisibility();
408 PRBool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
409 if (collapseGroup) {
410 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
411 if (tableFrame) {
412 tableFrame->SetNeedToCollapse(PR_TRUE);;
415 // for every content child that (is a column thingy and does not already have a frame)
416 // create a frame and adjust it's style
418 for (nsIFrame *kidFrame = mFrames.FirstChild(); kidFrame;
419 kidFrame = kidFrame->GetNextSibling()) {
420 // Give the child frame a chance to reflow, even though we know it'll have 0 size
421 nsHTMLReflowMetrics kidSize;
422 nsHTMLReflowState kidReflowState(aPresContext, aReflowState, kidFrame,
423 nsSize(0,0));
425 nsReflowStatus status;
426 ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState, 0, 0, 0, status);
427 FinishReflowChild(kidFrame, aPresContext, nsnull, kidSize, 0, 0, 0);
430 aDesiredSize.width=0;
431 aDesiredSize.height=0;
432 aStatus = NS_FRAME_COMPLETE;
433 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
434 return rv;
437 /* virtual */ PRBool
438 nsTableColGroupFrame::IsContainingBlock() const
440 return PR_TRUE;
443 nsTableColFrame * nsTableColGroupFrame::GetFirstColumn()
445 return GetNextColumn(nsnull);
448 nsTableColFrame * nsTableColGroupFrame::GetNextColumn(nsIFrame *aChildFrame)
450 nsTableColFrame *result = nsnull;
451 nsIFrame *childFrame = aChildFrame;
452 if (!childFrame) {
453 childFrame = mFrames.FirstChild();
455 else {
456 childFrame = childFrame->GetNextSibling();
458 while (childFrame)
460 if (NS_STYLE_DISPLAY_TABLE_COLUMN ==
461 childFrame->GetStyleDisplay()->mDisplay)
463 result = (nsTableColFrame *)childFrame;
464 break;
466 childFrame = childFrame->GetNextSibling();
468 return result;
471 PRInt32 nsTableColGroupFrame::GetSpan()
473 return GetStyleTable()->mSpan;
476 void nsTableColGroupFrame::SetContinuousBCBorderWidth(PRUint8 aForSide,
477 BCPixelSize aPixelValue)
479 switch (aForSide) {
480 case NS_SIDE_TOP:
481 mTopContBorderWidth = aPixelValue;
482 return;
483 case NS_SIDE_BOTTOM:
484 mBottomContBorderWidth = aPixelValue;
485 return;
486 default:
487 NS_ERROR("invalid side arg");
491 void nsTableColGroupFrame::GetContinuousBCBorderWidth(nsMargin& aBorder)
493 PRInt32 aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel();
494 nsTableFrame* table = nsTableFrame::GetTableFrame(this);
495 nsTableColFrame* col = table->GetColFrame(mStartColIndex + mColCount - 1);
496 col->GetContinuousBCBorderWidth(aBorder);
497 aBorder.top = BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips,
498 mTopContBorderWidth);
499 aBorder.bottom = BC_BORDER_TOP_HALF_COORD(aPixelsToTwips,
500 mBottomContBorderWidth);
501 return;
504 /* ----- global methods ----- */
506 nsIFrame*
507 NS_NewTableColGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
509 return new (aPresShell) nsTableColGroupFrame(aContext);
512 nsIAtom*
513 nsTableColGroupFrame::GetType() const
515 return nsGkAtoms::tableColGroupFrame;
518 #ifdef DEBUG
519 NS_IMETHODIMP
520 nsTableColGroupFrame::GetFrameName(nsAString& aResult) const
522 return MakeFrameName(NS_LITERAL_STRING("TableColGroup"), aResult);
525 void nsTableColGroupFrame::Dump(PRInt32 aIndent)
527 char* indent = new char[aIndent + 1];
528 if (!indent) return;
529 for (PRInt32 i = 0; i < aIndent + 1; i++) {
530 indent[i] = ' ';
532 indent[aIndent] = 0;
534 printf("%s**START COLGROUP DUMP**\n%s startcolIndex=%d colcount=%d span=%d coltype=",
535 indent, indent, GetStartColumnIndex(), GetColCount(), GetSpan());
536 nsTableColGroupType colType = GetColType();
537 switch (colType) {
538 case eColGroupContent:
539 printf(" content ");
540 break;
541 case eColGroupAnonymousCol:
542 printf(" anonymous-column ");
543 break;
544 case eColGroupAnonymousCell:
545 printf(" anonymous-cell ");
546 break;
548 // verify the colindices
549 PRInt32 j = GetStartColumnIndex();
550 nsTableColFrame* col = GetFirstColumn();
551 while (col) {
552 NS_ASSERTION(j == col->GetColIndex(), "wrong colindex on col frame");
553 col = col->GetNextCol();
554 j++;
556 NS_ASSERTION((j - GetStartColumnIndex()) == GetColCount(),
557 "number of cols out of sync");
558 printf("\n%s**END COLGROUP DUMP** ", indent);
559 delete [] indent;
561 #endif