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