layout/tables/nsTableColGroupFrame.cpp

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

mercurial