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"
14 #include "nsCSSRendering.h"
15 #include "nsIPresShell.h"
17 using namespace mozilla
;
19 #define COL_GROUP_TYPE_BITS (NS_FRAME_STATE_BIT(30) | \
20 NS_FRAME_STATE_BIT(31))
21 #define COL_GROUP_TYPE_OFFSET 30
24 nsTableColGroupFrame::GetColType() const
26 return (nsTableColGroupType
)((mState
& COL_GROUP_TYPE_BITS
) >> COL_GROUP_TYPE_OFFSET
);
29 void nsTableColGroupFrame::SetColType(nsTableColGroupType aType
)
31 NS_ASSERTION(GetColType() == eColGroupContent
,
32 "should only call nsTableColGroupFrame::SetColType with aType "
33 "!= eColGroupContent once");
34 uint32_t type
= aType
- eColGroupContent
;
35 RemoveStateBits(COL_GROUP_TYPE_BITS
);
36 AddStateBits(nsFrameState(type
<< COL_GROUP_TYPE_OFFSET
));
39 void nsTableColGroupFrame::ResetColIndices(nsIFrame
* aFirstColGroup
,
40 int32_t aFirstColIndex
,
41 nsIFrame
* aStartColFrame
)
43 nsTableColGroupFrame
* colGroupFrame
= (nsTableColGroupFrame
*)aFirstColGroup
;
44 int32_t colIndex
= aFirstColIndex
;
45 while (colGroupFrame
) {
46 if (nsGkAtoms::tableColGroupFrame
== colGroupFrame
->GetType()) {
47 // reset the starting col index for the first cg only if we should reset
48 // the whole colgroup (aStartColFrame defaults to nullptr) or if
49 // aFirstColIndex is smaller than the existing starting col index
50 if ((colIndex
!= aFirstColIndex
) ||
51 (colIndex
< colGroupFrame
->GetStartColumnIndex()) ||
53 colGroupFrame
->SetStartColumnIndex(colIndex
);
55 nsIFrame
* colFrame
= aStartColFrame
;
56 if (!colFrame
|| (colIndex
!= aFirstColIndex
)) {
57 colFrame
= colGroupFrame
->GetFirstPrincipalChild();
60 if (nsGkAtoms::tableColFrame
== colFrame
->GetType()) {
61 ((nsTableColFrame
*)colFrame
)->SetColIndex(colIndex
);
64 colFrame
= colFrame
->GetNextSibling();
67 colGroupFrame
= static_cast<nsTableColGroupFrame
*>
68 (colGroupFrame
->GetNextSibling());
74 nsTableColGroupFrame::AddColsToTable(int32_t aFirstColIndex
,
75 bool aResetSubsequentColIndices
,
76 const nsFrameList::Slice
& aCols
)
78 nsTableFrame
* tableFrame
= nsTableFrame::GetTableFrame(this);
80 tableFrame
->InvalidateFrameSubtree();
82 // set the col indices of the col frames and and add col info to the table
83 int32_t colIndex
= aFirstColIndex
;
84 nsFrameList::Enumerator
e(aCols
);
85 for (; !e
.AtEnd(); e
.Next()) {
86 ((nsTableColFrame
*)e
.get())->SetColIndex(colIndex
);
88 tableFrame
->InsertCol((nsTableColFrame
&)*e
.get(), colIndex
);
92 for (nsFrameList::Enumerator eTail
= e
.GetUnlimitedEnumerator();
95 ((nsTableColFrame
*)eTail
.get())->SetColIndex(colIndex
);
99 // We have already set the colindex for all the colframes in this
100 // colgroup that come after the first inserted colframe, but there could
101 // be other colgroups following this one and their colframes need
102 // correct colindices too.
103 if (aResetSubsequentColIndices
&& GetNextSibling()) {
104 ResetColIndices(GetNextSibling(), colIndex
);
111 nsTableColGroupFrame
*
112 nsTableColGroupFrame::GetLastRealColGroup(nsTableFrame
* aTableFrame
)
114 nsFrameList colGroups
= aTableFrame
->GetColGroups();
116 nsIFrame
* nextToLastColGroup
= nullptr;
117 nsFrameList::FrameLinkEnumerator
link(colGroups
);
118 for ( ; !link
.AtEnd(); link
.Next()) {
119 nextToLastColGroup
= link
.PrevFrame();
122 if (!link
.PrevFrame()) {
123 return nullptr; // there are no col group frames
126 nsTableColGroupType lastColGroupType
=
127 static_cast<nsTableColGroupFrame
*>(link
.PrevFrame())->GetColType();
128 if (eColGroupAnonymousCell
== lastColGroupType
) {
129 return static_cast<nsTableColGroupFrame
*>(nextToLastColGroup
);
132 return static_cast<nsTableColGroupFrame
*>(link
.PrevFrame());
135 // don't set mColCount here, it is done in AddColsToTable
137 nsTableColGroupFrame::SetInitialChildList(ChildListID aListID
,
138 nsFrameList
& aChildList
)
140 MOZ_ASSERT(mFrames
.IsEmpty(),
141 "unexpected second call to SetInitialChildList");
142 MOZ_ASSERT(aListID
== kPrincipalList
, "unexpected child list");
143 if (aChildList
.IsEmpty()) {
144 nsTableFrame
* tableFrame
= nsTableFrame::GetTableFrame(this);
145 tableFrame
->AppendAnonymousColFrames(this, GetSpan(), eColAnonymousColGroup
,
150 mFrames
.AppendFrames(this, aChildList
);
154 nsTableColGroupFrame::DidSetStyleContext(nsStyleContext
* aOldStyleContext
)
156 nsContainerFrame::DidSetStyleContext(aOldStyleContext
);
158 if (!aOldStyleContext
) //avoid this on init
161 nsTableFrame
* tableFrame
= nsTableFrame::GetTableFrame(this);
162 if (tableFrame
->IsBorderCollapse() &&
163 tableFrame
->BCRecalcNeeded(aOldStyleContext
, StyleContext())) {
164 int32_t colCount
= GetColCount();
166 return; // this is a degenerated colgroup
167 nsIntRect
damageArea(GetFirstColumn()->GetColIndex(), 0, colCount
,
168 tableFrame
->GetRowCount());
169 tableFrame
->AddBCDamageArea(damageArea
);
174 nsTableColGroupFrame::AppendFrames(ChildListID aListID
,
175 nsFrameList
& aFrameList
)
177 NS_ASSERTION(aListID
== kPrincipalList
, "unexpected child list");
179 nsTableColFrame
* col
= GetFirstColumn();
180 nsTableColFrame
* nextCol
;
181 while (col
&& col
->GetColType() == eColAnonymousColGroup
) {
182 // this colgroup spans one or more columns but now that there is a
183 // real column below, spanned anonymous columns should be removed,
184 // since the HTML spec says to ignore the span of a colgroup if it
185 // has content columns in it.
186 nextCol
= col
->GetNextCol();
187 RemoveFrame(kPrincipalList
, col
);
191 const nsFrameList::Slice
& newFrames
=
192 mFrames
.AppendFrames(this, aFrameList
);
193 InsertColsReflow(GetStartColumnIndex() + mColCount
, newFrames
);
197 nsTableColGroupFrame::InsertFrames(ChildListID aListID
,
198 nsIFrame
* aPrevFrame
,
199 nsFrameList
& aFrameList
)
201 NS_ASSERTION(aListID
== kPrincipalList
, "unexpected child list");
202 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
203 "inserting after sibling frame with different parent");
205 nsTableColFrame
* col
= GetFirstColumn();
206 nsTableColFrame
* nextCol
;
207 while (col
&& col
->GetColType() == eColAnonymousColGroup
) {
208 // this colgroup spans one or more columns but now that there is a
209 // real column below, spanned anonymous columns should be removed,
210 // since the HTML spec says to ignore the span of a colgroup if it
211 // has content columns in it.
212 nextCol
= col
->GetNextCol();
213 if (col
== aPrevFrame
) {
214 // This can happen when we're being appended to
215 NS_ASSERTION(!nextCol
|| nextCol
->GetColType() != eColAnonymousColGroup
,
216 "Inserting in the middle of our anonymous cols?");
217 // We'll want to insert at the beginning
218 aPrevFrame
= nullptr;
220 RemoveFrame(kPrincipalList
, col
);
224 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
== aPrevFrame
->LastContinuation(),
225 "Prev frame should be last in continuation chain");
226 NS_ASSERTION(!aPrevFrame
|| !GetNextColumn(aPrevFrame
) ||
227 GetNextColumn(aPrevFrame
)->GetColType() != eColAnonymousCol
,
228 "Shouldn't be inserting before a spanned colframe");
230 const nsFrameList::Slice
& newFrames
=
231 mFrames
.InsertFrames(this, aPrevFrame
, aFrameList
);
232 nsIFrame
* prevFrame
= nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame
,
233 nsGkAtoms::tableColFrame
);
235 int32_t colIndex
= (prevFrame
) ? ((nsTableColFrame
*)prevFrame
)->GetColIndex() + 1 : GetStartColumnIndex();
236 InsertColsReflow(colIndex
, newFrames
);
240 nsTableColGroupFrame::InsertColsReflow(int32_t aColIndex
,
241 const nsFrameList::Slice
& aCols
)
243 AddColsToTable(aColIndex
, true, aCols
);
245 PresContext()->PresShell()->FrameNeedsReflow(this,
246 nsIPresShell::eTreeChange
,
247 NS_FRAME_HAS_DIRTY_CHILDREN
);
251 nsTableColGroupFrame::RemoveChild(nsTableColFrame
& aChild
,
252 bool aResetSubsequentColIndices
)
254 int32_t colIndex
= 0;
255 nsIFrame
* nextChild
= nullptr;
256 if (aResetSubsequentColIndices
) {
257 colIndex
= aChild
.GetColIndex();
258 nextChild
= aChild
.GetNextSibling();
260 mFrames
.DestroyFrame(&aChild
);
262 if (aResetSubsequentColIndices
) {
263 if (nextChild
) { // reset inside this and all following colgroups
264 ResetColIndices(this, colIndex
, nextChild
);
267 nsIFrame
* nextGroup
= GetNextSibling();
268 if (nextGroup
) // reset next and all following colgroups
269 ResetColIndices(nextGroup
, colIndex
);
273 PresContext()->PresShell()->FrameNeedsReflow(this,
274 nsIPresShell::eTreeChange
,
275 NS_FRAME_HAS_DIRTY_CHILDREN
);
279 nsTableColGroupFrame::RemoveFrame(ChildListID aListID
,
282 NS_ASSERTION(aListID
== kPrincipalList
, "unexpected child list");
287 bool contentRemoval
= false;
289 if (nsGkAtoms::tableColFrame
== aOldFrame
->GetType()) {
290 nsTableColFrame
* colFrame
= (nsTableColFrame
*)aOldFrame
;
291 if (colFrame
->GetColType() == eColContent
) {
292 contentRemoval
= true;
293 // Remove any anonymous column frames this <col> produced via a colspan
294 nsTableColFrame
* col
= colFrame
->GetNextCol();
295 nsTableColFrame
* nextCol
;
296 while (col
&& col
->GetColType() == eColAnonymousCol
) {
298 nsIFrame
* providerFrame
= colFrame
->GetParentStyleContextFrame();
299 if (colFrame
->StyleContext()->GetParent() ==
300 providerFrame
->StyleContext()) {
301 NS_ASSERTION(col
->StyleContext() == colFrame
->StyleContext() &&
302 col
->GetContent() == colFrame
->GetContent(),
303 "How did that happen??");
305 // else colFrame is being removed because of a frame
306 // reconstruct on it, and its style context is still the old
307 // one, so we can't assert anything about how it compares to
308 // col's style context.
310 nextCol
= col
->GetNextCol();
311 RemoveFrame(kPrincipalList
, col
);
316 int32_t colIndex
= colFrame
->GetColIndex();
317 // The RemoveChild call handles calling FrameNeedsReflow on us.
318 RemoveChild(*colFrame
, true);
320 nsTableFrame
* tableFrame
= nsTableFrame::GetTableFrame(this);
321 tableFrame
->RemoveCol(this, colIndex
, true, true);
322 if (mFrames
.IsEmpty() && contentRemoval
&&
323 GetColType() == eColGroupContent
) {
324 tableFrame
->AppendAnonymousColFrames(this, GetSpan(),
325 eColAnonymousColGroup
, true);
329 mFrames
.DestroyFrame(aOldFrame
);
333 nsIFrame::LogicalSides
334 nsTableColGroupFrame::GetLogicalSkipSides(const nsHTMLReflowState
* aReflowState
) const
336 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak
==
337 NS_STYLE_BOX_DECORATION_BREAK_CLONE
)) {
338 return LogicalSides();
342 if (nullptr != GetPrevInFlow()) {
343 skip
|= eLogicalSideBitsBStart
;
345 if (nullptr != GetNextInFlow()) {
346 skip
|= eLogicalSideBitsBEnd
;
352 nsTableColGroupFrame::Reflow(nsPresContext
* aPresContext
,
353 nsHTMLReflowMetrics
& aDesiredSize
,
354 const nsHTMLReflowState
& aReflowState
,
355 nsReflowStatus
& aStatus
)
357 DO_GLOBAL_REFLOW_COUNT("nsTableColGroupFrame");
358 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
359 NS_ASSERTION(nullptr!=mContent
, "bad state -- null content for frame");
361 const nsStyleVisibility
* groupVis
= StyleVisibility();
362 bool collapseGroup
= (NS_STYLE_VISIBILITY_COLLAPSE
== groupVis
->mVisible
);
364 nsTableFrame
* tableFrame
= nsTableFrame::GetTableFrame(this);
365 tableFrame
->SetNeedToCollapse(true);
367 // for every content child that (is a column thingy and does not already have a frame)
368 // create a frame and adjust it's style
370 for (nsIFrame
*kidFrame
= mFrames
.FirstChild(); kidFrame
;
371 kidFrame
= kidFrame
->GetNextSibling()) {
372 // Give the child frame a chance to reflow, even though we know it'll have 0 size
373 nsHTMLReflowMetrics
kidSize(aReflowState
);
374 nsHTMLReflowState
kidReflowState(aPresContext
, aReflowState
, kidFrame
,
375 LogicalSize(kidFrame
->GetWritingMode()));
377 nsReflowStatus status
;
378 ReflowChild(kidFrame
, aPresContext
, kidSize
, kidReflowState
, 0, 0, 0, status
);
379 FinishReflowChild(kidFrame
, aPresContext
, kidSize
, nullptr, 0, 0, 0);
382 aDesiredSize
.ClearSize();
383 aStatus
= NS_FRAME_COMPLETE
;
384 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
387 nsTableColFrame
* nsTableColGroupFrame::GetFirstColumn()
389 return GetNextColumn(nullptr);
392 nsTableColFrame
* nsTableColGroupFrame::GetNextColumn(nsIFrame
*aChildFrame
)
394 nsTableColFrame
*result
= nullptr;
395 nsIFrame
*childFrame
= aChildFrame
;
397 childFrame
= mFrames
.FirstChild();
400 childFrame
= childFrame
->GetNextSibling();
404 if (NS_STYLE_DISPLAY_TABLE_COLUMN
==
405 childFrame
->StyleDisplay()->mDisplay
)
407 result
= (nsTableColFrame
*)childFrame
;
410 childFrame
= childFrame
->GetNextSibling();
415 int32_t nsTableColGroupFrame::GetSpan()
417 return StyleTable()->mSpan
;
420 void nsTableColGroupFrame::SetContinuousBCBorderWidth(uint8_t aForSide
,
421 BCPixelSize aPixelValue
)
425 mTopContBorderWidth
= aPixelValue
;
428 mBottomContBorderWidth
= aPixelValue
;
431 NS_ERROR("invalid side arg");
435 void nsTableColGroupFrame::GetContinuousBCBorderWidth(nsMargin
& aBorder
)
437 int32_t aPixelsToTwips
= nsPresContext::AppUnitsPerCSSPixel();
438 nsTableFrame
* table
= nsTableFrame::GetTableFrame(this);
439 nsTableColFrame
* col
= table
->GetColFrame(mStartColIndex
+ mColCount
- 1);
440 col
->GetContinuousBCBorderWidth(aBorder
);
441 aBorder
.top
= BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips
,
442 mTopContBorderWidth
);
443 aBorder
.bottom
= BC_BORDER_TOP_HALF_COORD(aPixelsToTwips
,
444 mBottomContBorderWidth
);
447 /* ----- global methods ----- */
449 nsTableColGroupFrame
*
450 NS_NewTableColGroupFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
452 return new (aPresShell
) nsTableColGroupFrame(aContext
);
455 NS_IMPL_FRAMEARENA_HELPERS(nsTableColGroupFrame
)
458 nsTableColGroupFrame::GetType() const
460 return nsGkAtoms::tableColGroupFrame
;
464 nsTableColGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey
)
466 nsIFrame::InvalidateFrame(aDisplayItemKey
);
467 GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey
);
471 nsTableColGroupFrame::InvalidateFrameWithRect(const nsRect
& aRect
, uint32_t aDisplayItemKey
)
473 nsIFrame::InvalidateFrameWithRect(aRect
, aDisplayItemKey
);
474 // If we have filters applied that would affects our bounds, then
475 // we get an inactive layer created and this is computed
476 // within FrameLayerBuilder
477 GetParent()->InvalidateFrameWithRect(aRect
+ GetPosition(), aDisplayItemKey
);
480 #ifdef DEBUG_FRAME_DUMP
482 nsTableColGroupFrame::GetFrameName(nsAString
& aResult
) const
484 return MakeFrameName(NS_LITERAL_STRING("TableColGroup"), aResult
);
487 void nsTableColGroupFrame::Dump(int32_t aIndent
)
489 char* indent
= new char[aIndent
+ 1];
491 for (int32_t i
= 0; i
< aIndent
+ 1; i
++) {
496 printf("%s**START COLGROUP DUMP**\n%s startcolIndex=%d colcount=%d span=%d coltype=",
497 indent
, indent
, GetStartColumnIndex(), GetColCount(), GetSpan());
498 nsTableColGroupType colType
= GetColType();
500 case eColGroupContent
:
503 case eColGroupAnonymousCol
:
504 printf(" anonymous-column ");
506 case eColGroupAnonymousCell
:
507 printf(" anonymous-cell ");
510 // verify the colindices
511 int32_t j
= GetStartColumnIndex();
512 nsTableColFrame
* col
= GetFirstColumn();
514 NS_ASSERTION(j
== col
->GetColIndex(), "wrong colindex on col frame");
515 col
= col
->GetNextCol();
518 NS_ASSERTION((j
- GetStartColumnIndex()) == GetColCount(),
519 "number of cols out of sync");
520 printf("\n%s**END COLGROUP DUMP** ", indent
);