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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : *
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.
35 : *
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"
49 :
50 : #define COL_GROUP_TYPE_BITS (NS_FRAME_STATE_BIT(30) | \
51 : NS_FRAME_STATE_BIT(31))
52 : #define COL_GROUP_TYPE_OFFSET 30
53 :
54 : nsTableColGroupType
55 0 : nsTableColGroupFrame::GetColType() const
56 : {
57 0 : return (nsTableColGroupType)((mState & COL_GROUP_TYPE_BITS) >> COL_GROUP_TYPE_OFFSET);
58 : }
59 :
60 0 : void nsTableColGroupFrame::SetColType(nsTableColGroupType aType)
61 : {
62 0 : PRUint32 type = aType - eColGroupContent;
63 0 : mState |= (type << COL_GROUP_TYPE_OFFSET);
64 0 : }
65 :
66 0 : void nsTableColGroupFrame::ResetColIndices(nsIFrame* aFirstColGroup,
67 : PRInt32 aFirstColIndex,
68 : nsIFrame* aStartColFrame)
69 : {
70 0 : nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame*)aFirstColGroup;
71 0 : PRInt32 colIndex = aFirstColIndex;
72 0 : while (colGroupFrame) {
73 0 : if (nsGkAtoms::tableColGroupFrame == colGroupFrame->GetType()) {
74 : // reset the starting col index for the first cg only if we should reset
75 : // the whole colgroup (aStartColFrame defaults to nsnull) or if
76 : // aFirstColIndex is smaller than the existing starting col index
77 0 : if ((colIndex != aFirstColIndex) ||
78 0 : (colIndex < colGroupFrame->GetStartColumnIndex()) ||
79 : !aStartColFrame) {
80 0 : colGroupFrame->SetStartColumnIndex(colIndex);
81 : }
82 0 : nsIFrame* colFrame = aStartColFrame;
83 0 : if (!colFrame || (colIndex != aFirstColIndex)) {
84 0 : colFrame = colGroupFrame->GetFirstPrincipalChild();
85 : }
86 0 : while (colFrame) {
87 0 : if (nsGkAtoms::tableColFrame == colFrame->GetType()) {
88 0 : ((nsTableColFrame*)colFrame)->SetColIndex(colIndex);
89 0 : colIndex++;
90 : }
91 0 : colFrame = colFrame->GetNextSibling();
92 : }
93 : }
94 : colGroupFrame = static_cast<nsTableColGroupFrame*>
95 0 : (colGroupFrame->GetNextSibling());
96 : }
97 0 : }
98 :
99 :
100 : nsresult
101 0 : nsTableColGroupFrame::AddColsToTable(PRInt32 aFirstColIndex,
102 : bool aResetSubsequentColIndices,
103 : const nsFrameList::Slice& aCols)
104 : {
105 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
106 :
107 : // set the col indices of the col frames and and add col info to the table
108 0 : PRInt32 colIndex = aFirstColIndex;
109 0 : nsFrameList::Enumerator e(aCols);
110 0 : for (; !e.AtEnd(); e.Next()) {
111 0 : ((nsTableColFrame*)e.get())->SetColIndex(colIndex);
112 0 : mColCount++;
113 0 : tableFrame->InsertCol((nsTableColFrame &)*e.get(), colIndex);
114 0 : colIndex++;
115 : }
116 :
117 0 : for (nsFrameList::Enumerator eTail = e.GetUnlimitedEnumerator();
118 0 : !eTail.AtEnd();
119 0 : eTail.Next()) {
120 0 : ((nsTableColFrame*)eTail.get())->SetColIndex(colIndex);
121 0 : colIndex++;
122 : }
123 :
124 : // We have already set the colindex for all the colframes in this
125 : // colgroup that come after the first inserted colframe, but there could
126 : // be other colgroups following this one and their colframes need
127 : // correct colindices too.
128 0 : if (aResetSubsequentColIndices && GetNextSibling()) {
129 0 : ResetColIndices(GetNextSibling(), colIndex);
130 : }
131 :
132 0 : return NS_OK;
133 : }
134 :
135 :
136 : nsTableColGroupFrame*
137 0 : nsTableColGroupFrame::GetLastRealColGroup(nsTableFrame* aTableFrame)
138 : {
139 0 : nsFrameList colGroups = aTableFrame->GetColGroups();
140 :
141 0 : nsIFrame* nextToLastColGroup = nsnull;
142 0 : nsFrameList::FrameLinkEnumerator link(colGroups);
143 0 : for ( ; !link.AtEnd(); link.Next()) {
144 0 : nextToLastColGroup = link.PrevFrame();
145 : }
146 :
147 0 : if (!link.PrevFrame()) {
148 0 : return nsnull; // there are no col group frames
149 : }
150 :
151 : nsTableColGroupType lastColGroupType =
152 0 : static_cast<nsTableColGroupFrame*>(link.PrevFrame())->GetColType();
153 0 : if (eColGroupAnonymousCell == lastColGroupType) {
154 0 : return static_cast<nsTableColGroupFrame*>(nextToLastColGroup);
155 : }
156 :
157 0 : return static_cast<nsTableColGroupFrame*>(link.PrevFrame());
158 : }
159 :
160 : // don't set mColCount here, it is done in AddColsToTable
161 : NS_IMETHODIMP
162 0 : nsTableColGroupFrame::SetInitialChildList(ChildListID aListID,
163 : nsFrameList& aChildList)
164 : {
165 0 : if (!mFrames.IsEmpty()) {
166 : // We already have child frames which means we've already been
167 : // initialized
168 0 : NS_NOTREACHED("unexpected second call to SetInitialChildList");
169 0 : return NS_ERROR_UNEXPECTED;
170 : }
171 0 : if (aListID != kPrincipalList) {
172 : // All we know about is the principal child list.
173 0 : NS_NOTREACHED("unknown frame list");
174 0 : return NS_ERROR_INVALID_ARG;
175 : }
176 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
177 0 : if (aChildList.IsEmpty()) {
178 : tableFrame->AppendAnonymousColFrames(this, GetSpan(), eColAnonymousColGroup,
179 0 : false);
180 0 : return NS_OK;
181 : }
182 :
183 0 : mFrames.AppendFrames(this, aChildList);
184 0 : return NS_OK;
185 : }
186 :
187 : /* virtual */ void
188 0 : nsTableColGroupFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
189 : {
190 0 : if (!aOldStyleContext) //avoid this on init
191 0 : return;
192 :
193 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
194 0 : if (tableFrame->IsBorderCollapse() &&
195 0 : tableFrame->BCRecalcNeeded(aOldStyleContext, GetStyleContext())) {
196 0 : PRInt32 colCount = GetColCount();
197 0 : if (!colCount)
198 0 : return; // this is a degenerated colgroup
199 : nsIntRect damageArea(GetFirstColumn()->GetColIndex(), 0, colCount,
200 0 : tableFrame->GetRowCount());
201 0 : tableFrame->AddBCDamageArea(damageArea);
202 : }
203 : }
204 :
205 : NS_IMETHODIMP
206 0 : nsTableColGroupFrame::AppendFrames(ChildListID aListID,
207 : nsFrameList& aFrameList)
208 : {
209 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
210 :
211 0 : nsTableColFrame* col = GetFirstColumn();
212 : nsTableColFrame* nextCol;
213 0 : while (col && col->GetColType() == eColAnonymousColGroup) {
214 : // this colgroup spans one or more columns but now that there is a
215 : // real column below, spanned anonymous columns should be removed,
216 : // since the HTML spec says to ignore the span of a colgroup if it
217 : // has content columns in it.
218 0 : nextCol = col->GetNextCol();
219 0 : RemoveFrame(kPrincipalList, col);
220 0 : col = nextCol;
221 : }
222 :
223 : const nsFrameList::Slice& newFrames =
224 0 : mFrames.AppendFrames(this, aFrameList);
225 0 : InsertColsReflow(GetStartColumnIndex() + mColCount, newFrames);
226 0 : return NS_OK;
227 : }
228 :
229 : NS_IMETHODIMP
230 0 : nsTableColGroupFrame::InsertFrames(ChildListID aListID,
231 : nsIFrame* aPrevFrame,
232 : nsFrameList& aFrameList)
233 : {
234 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
235 0 : NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
236 : "inserting after sibling frame with different parent");
237 :
238 0 : nsTableColFrame* col = GetFirstColumn();
239 : nsTableColFrame* nextCol;
240 0 : while (col && col->GetColType() == eColAnonymousColGroup) {
241 : // this colgroup spans one or more columns but now that there is a
242 : // real column below, spanned anonymous columns should be removed,
243 : // since the HTML spec says to ignore the span of a colgroup if it
244 : // has content columns in it.
245 0 : nextCol = col->GetNextCol();
246 0 : if (col == aPrevFrame) {
247 : // This can happen when we're being appended to
248 0 : NS_ASSERTION(!nextCol || nextCol->GetColType() != eColAnonymousColGroup,
249 : "Inserting in the middle of our anonymous cols?");
250 : // We'll want to insert at the beginning
251 0 : aPrevFrame = nsnull;
252 : }
253 0 : RemoveFrame(kPrincipalList, col);
254 0 : col = nextCol;
255 : }
256 :
257 0 : NS_ASSERTION(!aPrevFrame || aPrevFrame == aPrevFrame->GetLastContinuation(),
258 : "Prev frame should be last in continuation chain");
259 0 : NS_ASSERTION(!aPrevFrame || !GetNextColumn(aPrevFrame) ||
260 : GetNextColumn(aPrevFrame)->GetColType() != eColAnonymousCol,
261 : "Shouldn't be inserting before a spanned colframe");
262 :
263 : const nsFrameList::Slice& newFrames =
264 0 : mFrames.InsertFrames(this, aPrevFrame, aFrameList);
265 : nsIFrame* prevFrame = nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame,
266 0 : nsGkAtoms::tableColFrame);
267 :
268 0 : PRInt32 colIndex = (prevFrame) ? ((nsTableColFrame*)prevFrame)->GetColIndex() + 1 : GetStartColumnIndex();
269 0 : InsertColsReflow(colIndex, newFrames);
270 :
271 0 : return NS_OK;
272 : }
273 :
274 : void
275 0 : nsTableColGroupFrame::InsertColsReflow(PRInt32 aColIndex,
276 : const nsFrameList::Slice& aCols)
277 : {
278 0 : AddColsToTable(aColIndex, true, aCols);
279 :
280 0 : PresContext()->PresShell()->FrameNeedsReflow(this,
281 : nsIPresShell::eTreeChange,
282 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
283 0 : }
284 :
285 : void
286 0 : nsTableColGroupFrame::RemoveChild(nsTableColFrame& aChild,
287 : bool aResetSubsequentColIndices)
288 : {
289 0 : PRInt32 colIndex = 0;
290 0 : nsIFrame* nextChild = nsnull;
291 0 : if (aResetSubsequentColIndices) {
292 0 : colIndex = aChild.GetColIndex();
293 0 : nextChild = aChild.GetNextSibling();
294 : }
295 0 : mFrames.DestroyFrame(&aChild);
296 0 : mColCount--;
297 0 : if (aResetSubsequentColIndices) {
298 0 : if (nextChild) { // reset inside this and all following colgroups
299 0 : ResetColIndices(this, colIndex, nextChild);
300 : }
301 : else {
302 0 : nsIFrame* nextGroup = GetNextSibling();
303 0 : if (nextGroup) // reset next and all following colgroups
304 0 : ResetColIndices(nextGroup, colIndex);
305 : }
306 : }
307 :
308 0 : PresContext()->PresShell()->FrameNeedsReflow(this,
309 : nsIPresShell::eTreeChange,
310 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
311 0 : }
312 :
313 : NS_IMETHODIMP
314 0 : nsTableColGroupFrame::RemoveFrame(ChildListID aListID,
315 : nsIFrame* aOldFrame)
316 : {
317 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
318 :
319 0 : if (!aOldFrame) return NS_OK;
320 0 : bool contentRemoval = false;
321 :
322 0 : if (nsGkAtoms::tableColFrame == aOldFrame->GetType()) {
323 0 : nsTableColFrame* colFrame = (nsTableColFrame*)aOldFrame;
324 0 : if (colFrame->GetColType() == eColContent) {
325 0 : contentRemoval = true;
326 : // Remove any anonymous column frames this <col> produced via a colspan
327 0 : nsTableColFrame* col = colFrame->GetNextCol();
328 : nsTableColFrame* nextCol;
329 0 : while (col && col->GetColType() == eColAnonymousCol) {
330 : #ifdef DEBUG
331 0 : nsIFrame* providerFrame = colFrame->GetParentStyleContextFrame();
332 0 : if (colFrame->GetStyleContext()->GetParent() ==
333 0 : providerFrame->GetStyleContext()) {
334 0 : NS_ASSERTION(col->GetStyleContext() == colFrame->GetStyleContext() &&
335 : col->GetContent() == colFrame->GetContent(),
336 : "How did that happen??");
337 : }
338 : // else colFrame is being removed because of a frame
339 : // reconstruct on it, and its style context is still the old
340 : // one, so we can't assert anything about how it compares to
341 : // col's style context.
342 : #endif
343 0 : nextCol = col->GetNextCol();
344 0 : RemoveFrame(kPrincipalList, col);
345 0 : col = nextCol;
346 : }
347 : }
348 :
349 0 : PRInt32 colIndex = colFrame->GetColIndex();
350 : // The RemoveChild call handles calling FrameNeedsReflow on us.
351 0 : RemoveChild(*colFrame, true);
352 :
353 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
354 0 : tableFrame->RemoveCol(this, colIndex, true, true);
355 0 : if (mFrames.IsEmpty() && contentRemoval &&
356 0 : GetColType() == eColGroupContent) {
357 : tableFrame->AppendAnonymousColFrames(this, GetSpan(),
358 0 : eColAnonymousColGroup, true);
359 : }
360 : }
361 : else {
362 0 : mFrames.DestroyFrame(aOldFrame);
363 : }
364 :
365 0 : return NS_OK;
366 : }
367 :
368 : PRIntn
369 0 : nsTableColGroupFrame::GetSkipSides() const
370 : {
371 0 : PRIntn skip = 0;
372 0 : if (nsnull != GetPrevInFlow()) {
373 0 : skip |= 1 << NS_SIDE_TOP;
374 : }
375 0 : if (nsnull != GetNextInFlow()) {
376 0 : skip |= 1 << NS_SIDE_BOTTOM;
377 : }
378 0 : return skip;
379 : }
380 :
381 0 : NS_METHOD nsTableColGroupFrame::Reflow(nsPresContext* aPresContext,
382 : nsHTMLReflowMetrics& aDesiredSize,
383 : const nsHTMLReflowState& aReflowState,
384 : nsReflowStatus& aStatus)
385 : {
386 0 : DO_GLOBAL_REFLOW_COUNT("nsTableColGroupFrame");
387 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
388 0 : NS_ASSERTION(nsnull!=mContent, "bad state -- null content for frame");
389 0 : nsresult rv=NS_OK;
390 :
391 0 : const nsStyleVisibility* groupVis = GetStyleVisibility();
392 0 : bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
393 0 : if (collapseGroup) {
394 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
395 0 : tableFrame->SetNeedToCollapse(true);
396 : }
397 : // for every content child that (is a column thingy and does not already have a frame)
398 : // create a frame and adjust it's style
399 :
400 0 : for (nsIFrame *kidFrame = mFrames.FirstChild(); kidFrame;
401 : kidFrame = kidFrame->GetNextSibling()) {
402 : // Give the child frame a chance to reflow, even though we know it'll have 0 size
403 0 : nsHTMLReflowMetrics kidSize;
404 : nsHTMLReflowState kidReflowState(aPresContext, aReflowState, kidFrame,
405 0 : nsSize(0,0));
406 :
407 : nsReflowStatus status;
408 0 : ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState, 0, 0, 0, status);
409 0 : FinishReflowChild(kidFrame, aPresContext, nsnull, kidSize, 0, 0, 0);
410 : }
411 :
412 0 : aDesiredSize.width=0;
413 0 : aDesiredSize.height=0;
414 0 : aStatus = NS_FRAME_COMPLETE;
415 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
416 0 : return rv;
417 : }
418 :
419 0 : nsTableColFrame * nsTableColGroupFrame::GetFirstColumn()
420 : {
421 0 : return GetNextColumn(nsnull);
422 : }
423 :
424 0 : nsTableColFrame * nsTableColGroupFrame::GetNextColumn(nsIFrame *aChildFrame)
425 : {
426 0 : nsTableColFrame *result = nsnull;
427 0 : nsIFrame *childFrame = aChildFrame;
428 0 : if (!childFrame) {
429 0 : childFrame = mFrames.FirstChild();
430 : }
431 : else {
432 0 : childFrame = childFrame->GetNextSibling();
433 : }
434 0 : while (childFrame)
435 : {
436 0 : if (NS_STYLE_DISPLAY_TABLE_COLUMN ==
437 0 : childFrame->GetStyleDisplay()->mDisplay)
438 : {
439 0 : result = (nsTableColFrame *)childFrame;
440 0 : break;
441 : }
442 0 : childFrame = childFrame->GetNextSibling();
443 : }
444 0 : return result;
445 : }
446 :
447 0 : PRInt32 nsTableColGroupFrame::GetSpan()
448 : {
449 0 : return GetStyleTable()->mSpan;
450 : }
451 :
452 0 : void nsTableColGroupFrame::SetContinuousBCBorderWidth(PRUint8 aForSide,
453 : BCPixelSize aPixelValue)
454 : {
455 0 : switch (aForSide) {
456 : case NS_SIDE_TOP:
457 0 : mTopContBorderWidth = aPixelValue;
458 0 : return;
459 : case NS_SIDE_BOTTOM:
460 0 : mBottomContBorderWidth = aPixelValue;
461 0 : return;
462 : default:
463 0 : NS_ERROR("invalid side arg");
464 : }
465 : }
466 :
467 0 : void nsTableColGroupFrame::GetContinuousBCBorderWidth(nsMargin& aBorder)
468 : {
469 0 : PRInt32 aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel();
470 0 : nsTableFrame* table = nsTableFrame::GetTableFrame(this);
471 0 : nsTableColFrame* col = table->GetColFrame(mStartColIndex + mColCount - 1);
472 0 : col->GetContinuousBCBorderWidth(aBorder);
473 : aBorder.top = BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips,
474 0 : mTopContBorderWidth);
475 : aBorder.bottom = BC_BORDER_TOP_HALF_COORD(aPixelsToTwips,
476 0 : mBottomContBorderWidth);
477 0 : }
478 :
479 : /* ----- global methods ----- */
480 :
481 : nsIFrame*
482 0 : NS_NewTableColGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
483 : {
484 0 : return new (aPresShell) nsTableColGroupFrame(aContext);
485 : }
486 :
487 0 : NS_IMPL_FRAMEARENA_HELPERS(nsTableColGroupFrame)
488 :
489 : nsIAtom*
490 0 : nsTableColGroupFrame::GetType() const
491 : {
492 0 : return nsGkAtoms::tableColGroupFrame;
493 : }
494 :
495 : #ifdef DEBUG
496 : NS_IMETHODIMP
497 0 : nsTableColGroupFrame::GetFrameName(nsAString& aResult) const
498 : {
499 0 : return MakeFrameName(NS_LITERAL_STRING("TableColGroup"), aResult);
500 : }
501 :
502 0 : void nsTableColGroupFrame::Dump(PRInt32 aIndent)
503 : {
504 0 : char* indent = new char[aIndent + 1];
505 0 : if (!indent) return;
506 0 : for (PRInt32 i = 0; i < aIndent + 1; i++) {
507 0 : indent[i] = ' ';
508 : }
509 0 : indent[aIndent] = 0;
510 :
511 : printf("%s**START COLGROUP DUMP**\n%s startcolIndex=%d colcount=%d span=%d coltype=",
512 0 : indent, indent, GetStartColumnIndex(), GetColCount(), GetSpan());
513 0 : nsTableColGroupType colType = GetColType();
514 0 : switch (colType) {
515 : case eColGroupContent:
516 0 : printf(" content ");
517 0 : break;
518 : case eColGroupAnonymousCol:
519 0 : printf(" anonymous-column ");
520 0 : break;
521 : case eColGroupAnonymousCell:
522 0 : printf(" anonymous-cell ");
523 0 : break;
524 : }
525 : // verify the colindices
526 0 : PRInt32 j = GetStartColumnIndex();
527 0 : nsTableColFrame* col = GetFirstColumn();
528 0 : while (col) {
529 0 : NS_ASSERTION(j == col->GetColIndex(), "wrong colindex on col frame");
530 0 : col = col->GetNextCol();
531 0 : j++;
532 : }
533 0 : NS_ASSERTION((j - GetStartColumnIndex()) == GetColCount(),
534 : "number of cols out of sync");
535 0 : printf("\n%s**END COLGROUP DUMP** ", indent);
536 0 : delete [] indent;
537 : }
538 : #endif
|