Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/Likely.h"
8 #include "mozilla/MathAlgorithms.h"
10 #include "nsCOMPtr.h"
11 #include "nsTableFrame.h"
12 #include "nsRenderingContext.h"
13 #include "nsStyleContext.h"
14 #include "nsStyleConsts.h"
15 #include "nsIContent.h"
16 #include "nsCellMap.h"
17 #include "nsTableCellFrame.h"
18 #include "nsHTMLParts.h"
19 #include "nsTableColFrame.h"
20 #include "nsTableColGroupFrame.h"
21 #include "nsTableRowFrame.h"
22 #include "nsTableRowGroupFrame.h"
23 #include "nsTableOuterFrame.h"
24 #include "nsTablePainter.h"
26 #include "BasicTableLayoutStrategy.h"
27 #include "FixedTableLayoutStrategy.h"
29 #include "nsPresContext.h"
30 #include "nsContentUtils.h"
31 #include "nsCSSRendering.h"
32 #include "nsGkAtoms.h"
33 #include "nsCSSAnonBoxes.h"
34 #include "nsIPresShell.h"
35 #include "nsIDOMElement.h"
36 #include "nsIDOMHTMLElement.h"
37 #include "nsIScriptError.h"
38 #include "nsFrameManager.h"
39 #include "nsError.h"
40 #include "nsAutoPtr.h"
41 #include "nsCSSFrameConstructor.h"
42 #include "nsStyleSet.h"
43 #include "nsDisplayList.h"
44 #include "nsIScrollableFrame.h"
45 #include "nsCSSProps.h"
46 #include "RestyleTracker.h"
47 #include <algorithm>
49 using namespace mozilla;
50 using namespace mozilla::layout;
52 /********************************************************************************
53 ** nsTableReflowState **
54 ********************************************************************************/
56 struct nsTableReflowState {
58 // the real reflow state
59 const nsHTMLReflowState& reflowState;
61 // The table's available size
62 nsSize availSize;
64 // Stationary x-offset
65 nscoord x;
67 // Running y-offset
68 nscoord y;
70 nsTableReflowState(nsPresContext& aPresContext,
71 const nsHTMLReflowState& aReflowState,
72 nsTableFrame& aTableFrame,
73 nscoord aAvailWidth,
74 nscoord aAvailHeight)
75 : reflowState(aReflowState)
76 {
77 Init(aPresContext, aTableFrame, aAvailWidth, aAvailHeight);
78 }
80 void Init(nsPresContext& aPresContext,
81 nsTableFrame& aTableFrame,
82 nscoord aAvailWidth,
83 nscoord aAvailHeight)
84 {
85 nsTableFrame* table = static_cast<nsTableFrame*>(aTableFrame.FirstInFlow());
86 nsMargin borderPadding = table->GetChildAreaOffset(&reflowState);
87 nscoord cellSpacingX = table->GetCellSpacingX();
89 x = borderPadding.left + cellSpacingX;
90 y = borderPadding.top; //cellspacing added during reflow
92 availSize.width = aAvailWidth;
93 if (NS_UNCONSTRAINEDSIZE != availSize.width) {
94 availSize.width -= borderPadding.left + borderPadding.right
95 + (2 * cellSpacingX);
96 availSize.width = std::max(0, availSize.width);
97 }
99 availSize.height = aAvailHeight;
100 if (NS_UNCONSTRAINEDSIZE != availSize.height) {
101 availSize.height -= borderPadding.top + borderPadding.bottom
102 + (2 * table->GetCellSpacingY());
103 availSize.height = std::max(0, availSize.height);
104 }
105 }
107 nsTableReflowState(nsPresContext& aPresContext,
108 const nsHTMLReflowState& aReflowState,
109 nsTableFrame& aTableFrame)
110 : reflowState(aReflowState)
111 {
112 Init(aPresContext, aTableFrame, aReflowState.AvailableWidth(), aReflowState.AvailableHeight());
113 }
115 };
117 /********************************************************************************
118 ** nsTableFrame **
119 ********************************************************************************/
121 struct BCPropertyData
122 {
123 BCPropertyData() : mTopBorderWidth(0), mRightBorderWidth(0),
124 mBottomBorderWidth(0), mLeftBorderWidth(0),
125 mLeftCellBorderWidth(0), mRightCellBorderWidth(0) {}
126 nsIntRect mDamageArea;
127 BCPixelSize mTopBorderWidth;
128 BCPixelSize mRightBorderWidth;
129 BCPixelSize mBottomBorderWidth;
130 BCPixelSize mLeftBorderWidth;
131 BCPixelSize mLeftCellBorderWidth;
132 BCPixelSize mRightCellBorderWidth;
133 };
135 nsIFrame*
136 nsTableFrame::GetParentStyleContextFrame() const
137 {
138 // Since our parent, the table outer frame, returned this frame, we
139 // must return whatever our parent would normally have returned.
141 NS_PRECONDITION(mParent, "table constructed without outer table");
142 if (!mContent->GetParent() && !StyleContext()->GetPseudo()) {
143 // We're the root. We have no style context parent.
144 return nullptr;
145 }
147 return static_cast<nsFrame*>(GetParent())->DoGetParentStyleContextFrame();
148 }
151 nsIAtom*
152 nsTableFrame::GetType() const
153 {
154 return nsGkAtoms::tableFrame;
155 }
158 nsTableFrame::nsTableFrame(nsStyleContext* aContext)
159 : nsContainerFrame(aContext),
160 mCellMap(nullptr),
161 mTableLayoutStrategy(nullptr)
162 {
163 memset(&mBits, 0, sizeof(mBits));
164 }
166 void
167 nsTableFrame::Init(nsIContent* aContent,
168 nsIFrame* aParent,
169 nsIFrame* aPrevInFlow)
170 {
171 NS_PRECONDITION(!mCellMap, "Init called twice");
172 NS_PRECONDITION(!aPrevInFlow ||
173 aPrevInFlow->GetType() == nsGkAtoms::tableFrame,
174 "prev-in-flow must be of same type");
176 // Let the base class do its processing
177 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
179 // see if border collapse is on, if so set it
180 const nsStyleTableBorder* tableStyle = StyleTableBorder();
181 bool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse);
182 SetBorderCollapse(borderCollapse);
184 // Create the cell map if this frame is the first-in-flow.
185 if (!aPrevInFlow) {
186 mCellMap = new nsTableCellMap(*this, borderCollapse);
187 }
189 if (aPrevInFlow) {
190 // set my width, because all frames in a table flow are the same width and
191 // code in nsTableOuterFrame depends on this being set
192 mRect.width = aPrevInFlow->GetSize().width;
193 }
194 else {
195 NS_ASSERTION(!mTableLayoutStrategy, "strategy was created before Init was called");
196 // create the strategy
197 if (IsAutoLayout())
198 mTableLayoutStrategy = new BasicTableLayoutStrategy(this);
199 else
200 mTableLayoutStrategy = new FixedTableLayoutStrategy(this);
201 }
202 }
204 nsTableFrame::~nsTableFrame()
205 {
206 delete mCellMap;
207 delete mTableLayoutStrategy;
208 }
210 void
211 nsTableFrame::DestroyFrom(nsIFrame* aDestructRoot)
212 {
213 mColGroups.DestroyFramesFrom(aDestructRoot);
214 nsContainerFrame::DestroyFrom(aDestructRoot);
215 }
217 // Make sure any views are positioned properly
218 void
219 nsTableFrame::RePositionViews(nsIFrame* aFrame)
220 {
221 nsContainerFrame::PositionFrameView(aFrame);
222 nsContainerFrame::PositionChildViews(aFrame);
223 }
225 static bool
226 IsRepeatedFrame(nsIFrame* kidFrame)
227 {
228 return (kidFrame->GetType() == nsGkAtoms::tableRowFrame ||
229 kidFrame->GetType() == nsGkAtoms::tableRowGroupFrame) &&
230 (kidFrame->GetStateBits() & NS_REPEATED_ROW_OR_ROWGROUP);
231 }
233 bool
234 nsTableFrame::PageBreakAfter(nsIFrame* aSourceFrame,
235 nsIFrame* aNextFrame)
236 {
237 const nsStyleDisplay* display = aSourceFrame->StyleDisplay();
238 nsTableRowGroupFrame* prevRg = do_QueryFrame(aSourceFrame);
239 // don't allow a page break after a repeated element ...
240 if ((display->mBreakAfter || (prevRg && prevRg->HasInternalBreakAfter())) &&
241 !IsRepeatedFrame(aSourceFrame)) {
242 return !(aNextFrame && IsRepeatedFrame(aNextFrame)); // or before
243 }
245 if (aNextFrame) {
246 display = aNextFrame->StyleDisplay();
247 // don't allow a page break before a repeated element ...
248 nsTableRowGroupFrame* nextRg = do_QueryFrame(aNextFrame);
249 if ((display->mBreakBefore ||
250 (nextRg && nextRg->HasInternalBreakBefore())) &&
251 !IsRepeatedFrame(aNextFrame)) {
252 return !IsRepeatedFrame(aSourceFrame); // or after
253 }
254 }
255 return false;
256 }
258 typedef nsTArray<nsIFrame*> FrameTArray;
260 /* static */ void
261 nsTableFrame::DestroyPositionedTablePartArray(void* aPropertyValue)
262 {
263 auto positionedObjs = static_cast<FrameTArray*>(aPropertyValue);
264 delete positionedObjs;
265 }
267 /* static */ void
268 nsTableFrame::RegisterPositionedTablePart(nsIFrame* aFrame)
269 {
270 // Supporting relative positioning for table parts other than table cells has
271 // the potential to break sites that apply 'position: relative' to those
272 // parts, expecting nothing to happen. We warn at the console to make tracking
273 // down the issue easy.
274 if (nsGkAtoms::tableCellFrame != aFrame->GetType()) {
275 nsIContent* content = aFrame->GetContent();
276 nsPresContext* presContext = aFrame->PresContext();
277 if (content && !presContext->HasWarnedAboutPositionedTableParts()) {
278 presContext->SetHasWarnedAboutPositionedTableParts();
279 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
280 NS_LITERAL_CSTRING("Layout: Tables"),
281 content->OwnerDoc(),
282 nsContentUtils::eLAYOUT_PROPERTIES,
283 "TablePartRelPosWarning");
284 }
285 }
287 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(aFrame);
288 MOZ_ASSERT(tableFrame, "Should have a table frame here");
289 tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
291 // Retrieve the positioned parts array for this table.
292 FrameProperties props = tableFrame->Properties();
293 auto positionedParts =
294 static_cast<FrameTArray*>(props.Get(PositionedTablePartArray()));
296 // Lazily create the array if it doesn't exist yet.
297 if (!positionedParts) {
298 positionedParts = new FrameTArray;
299 props.Set(PositionedTablePartArray(), positionedParts);
300 }
302 // Add this frame to the list.
303 positionedParts->AppendElement(aFrame);
304 }
306 /* static */ void
307 nsTableFrame::UnregisterPositionedTablePart(nsIFrame* aFrame,
308 nsIFrame* aDestructRoot)
309 {
310 // Retrieve the table frame, and ensure that we hit aDestructRoot on the way.
311 // If we don't, that means that the table frame will be destroyed, so we don't
312 // need to bother with unregistering this frame.
313 nsTableFrame* tableFrame = GetTableFramePassingThrough(aDestructRoot, aFrame);
314 if (!tableFrame) {
315 return;
316 }
317 tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
319 // Retrieve the positioned parts array for this table.
320 FrameProperties props = tableFrame->Properties();
321 auto positionedParts =
322 static_cast<FrameTArray*>(props.Get(PositionedTablePartArray()));
324 // Remove the frame.
325 MOZ_ASSERT(positionedParts &&
326 positionedParts->IndexOf(aFrame) != FrameTArray::NoIndex,
327 "Asked to unregister a positioned table part that wasn't registered");
328 if (positionedParts) {
329 positionedParts->RemoveElement(aFrame);
330 }
331 }
333 // XXX this needs to be cleaned up so that the frame constructor breaks out col group
334 // frames into a separate child list, bug 343048.
335 nsresult
336 nsTableFrame::SetInitialChildList(ChildListID aListID,
337 nsFrameList& aChildList)
338 {
340 if (!mFrames.IsEmpty() || !mColGroups.IsEmpty()) {
341 // We already have child frames which means we've already been
342 // initialized
343 NS_NOTREACHED("unexpected second call to SetInitialChildList");
344 return NS_ERROR_UNEXPECTED;
345 }
346 if (aListID != kPrincipalList) {
347 // All we know about is the principal child list.
348 NS_NOTREACHED("unknown frame list");
349 return NS_ERROR_INVALID_ARG;
350 }
352 // XXXbz the below code is an icky cesspit that's only needed in its current
353 // form for two reasons:
354 // 1) Both rowgroups and column groups come in on the principal child list.
355 while (aChildList.NotEmpty()) {
356 nsIFrame* childFrame = aChildList.FirstChild();
357 aChildList.RemoveFirstChild();
358 const nsStyleDisplay* childDisplay = childFrame->StyleDisplay();
360 if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == childDisplay->mDisplay) {
361 NS_ASSERTION(nsGkAtoms::tableColGroupFrame == childFrame->GetType(),
362 "This is not a colgroup");
363 mColGroups.AppendFrame(nullptr, childFrame);
364 }
365 else { // row groups and unknown frames go on the main list for now
366 mFrames.AppendFrame(nullptr, childFrame);
367 }
368 }
370 // If we have a prev-in-flow, then we're a table that has been split and
371 // so don't treat this like an append
372 if (!GetPrevInFlow()) {
373 // process col groups first so that real cols get constructed before
374 // anonymous ones due to cells in rows.
375 InsertColGroups(0, mColGroups);
376 InsertRowGroups(mFrames);
377 // calc collapsing borders
378 if (IsBorderCollapse()) {
379 SetFullBCDamageArea();
380 }
381 }
383 return NS_OK;
384 }
386 void nsTableFrame::AttributeChangedFor(nsIFrame* aFrame,
387 nsIContent* aContent,
388 nsIAtom* aAttribute)
389 {
390 nsTableCellFrame *cellFrame = do_QueryFrame(aFrame);
391 if (cellFrame) {
392 if ((nsGkAtoms::rowspan == aAttribute) ||
393 (nsGkAtoms::colspan == aAttribute)) {
394 nsTableCellMap* cellMap = GetCellMap();
395 if (cellMap) {
396 // for now just remove the cell from the map and reinsert it
397 int32_t rowIndex, colIndex;
398 cellFrame->GetRowIndex(rowIndex);
399 cellFrame->GetColIndex(colIndex);
400 RemoveCell(cellFrame, rowIndex);
401 nsAutoTArray<nsTableCellFrame*, 1> cells;
402 cells.AppendElement(cellFrame);
403 InsertCells(cells, rowIndex, colIndex - 1);
405 // XXX Should this use eStyleChange? It currently doesn't need
406 // to, but it might given more optimization.
407 PresContext()->PresShell()->
408 FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
409 }
410 }
411 }
412 }
415 /* ****** CellMap methods ******* */
417 /* return the effective col count */
418 int32_t nsTableFrame::GetEffectiveColCount() const
419 {
420 int32_t colCount = GetColCount();
421 if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto) {
422 nsTableCellMap* cellMap = GetCellMap();
423 if (!cellMap) {
424 return 0;
425 }
426 // don't count cols at the end that don't have originating cells
427 for (int32_t colX = colCount - 1; colX >= 0; colX--) {
428 if (cellMap->GetNumCellsOriginatingInCol(colX) > 0) {
429 break;
430 }
431 colCount--;
432 }
433 }
434 return colCount;
435 }
437 int32_t nsTableFrame::GetIndexOfLastRealCol()
438 {
439 int32_t numCols = mColFrames.Length();
440 if (numCols > 0) {
441 for (int32_t colX = numCols - 1; colX >= 0; colX--) {
442 nsTableColFrame* colFrame = GetColFrame(colX);
443 if (colFrame) {
444 if (eColAnonymousCell != colFrame->GetColType()) {
445 return colX;
446 }
447 }
448 }
449 }
450 return -1;
451 }
453 nsTableColFrame*
454 nsTableFrame::GetColFrame(int32_t aColIndex) const
455 {
456 NS_ASSERTION(!GetPrevInFlow(), "GetColFrame called on next in flow");
457 int32_t numCols = mColFrames.Length();
458 if ((aColIndex >= 0) && (aColIndex < numCols)) {
459 return mColFrames.ElementAt(aColIndex);
460 }
461 else {
462 NS_ERROR("invalid col index");
463 return nullptr;
464 }
465 }
467 int32_t nsTableFrame::GetEffectiveRowSpan(int32_t aRowIndex,
468 const nsTableCellFrame& aCell) const
469 {
470 nsTableCellMap* cellMap = GetCellMap();
471 NS_PRECONDITION (nullptr != cellMap, "bad call, cellMap not yet allocated.");
473 int32_t colIndex;
474 aCell.GetColIndex(colIndex);
475 return cellMap->GetEffectiveRowSpan(aRowIndex, colIndex);
476 }
478 int32_t nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell,
479 nsCellMap* aCellMap)
480 {
481 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
483 int32_t colIndex, rowIndex;
484 aCell.GetColIndex(colIndex);
485 aCell.GetRowIndex(rowIndex);
487 if (aCellMap)
488 return aCellMap->GetRowSpan(rowIndex, colIndex, true);
489 else
490 return tableCellMap->GetEffectiveRowSpan(rowIndex, colIndex);
491 }
493 int32_t nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell,
494 nsCellMap* aCellMap) const
495 {
496 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
498 int32_t colIndex, rowIndex;
499 aCell.GetColIndex(colIndex);
500 aCell.GetRowIndex(rowIndex);
501 bool ignore;
503 if (aCellMap)
504 return aCellMap->GetEffectiveColSpan(*tableCellMap, rowIndex, colIndex, ignore);
505 else
506 return tableCellMap->GetEffectiveColSpan(rowIndex, colIndex);
507 }
509 bool nsTableFrame::HasMoreThanOneCell(int32_t aRowIndex) const
510 {
511 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
512 return tableCellMap->HasMoreThanOneCell(aRowIndex);
513 }
515 void nsTableFrame::AdjustRowIndices(int32_t aRowIndex,
516 int32_t aAdjustment)
517 {
518 // Iterate over the row groups and adjust the row indices of all rows
519 // whose index is >= aRowIndex.
520 RowGroupArray rowGroups;
521 OrderRowGroups(rowGroups);
523 for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
524 rowGroups[rgX]->AdjustRowIndices(aRowIndex, aAdjustment);
525 }
526 }
529 void nsTableFrame::ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude)
530 {
531 // Iterate over the row groups and adjust the row indices of all rows
532 // omit the rowgroups that will be inserted later
533 RowGroupArray rowGroups;
534 OrderRowGroups(rowGroups);
536 int32_t rowIndex = 0;
537 nsTHashtable<nsPtrHashKey<nsTableRowGroupFrame> > excludeRowGroups;
538 nsFrameList::Enumerator excludeRowGroupsEnumerator(aRowGroupsToExclude);
539 while (!excludeRowGroupsEnumerator.AtEnd()) {
540 excludeRowGroups.PutEntry(static_cast<nsTableRowGroupFrame*>(excludeRowGroupsEnumerator.get()));
541 excludeRowGroupsEnumerator.Next();
542 }
544 for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
545 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
546 if (!excludeRowGroups.GetEntry(rgFrame)) {
547 const nsFrameList& rowFrames = rgFrame->PrincipalChildList();
548 for (nsFrameList::Enumerator rows(rowFrames); !rows.AtEnd(); rows.Next()) {
549 if (NS_STYLE_DISPLAY_TABLE_ROW==rows.get()->StyleDisplay()->mDisplay) {
550 ((nsTableRowFrame *)rows.get())->SetRowIndex(rowIndex);
551 rowIndex++;
552 }
553 }
554 }
555 }
556 }
557 void nsTableFrame::InsertColGroups(int32_t aStartColIndex,
558 const nsFrameList::Slice& aColGroups)
559 {
560 int32_t colIndex = aStartColIndex;
561 nsFrameList::Enumerator colGroups(aColGroups);
562 for (; !colGroups.AtEnd(); colGroups.Next()) {
563 MOZ_ASSERT(colGroups.get()->GetType() == nsGkAtoms::tableColGroupFrame);
564 nsTableColGroupFrame* cgFrame =
565 static_cast<nsTableColGroupFrame*>(colGroups.get());
566 cgFrame->SetStartColumnIndex(colIndex);
567 // XXXbz this sucks. AddColsToTable will actually remove colgroups from
568 // the list we're traversing! Need to fix things here. :( I guess this is
569 // why the old code used pointer-to-last-frame as opposed to
570 // pointer-to-frame-after-last....
572 // How about dealing with this by storing a const reference to the
573 // mNextSibling of the framelist's last frame, instead of storing a pointer
574 // to the first-after-next frame? Will involve making nsFrameList friend
575 // of nsIFrame, but it's time for that anyway.
576 cgFrame->AddColsToTable(colIndex, false,
577 colGroups.get()->PrincipalChildList());
578 int32_t numCols = cgFrame->GetColCount();
579 colIndex += numCols;
580 }
582 nsFrameList::Enumerator remainingColgroups = colGroups.GetUnlimitedEnumerator();
583 if (!remainingColgroups.AtEnd()) {
584 nsTableColGroupFrame::ResetColIndices(
585 static_cast<nsTableColGroupFrame*>(remainingColgroups.get()), colIndex);
586 }
587 }
589 void nsTableFrame::InsertCol(nsTableColFrame& aColFrame,
590 int32_t aColIndex)
591 {
592 mColFrames.InsertElementAt(aColIndex, &aColFrame);
593 nsTableColType insertedColType = aColFrame.GetColType();
594 int32_t numCacheCols = mColFrames.Length();
595 nsTableCellMap* cellMap = GetCellMap();
596 if (cellMap) {
597 int32_t numMapCols = cellMap->GetColCount();
598 if (numCacheCols > numMapCols) {
599 bool removedFromCache = false;
600 if (eColAnonymousCell != insertedColType) {
601 nsTableColFrame* lastCol = mColFrames.ElementAt(numCacheCols - 1);
602 if (lastCol) {
603 nsTableColType lastColType = lastCol->GetColType();
604 if (eColAnonymousCell == lastColType) {
605 // remove the col from the cache
606 mColFrames.RemoveElementAt(numCacheCols - 1);
607 // remove the col from the eColGroupAnonymousCell col group
608 nsTableColGroupFrame* lastColGroup = (nsTableColGroupFrame *)mColGroups.LastChild();
609 if (lastColGroup) {
610 lastColGroup->RemoveChild(*lastCol, false);
612 // remove the col group if it is empty
613 if (lastColGroup->GetColCount() <= 0) {
614 mColGroups.DestroyFrame((nsIFrame*)lastColGroup);
615 }
616 }
617 removedFromCache = true;
618 }
619 }
620 }
621 if (!removedFromCache) {
622 cellMap->AddColsAtEnd(1);
623 }
624 }
625 }
626 // for now, just bail and recalc all of the collapsing borders
627 if (IsBorderCollapse()) {
628 nsIntRect damageArea(aColIndex, 0, 1, GetRowCount());
629 AddBCDamageArea(damageArea);
630 }
631 }
633 void nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame,
634 int32_t aColIndex,
635 bool aRemoveFromCache,
636 bool aRemoveFromCellMap)
637 {
638 if (aRemoveFromCache) {
639 mColFrames.RemoveElementAt(aColIndex);
640 }
641 if (aRemoveFromCellMap) {
642 nsTableCellMap* cellMap = GetCellMap();
643 if (cellMap) {
644 AppendAnonymousColFrames(1);
645 }
646 }
647 // for now, just bail and recalc all of the collapsing borders
648 if (IsBorderCollapse()) {
649 nsIntRect damageArea(0, 0, GetColCount(), GetRowCount());
650 AddBCDamageArea(damageArea);
651 }
652 }
654 /** Get the cell map for this table frame. It is not always mCellMap.
655 * Only the first-in-flow has a legit cell map.
656 */
657 nsTableCellMap*
658 nsTableFrame::GetCellMap() const
659 {
660 return static_cast<nsTableFrame*>(FirstInFlow())->mCellMap;
661 }
663 // XXX this needs to be moved to nsCSSFrameConstructor
664 nsTableColGroupFrame*
665 nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType)
666 {
667 nsIContent* colGroupContent = GetContent();
668 nsPresContext* presContext = PresContext();
669 nsIPresShell *shell = presContext->PresShell();
671 nsRefPtr<nsStyleContext> colGroupStyle;
672 colGroupStyle = shell->StyleSet()->
673 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup, mStyleContext);
674 // Create a col group frame
675 nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle);
676 ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType);
677 newFrame->Init(colGroupContent, this, nullptr);
678 return (nsTableColGroupFrame *)newFrame;
679 }
681 void
682 nsTableFrame::AppendAnonymousColFrames(int32_t aNumColsToAdd)
683 {
684 // get the last col group frame
685 nsTableColGroupFrame* colGroupFrame =
686 static_cast<nsTableColGroupFrame*>(mColGroups.LastChild());
688 if (!colGroupFrame ||
689 (colGroupFrame->GetColType() != eColGroupAnonymousCell)) {
690 int32_t colIndex = (colGroupFrame) ?
691 colGroupFrame->GetStartColumnIndex() +
692 colGroupFrame->GetColCount() : 0;
693 colGroupFrame = CreateAnonymousColGroupFrame(eColGroupAnonymousCell);
694 if (!colGroupFrame) {
695 return;
696 }
697 // add the new frame to the child list
698 mColGroups.AppendFrame(this, colGroupFrame);
699 colGroupFrame->SetStartColumnIndex(colIndex);
700 }
701 AppendAnonymousColFrames(colGroupFrame, aNumColsToAdd, eColAnonymousCell,
702 true);
704 }
706 // XXX this needs to be moved to nsCSSFrameConstructor
707 // Right now it only creates the col frames at the end
708 void
709 nsTableFrame::AppendAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame,
710 int32_t aNumColsToAdd,
711 nsTableColType aColType,
712 bool aAddToTable)
713 {
714 NS_PRECONDITION(aColGroupFrame, "null frame");
715 NS_PRECONDITION(aColType != eColAnonymousCol, "Shouldn't happen");
717 nsIPresShell *shell = PresContext()->PresShell();
719 // Get the last col frame
720 nsFrameList newColFrames;
722 int32_t startIndex = mColFrames.Length();
723 int32_t lastIndex = startIndex + aNumColsToAdd - 1;
725 for (int32_t childX = startIndex; childX <= lastIndex; childX++) {
726 nsIContent* iContent;
727 nsRefPtr<nsStyleContext> styleContext;
728 nsStyleContext* parentStyleContext;
730 // all anonymous cols that we create here use a pseudo style context of the
731 // col group
732 iContent = aColGroupFrame->GetContent();
733 parentStyleContext = aColGroupFrame->StyleContext();
734 styleContext = shell->StyleSet()->
735 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableCol, parentStyleContext);
736 // ASSERTION to check for bug 54454 sneaking back in...
737 NS_ASSERTION(iContent, "null content in CreateAnonymousColFrames");
739 // create the new col frame
740 nsIFrame* colFrame = NS_NewTableColFrame(shell, styleContext);
741 ((nsTableColFrame *) colFrame)->SetColType(aColType);
742 colFrame->Init(iContent, aColGroupFrame, nullptr);
744 newColFrames.AppendFrame(nullptr, colFrame);
745 }
746 nsFrameList& cols = aColGroupFrame->GetWritableChildList();
747 nsIFrame* oldLastCol = cols.LastChild();
748 const nsFrameList::Slice& newCols =
749 cols.InsertFrames(nullptr, oldLastCol, newColFrames);
750 if (aAddToTable) {
751 // get the starting col index in the cache
752 int32_t startColIndex;
753 if (oldLastCol) {
754 startColIndex =
755 static_cast<nsTableColFrame*>(oldLastCol)->GetColIndex() + 1;
756 } else {
757 startColIndex = aColGroupFrame->GetStartColumnIndex();
758 }
760 aColGroupFrame->AddColsToTable(startColIndex, true, newCols);
761 }
762 }
764 void
765 nsTableFrame::MatchCellMapToColCache(nsTableCellMap* aCellMap)
766 {
767 int32_t numColsInMap = GetColCount();
768 int32_t numColsInCache = mColFrames.Length();
769 int32_t numColsToAdd = numColsInMap - numColsInCache;
770 if (numColsToAdd > 0) {
771 // this sets the child list, updates the col cache and cell map
772 AppendAnonymousColFrames(numColsToAdd);
773 }
774 if (numColsToAdd < 0) {
775 int32_t numColsNotRemoved = DestroyAnonymousColFrames(-numColsToAdd);
776 // if the cell map has fewer cols than the cache, correct it
777 if (numColsNotRemoved > 0) {
778 aCellMap->AddColsAtEnd(numColsNotRemoved);
779 }
780 }
781 if (numColsToAdd && HasZeroColSpans()) {
782 SetNeedColSpanExpansion(true);
783 }
784 if (NeedColSpanExpansion()) {
785 // This flag can be set in two ways -- either by changing
786 // the number of columns (that happens in the block above),
787 // or by adding a cell with colspan="0" to the cellmap. To
788 // handle the latter case we need to explicitly check the
789 // flag here -- it may be set even if the number of columns
790 // did not change.
791 //
792 // @see nsCellMap::AppendCell
794 aCellMap->ExpandZeroColSpans();
795 }
796 }
798 void
799 nsTableFrame::DidResizeColumns()
800 {
801 NS_PRECONDITION(!GetPrevInFlow(),
802 "should only be called on first-in-flow");
803 if (mBits.mResizedColumns)
804 return; // already marked
806 for (nsTableFrame *f = this; f;
807 f = static_cast<nsTableFrame*>(f->GetNextInFlow()))
808 f->mBits.mResizedColumns = true;
809 }
811 void
812 nsTableFrame::AppendCell(nsTableCellFrame& aCellFrame,
813 int32_t aRowIndex)
814 {
815 nsTableCellMap* cellMap = GetCellMap();
816 if (cellMap) {
817 nsIntRect damageArea(0,0,0,0);
818 cellMap->AppendCell(aCellFrame, aRowIndex, true, damageArea);
819 MatchCellMapToColCache(cellMap);
820 if (IsBorderCollapse()) {
821 AddBCDamageArea(damageArea);
822 }
823 }
824 }
826 void nsTableFrame::InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames,
827 int32_t aRowIndex,
828 int32_t aColIndexBefore)
829 {
830 nsTableCellMap* cellMap = GetCellMap();
831 if (cellMap) {
832 nsIntRect damageArea(0,0,0,0);
833 cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea);
834 MatchCellMapToColCache(cellMap);
835 if (IsBorderCollapse()) {
836 AddBCDamageArea(damageArea);
837 }
838 }
839 }
841 // this removes the frames from the col group and table, but not the cell map
842 int32_t
843 nsTableFrame::DestroyAnonymousColFrames(int32_t aNumFrames)
844 {
845 // only remove cols that are of type eTypeAnonymous cell (they are at the end)
846 int32_t endIndex = mColFrames.Length() - 1;
847 int32_t startIndex = (endIndex - aNumFrames) + 1;
848 int32_t numColsRemoved = 0;
849 for (int32_t colX = endIndex; colX >= startIndex; colX--) {
850 nsTableColFrame* colFrame = GetColFrame(colX);
851 if (colFrame && (eColAnonymousCell == colFrame->GetColType())) {
852 nsTableColGroupFrame* cgFrame =
853 static_cast<nsTableColGroupFrame*>(colFrame->GetParent());
854 // remove the frame from the colgroup
855 cgFrame->RemoveChild(*colFrame, false);
856 // remove the frame from the cache, but not the cell map
857 RemoveCol(nullptr, colX, true, false);
858 numColsRemoved++;
859 }
860 else {
861 break;
862 }
863 }
864 return (aNumFrames - numColsRemoved);
865 }
867 void nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame,
868 int32_t aRowIndex)
869 {
870 nsTableCellMap* cellMap = GetCellMap();
871 if (cellMap) {
872 nsIntRect damageArea(0,0,0,0);
873 cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea);
874 MatchCellMapToColCache(cellMap);
875 if (IsBorderCollapse()) {
876 AddBCDamageArea(damageArea);
877 }
878 }
879 }
881 int32_t
882 nsTableFrame::GetStartRowIndex(nsTableRowGroupFrame* aRowGroupFrame)
883 {
884 RowGroupArray orderedRowGroups;
885 OrderRowGroups(orderedRowGroups);
887 int32_t rowIndex = 0;
888 for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
889 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
890 if (rgFrame == aRowGroupFrame) {
891 break;
892 }
893 int32_t numRows = rgFrame->GetRowCount();
894 rowIndex += numRows;
895 }
896 return rowIndex;
897 }
899 // this cannot extend beyond a single row group
900 void nsTableFrame::AppendRows(nsTableRowGroupFrame* aRowGroupFrame,
901 int32_t aRowIndex,
902 nsTArray<nsTableRowFrame*>& aRowFrames)
903 {
904 nsTableCellMap* cellMap = GetCellMap();
905 if (cellMap) {
906 int32_t absRowIndex = GetStartRowIndex(aRowGroupFrame) + aRowIndex;
907 InsertRows(aRowGroupFrame, aRowFrames, absRowIndex, true);
908 }
909 }
911 // this cannot extend beyond a single row group
912 int32_t
913 nsTableFrame::InsertRows(nsTableRowGroupFrame* aRowGroupFrame,
914 nsTArray<nsTableRowFrame*>& aRowFrames,
915 int32_t aRowIndex,
916 bool aConsiderSpans)
917 {
918 #ifdef DEBUG_TABLE_CELLMAP
919 printf("=== insertRowsBefore firstRow=%d \n", aRowIndex);
920 Dump(true, false, true);
921 #endif
923 int32_t numColsToAdd = 0;
924 nsTableCellMap* cellMap = GetCellMap();
925 if (cellMap) {
926 nsIntRect damageArea(0,0,0,0);
927 int32_t origNumRows = cellMap->GetRowCount();
928 int32_t numNewRows = aRowFrames.Length();
929 cellMap->InsertRows(aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea);
930 MatchCellMapToColCache(cellMap);
931 if (aRowIndex < origNumRows) {
932 AdjustRowIndices(aRowIndex, numNewRows);
933 }
934 // assign the correct row indices to the new rows. If they were adjusted above
935 // it may not have been done correctly because each row is constructed with index 0
936 for (int32_t rowY = 0; rowY < numNewRows; rowY++) {
937 nsTableRowFrame* rowFrame = aRowFrames.ElementAt(rowY);
938 rowFrame->SetRowIndex(aRowIndex + rowY);
939 }
940 if (IsBorderCollapse()) {
941 AddBCDamageArea(damageArea);
942 }
943 }
944 #ifdef DEBUG_TABLE_CELLMAP
945 printf("=== insertRowsAfter \n");
946 Dump(true, false, true);
947 #endif
949 return numColsToAdd;
950 }
952 // this cannot extend beyond a single row group
953 void nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame,
954 int32_t aNumRowsToRemove,
955 bool aConsiderSpans)
956 {
957 #ifdef TBD_OPTIMIZATION
958 // decide if we need to rebalance. we have to do this here because the row group
959 // cannot do it when it gets the dirty reflow corresponding to the frame being destroyed
960 bool stopTelling = false;
961 for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking);
962 kidFrame = kidFrame->GetNextSibling()) {
963 nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
964 if (cellFrame) {
965 stopTelling = tableFrame->CellChangedWidth(*cellFrame, cellFrame->GetPass1MaxElementWidth(),
966 cellFrame->GetMaximumWidth(), true);
967 }
968 }
969 // XXX need to consider what happens if there are cells that have rowspans
970 // into the deleted row. Need to consider moving rows if a rebalance doesn't happen
971 #endif
973 int32_t firstRowIndex = aFirstRowFrame.GetRowIndex();
974 #ifdef DEBUG_TABLE_CELLMAP
975 printf("=== removeRowsBefore firstRow=%d numRows=%d\n", firstRowIndex, aNumRowsToRemove);
976 Dump(true, false, true);
977 #endif
978 nsTableCellMap* cellMap = GetCellMap();
979 if (cellMap) {
980 nsIntRect damageArea(0,0,0,0);
981 cellMap->RemoveRows(firstRowIndex, aNumRowsToRemove, aConsiderSpans, damageArea);
982 MatchCellMapToColCache(cellMap);
983 if (IsBorderCollapse()) {
984 AddBCDamageArea(damageArea);
985 }
986 }
987 AdjustRowIndices(firstRowIndex, -aNumRowsToRemove);
988 #ifdef DEBUG_TABLE_CELLMAP
989 printf("=== removeRowsAfter\n");
990 Dump(true, true, true);
991 #endif
992 }
994 // collect the rows ancestors of aFrame
995 int32_t
996 nsTableFrame::CollectRows(nsIFrame* aFrame,
997 nsTArray<nsTableRowFrame*>& aCollection)
998 {
999 NS_PRECONDITION(aFrame, "null frame");
1000 int32_t numRows = 0;
1001 nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
1002 while (childFrame) {
1003 aCollection.AppendElement(static_cast<nsTableRowFrame*>(childFrame));
1004 numRows++;
1005 childFrame = childFrame->GetNextSibling();
1006 }
1007 return numRows;
1008 }
1010 void
1011 nsTableFrame::InsertRowGroups(const nsFrameList::Slice& aRowGroups)
1012 {
1013 #ifdef DEBUG_TABLE_CELLMAP
1014 printf("=== insertRowGroupsBefore\n");
1015 Dump(true, false, true);
1016 #endif
1017 nsTableCellMap* cellMap = GetCellMap();
1018 if (cellMap) {
1019 RowGroupArray orderedRowGroups;
1020 OrderRowGroups(orderedRowGroups);
1022 nsAutoTArray<nsTableRowFrame*, 8> rows;
1023 // Loop over the rowgroups and check if some of them are new, if they are
1024 // insert cellmaps in the order that is predefined by OrderRowGroups,
1025 // XXXbz this code is O(N*M) where N is number of new rowgroups
1026 // and M is number of rowgroups we have!
1027 uint32_t rgIndex;
1028 for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
1029 for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd();
1030 rowgroups.Next()) {
1031 if (orderedRowGroups[rgIndex] == rowgroups.get()) {
1032 nsTableRowGroupFrame* priorRG =
1033 (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1];
1034 // create and add the cell map for the row group
1035 cellMap->InsertGroupCellMap(orderedRowGroups[rgIndex], priorRG);
1037 break;
1038 }
1039 }
1040 }
1041 cellMap->Synchronize(this);
1042 ResetRowIndices(aRowGroups);
1044 //now that the cellmaps are reordered too insert the rows
1045 for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
1046 for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd();
1047 rowgroups.Next()) {
1048 if (orderedRowGroups[rgIndex] == rowgroups.get()) {
1049 nsTableRowGroupFrame* priorRG =
1050 (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1];
1051 // collect the new row frames in an array and add them to the table
1052 int32_t numRows = CollectRows(rowgroups.get(), rows);
1053 if (numRows > 0) {
1054 int32_t rowIndex = 0;
1055 if (priorRG) {
1056 int32_t priorNumRows = priorRG->GetRowCount();
1057 rowIndex = priorRG->GetStartRowIndex() + priorNumRows;
1058 }
1059 InsertRows(orderedRowGroups[rgIndex], rows, rowIndex, true);
1060 rows.Clear();
1061 }
1062 break;
1063 }
1064 }
1065 }
1067 }
1068 #ifdef DEBUG_TABLE_CELLMAP
1069 printf("=== insertRowGroupsAfter\n");
1070 Dump(true, true, true);
1071 #endif
1072 }
1075 /////////////////////////////////////////////////////////////////////////////
1076 // Child frame enumeration
1078 const nsFrameList&
1079 nsTableFrame::GetChildList(ChildListID aListID) const
1080 {
1081 if (aListID == kColGroupList) {
1082 return mColGroups;
1083 }
1084 return nsContainerFrame::GetChildList(aListID);
1085 }
1087 void
1088 nsTableFrame::GetChildLists(nsTArray<ChildList>* aLists) const
1089 {
1090 nsContainerFrame::GetChildLists(aLists);
1091 mColGroups.AppendIfNonempty(aLists, kColGroupList);
1092 }
1094 nsRect
1095 nsDisplayTableItem::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
1096 *aSnap = false;
1097 return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
1098 }
1100 bool
1101 nsDisplayTableItem::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder,
1102 nsIFrame* aFrame)
1103 {
1104 if (!mPartHasFixedBackground)
1105 return false;
1107 // If aFrame is mFrame or an ancestor in this document, and aFrame is
1108 // not the viewport frame, then moving aFrame will move mFrame
1109 // relative to the viewport, so our fixed-pos background will change.
1110 return mFrame == aFrame ||
1111 nsLayoutUtils::IsProperAncestorFrame(aFrame, mFrame);
1112 }
1114 /* static */ void
1115 nsDisplayTableItem::UpdateForFrameBackground(nsIFrame* aFrame)
1116 {
1117 nsStyleContext *bgSC;
1118 if (!nsCSSRendering::FindBackground(aFrame, &bgSC))
1119 return;
1120 if (!bgSC->StyleBackground()->HasFixedBackground())
1121 return;
1123 mPartHasFixedBackground = true;
1124 }
1126 class nsDisplayTableBorderBackground : public nsDisplayTableItem {
1127 public:
1128 nsDisplayTableBorderBackground(nsDisplayListBuilder* aBuilder,
1129 nsTableFrame* aFrame) :
1130 nsDisplayTableItem(aBuilder, aFrame) {
1131 MOZ_COUNT_CTOR(nsDisplayTableBorderBackground);
1132 }
1133 #ifdef NS_BUILD_REFCNT_LOGGING
1134 virtual ~nsDisplayTableBorderBackground() {
1135 MOZ_COUNT_DTOR(nsDisplayTableBorderBackground);
1136 }
1137 #endif
1139 virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
1140 const nsDisplayItemGeometry* aGeometry,
1141 nsRegion *aInvalidRegion) MOZ_OVERRIDE;
1142 virtual void Paint(nsDisplayListBuilder* aBuilder,
1143 nsRenderingContext* aCtx) MOZ_OVERRIDE;
1144 NS_DISPLAY_DECL_NAME("TableBorderBackground", TYPE_TABLE_BORDER_BACKGROUND)
1145 };
1147 #ifdef DEBUG
1148 static bool
1149 IsFrameAllowedInTable(nsIAtom* aType)
1150 {
1151 return IS_TABLE_CELL(aType) ||
1152 nsGkAtoms::tableRowFrame == aType ||
1153 nsGkAtoms::tableRowGroupFrame == aType ||
1154 nsGkAtoms::scrollFrame == aType ||
1155 nsGkAtoms::tableFrame == aType ||
1156 nsGkAtoms::tableColFrame == aType ||
1157 nsGkAtoms::tableColGroupFrame == aType;
1158 }
1159 #endif
1161 /* static */ bool
1162 nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(nsIFrame* aStart,
1163 nsIFrame* aEnd)
1164 {
1165 for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) {
1166 NS_ASSERTION(IsFrameAllowedInTable(f->GetType()), "unexpected frame type");
1168 if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(f))
1169 return true;
1171 nsTableCellFrame *cellFrame = do_QueryFrame(f);
1172 if (cellFrame)
1173 continue;
1175 if (AnyTablePartHasUndecodedBackgroundImage(f->PrincipalChildList().FirstChild(), nullptr))
1176 return true;
1177 }
1179 return false;
1180 }
1182 void
1183 nsDisplayTableBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
1184 const nsDisplayItemGeometry* aGeometry,
1185 nsRegion *aInvalidRegion)
1186 {
1187 if (aBuilder->ShouldSyncDecodeImages()) {
1188 if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling()) ||
1189 nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(
1190 mFrame->GetChildList(nsIFrame::kColGroupList).FirstChild(), nullptr)) {
1191 bool snap;
1192 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
1193 }
1194 }
1196 nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
1197 }
1199 void
1200 nsDisplayTableBorderBackground::Paint(nsDisplayListBuilder* aBuilder,
1201 nsRenderingContext* aCtx)
1202 {
1203 static_cast<nsTableFrame*>(mFrame)->
1204 PaintTableBorderBackground(*aCtx, mVisibleRect,
1205 ToReferenceFrame(),
1206 aBuilder->GetBackgroundPaintFlags());
1207 }
1209 static int32_t GetTablePartRank(nsDisplayItem* aItem)
1210 {
1211 nsIAtom* type = aItem->Frame()->GetType();
1212 if (type == nsGkAtoms::tableFrame)
1213 return 0;
1214 if (type == nsGkAtoms::tableRowGroupFrame)
1215 return 1;
1216 if (type == nsGkAtoms::tableRowFrame)
1217 return 2;
1218 return 3;
1219 }
1221 static bool CompareByTablePartRank(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
1222 void* aClosure)
1223 {
1224 return GetTablePartRank(aItem1) <= GetTablePartRank(aItem2);
1225 }
1227 /* static */ void
1228 nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
1229 const nsRect& aDirtyRect, const nsDisplayListSet& aLists)
1230 {
1231 // This is similar to what nsContainerFrame::BuildDisplayListForNonBlockChildren
1232 // does, except that we allow the children's background and borders to go
1233 // in our BorderBackground list. This doesn't really affect background
1234 // painting --- the children won't actually draw their own backgrounds
1235 // because the nsTableFrame already drew them, unless a child has its own
1236 // stacking context, in which case the child won't use its passed-in
1237 // BorderBackground list anyway. It does affect cell borders though; this
1238 // lets us get cell borders into the nsTableFrame's BorderBackground list.
1239 nsIFrame* kid = aFrame->GetFirstPrincipalChild();
1240 while (kid) {
1241 aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
1242 kid = kid->GetNextSibling();
1243 }
1244 }
1246 /* static */ void
1247 nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder,
1248 nsFrame* aFrame,
1249 const nsRect& aDirtyRect,
1250 const nsDisplayListSet& aLists,
1251 nsDisplayTableItem* aDisplayItem,
1252 DisplayGenericTablePartTraversal aTraversal)
1253 {
1254 nsDisplayList eventsBorderBackground;
1255 // If we need to sort the event backgrounds, then we'll put descendants'
1256 // display items into their own set of lists.
1257 bool sortEventBackgrounds = aDisplayItem && aBuilder->IsForEventDelivery();
1258 nsDisplayListCollection separatedCollection;
1259 const nsDisplayListSet* lists = sortEventBackgrounds ? &separatedCollection : &aLists;
1261 nsAutoPushCurrentTableItem pushTableItem;
1262 if (aDisplayItem) {
1263 pushTableItem.Push(aBuilder, aDisplayItem);
1264 }
1266 if (aFrame->IsVisibleForPainting(aBuilder)) {
1267 nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem();
1268 // currentItem may be null, when none of the table parts have a
1269 // background or border
1270 if (currentItem) {
1271 currentItem->UpdateForFrameBackground(aFrame);
1272 }
1274 // Paint the outset box-shadows for the table frames
1275 bool hasBoxShadow = aFrame->StyleBorder()->mBoxShadow != nullptr;
1276 if (hasBoxShadow) {
1277 lists->BorderBackground()->AppendNewToTop(
1278 new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, aFrame));
1279 }
1281 // Create dedicated background display items per-frame when we're
1282 // handling events.
1283 // XXX how to handle collapsed borders?
1284 if (aBuilder->IsForEventDelivery()) {
1285 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame,
1286 lists->BorderBackground());
1287 }
1289 // Paint the inset box-shadows for the table frames
1290 if (hasBoxShadow) {
1291 lists->BorderBackground()->AppendNewToTop(
1292 new (aBuilder) nsDisplayBoxShadowInner(aBuilder, aFrame));
1293 }
1294 }
1296 aTraversal(aBuilder, aFrame, aDirtyRect, *lists);
1298 if (sortEventBackgrounds) {
1299 // Ensure that the table frame event background goes before the
1300 // table rowgroups event backgrounds, before the table row event backgrounds,
1301 // before everything else (cells and their blocks)
1302 separatedCollection.BorderBackground()->Sort(aBuilder, CompareByTablePartRank, nullptr);
1303 separatedCollection.MoveTo(aLists);
1304 }
1306 aFrame->DisplayOutline(aBuilder, aLists);
1307 }
1309 static bool
1310 AnyTablePartHasBorderOrBackground(nsIFrame* aStart, nsIFrame* aEnd)
1311 {
1312 for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) {
1313 NS_ASSERTION(IsFrameAllowedInTable(f->GetType()), "unexpected frame type");
1315 if (FrameHasBorderOrBackground(f))
1316 return true;
1318 nsTableCellFrame *cellFrame = do_QueryFrame(f);
1319 if (cellFrame)
1320 continue;
1322 if (AnyTablePartHasBorderOrBackground(f->PrincipalChildList().FirstChild(), nullptr))
1323 return true;
1324 }
1326 return false;
1327 }
1329 // table paint code is concerned primarily with borders and bg color
1330 // SEC: TODO: adjust the rect for captions
1331 void
1332 nsTableFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1333 const nsRect& aDirtyRect,
1334 const nsDisplayListSet& aLists)
1335 {
1336 DO_GLOBAL_REFLOW_COUNT_DSP_COLOR("nsTableFrame", NS_RGB(255,128,255));
1338 nsDisplayTableItem* item = nullptr;
1339 if (IsVisibleInSelection(aBuilder)) {
1340 if (StyleVisibility()->IsVisible()) {
1341 nsMargin deflate = GetDeflationForBackground(PresContext());
1342 // If 'deflate' is (0,0,0,0) then we can paint the table background
1343 // in its own display item, so do that to take advantage of
1344 // opacity and visibility optimizations
1345 if (deflate == nsMargin(0, 0, 0, 0)) {
1346 DisplayBackgroundUnconditional(aBuilder, aLists, false);
1347 }
1348 }
1350 // This background is created if any of the table parts are visible,
1351 // or if we're doing event handling (since DisplayGenericTablePart
1352 // needs the item for the |sortEventBackgrounds|-dependent code).
1353 // Specific visibility decisions are delegated to the table background
1354 // painter, which handles borders and backgrounds for the table.
1355 if (aBuilder->IsForEventDelivery() ||
1356 AnyTablePartHasBorderOrBackground(this, GetNextSibling()) ||
1357 AnyTablePartHasBorderOrBackground(mColGroups.FirstChild(), nullptr)) {
1358 item = new (aBuilder) nsDisplayTableBorderBackground(aBuilder, this);
1359 aLists.BorderBackground()->AppendNewToTop(item);
1360 }
1361 }
1362 DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item);
1363 }
1365 nsMargin
1366 nsTableFrame::GetDeflationForBackground(nsPresContext* aPresContext) const
1367 {
1368 if (eCompatibility_NavQuirks != aPresContext->CompatibilityMode() ||
1369 !IsBorderCollapse())
1370 return nsMargin(0,0,0,0);
1372 return GetOuterBCBorder();
1373 }
1375 // XXX We don't put the borders and backgrounds in tree order like we should.
1376 // That requires some major surgery which we aren't going to do right now.
1377 void
1378 nsTableFrame::PaintTableBorderBackground(nsRenderingContext& aRenderingContext,
1379 const nsRect& aDirtyRect,
1380 nsPoint aPt, uint32_t aBGPaintFlags)
1381 {
1382 nsPresContext* presContext = PresContext();
1384 TableBackgroundPainter painter(this, TableBackgroundPainter::eOrigin_Table,
1385 presContext, aRenderingContext,
1386 aDirtyRect, aPt, aBGPaintFlags);
1387 nsMargin deflate = GetDeflationForBackground(presContext);
1388 // If 'deflate' is (0,0,0,0) then we'll paint the table background
1389 // in a separate display item, so don't do it here.
1390 nsresult rv = painter.PaintTable(this, deflate, deflate != nsMargin(0, 0, 0, 0));
1391 if (NS_FAILED(rv)) return;
1393 if (StyleVisibility()->IsVisible()) {
1394 if (!IsBorderCollapse()) {
1395 int skipSides = GetSkipSides();
1396 nsRect rect(aPt, mRect.Size());
1397 nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
1398 aDirtyRect, rect, mStyleContext, skipSides);
1399 }
1400 else {
1401 // XXX we should probably get rid of this translation at some stage
1402 // But that would mean modifying PaintBCBorders, ugh
1403 nsRenderingContext::AutoPushTranslation translate(&aRenderingContext, aPt);
1404 PaintBCBorders(aRenderingContext, aDirtyRect - aPt);
1405 }
1406 }
1407 }
1409 int
1410 nsTableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
1411 {
1412 int skip = 0;
1413 // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto
1414 // account for pagination
1415 if (nullptr != GetPrevInFlow()) {
1416 skip |= LOGICAL_SIDE_B_START;
1417 }
1418 if (nullptr != GetNextInFlow()) {
1419 skip |= LOGICAL_SIDE_B_END;
1420 }
1421 return skip;
1422 }
1424 void
1425 nsTableFrame::SetColumnDimensions(nscoord aHeight,
1426 const nsMargin& aBorderPadding)
1427 {
1428 nscoord cellSpacingX = GetCellSpacingX();
1429 nscoord cellSpacingY = GetCellSpacingY();
1430 nscoord colHeight = aHeight -= aBorderPadding.top + aBorderPadding.bottom +
1431 2* cellSpacingY;
1433 nsTableIterator iter(mColGroups);
1434 nsIFrame* colGroupFrame = iter.First();
1435 bool tableIsLTR = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
1436 int32_t colX =tableIsLTR ? 0 : std::max(0, GetColCount() - 1);
1437 int32_t tableColIncr = tableIsLTR ? 1 : -1;
1438 nsPoint colGroupOrigin(aBorderPadding.left + cellSpacingX,
1439 aBorderPadding.top + cellSpacingY);
1440 while (colGroupFrame) {
1441 MOZ_ASSERT(colGroupFrame->GetType() == nsGkAtoms::tableColGroupFrame);
1442 nscoord colGroupWidth = 0;
1443 nsTableIterator iterCol(*colGroupFrame);
1444 nsIFrame* colFrame = iterCol.First();
1445 nsPoint colOrigin(0,0);
1446 while (colFrame) {
1447 if (NS_STYLE_DISPLAY_TABLE_COLUMN ==
1448 colFrame->StyleDisplay()->mDisplay) {
1449 NS_ASSERTION(colX < GetColCount(), "invalid number of columns");
1450 nscoord colWidth = GetColumnWidth(colX);
1451 nsRect colRect(colOrigin.x, colOrigin.y, colWidth, colHeight);
1452 colFrame->SetRect(colRect);
1453 colOrigin.x += colWidth + cellSpacingX;
1454 colGroupWidth += colWidth + cellSpacingX;
1455 colX += tableColIncr;
1456 }
1457 colFrame = iterCol.Next();
1458 }
1459 if (colGroupWidth) {
1460 colGroupWidth -= cellSpacingX;
1461 }
1463 nsRect colGroupRect(colGroupOrigin.x, colGroupOrigin.y, colGroupWidth, colHeight);
1464 colGroupFrame->SetRect(colGroupRect);
1465 colGroupFrame = iter.Next();
1466 colGroupOrigin.x += colGroupWidth + cellSpacingX;
1467 }
1468 }
1470 // SEC: TODO need to worry about continuing frames prev/next in flow for splitting across pages.
1472 // XXX this could be made more general to handle row modifications that change the
1473 // table height, but first we need to scrutinize every Invalidate
1474 void
1475 nsTableFrame::ProcessRowInserted(nscoord aNewHeight)
1476 {
1477 SetRowInserted(false); // reset the bit that got us here
1478 nsTableFrame::RowGroupArray rowGroups;
1479 OrderRowGroups(rowGroups);
1480 // find the row group containing the inserted row
1481 for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
1482 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
1483 NS_ASSERTION(rgFrame, "Must have rgFrame here");
1484 nsIFrame* childFrame = rgFrame->GetFirstPrincipalChild();
1485 // find the row that was inserted first
1486 while (childFrame) {
1487 nsTableRowFrame *rowFrame = do_QueryFrame(childFrame);
1488 if (rowFrame) {
1489 if (rowFrame->IsFirstInserted()) {
1490 rowFrame->SetFirstInserted(false);
1491 // damage the table from the 1st row inserted to the end of the table
1492 nsIFrame::InvalidateFrame();
1493 // XXXbz didn't we do this up front? Why do we need to do it again?
1494 SetRowInserted(false);
1495 return; // found it, so leave
1496 }
1497 }
1498 childFrame = childFrame->GetNextSibling();
1499 }
1500 }
1501 }
1503 /* virtual */ void
1504 nsTableFrame::MarkIntrinsicWidthsDirty()
1505 {
1506 nsITableLayoutStrategy* tls = LayoutStrategy();
1507 if (MOZ_UNLIKELY(!tls)) {
1508 // This is a FrameNeedsReflow() from nsBlockFrame::RemoveFrame()
1509 // walking up the ancestor chain in a table next-in-flow. In this case
1510 // our original first-in-flow (which owns the TableLayoutStrategy) has
1511 // already been destroyed and unhooked from the flow chain and thusly
1512 // LayoutStrategy() returns null. All the frames in the flow will be
1513 // destroyed so no need to mark anything dirty here. See bug 595758.
1514 return;
1515 }
1516 tls->MarkIntrinsicWidthsDirty();
1518 // XXXldb Call SetBCDamageArea?
1520 nsContainerFrame::MarkIntrinsicWidthsDirty();
1521 }
1523 /* virtual */ nscoord
1524 nsTableFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
1525 {
1526 if (NeedToCalcBCBorders())
1527 CalcBCBorders();
1529 ReflowColGroups(aRenderingContext);
1531 return LayoutStrategy()->GetMinWidth(aRenderingContext);
1532 }
1534 /* virtual */ nscoord
1535 nsTableFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
1536 {
1537 if (NeedToCalcBCBorders())
1538 CalcBCBorders();
1540 ReflowColGroups(aRenderingContext);
1542 return LayoutStrategy()->GetPrefWidth(aRenderingContext, false);
1543 }
1545 /* virtual */ nsIFrame::IntrinsicWidthOffsetData
1546 nsTableFrame::IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext)
1547 {
1548 IntrinsicWidthOffsetData result =
1549 nsContainerFrame::IntrinsicWidthOffsets(aRenderingContext);
1551 result.hMargin = 0;
1552 result.hPctMargin = 0;
1554 if (IsBorderCollapse()) {
1555 result.hPadding = 0;
1556 result.hPctPadding = 0;
1558 nsMargin outerBC = GetIncludedOuterBCBorder();
1559 result.hBorder = outerBC.LeftRight();
1560 }
1562 return result;
1563 }
1565 /* virtual */ nsSize
1566 nsTableFrame::ComputeSize(nsRenderingContext *aRenderingContext,
1567 nsSize aCBSize, nscoord aAvailableWidth,
1568 nsSize aMargin, nsSize aBorder, nsSize aPadding,
1569 uint32_t aFlags)
1570 {
1571 nsSize result =
1572 nsContainerFrame::ComputeSize(aRenderingContext, aCBSize, aAvailableWidth,
1573 aMargin, aBorder, aPadding, aFlags);
1575 // If we're a container for font size inflation, then shrink
1576 // wrapping inside of us should not apply font size inflation.
1577 AutoMaybeDisableFontInflation an(this);
1579 // Tables never shrink below their min width.
1580 nscoord minWidth = GetMinWidth(aRenderingContext);
1581 if (minWidth > result.width)
1582 result.width = minWidth;
1584 return result;
1585 }
1587 nscoord
1588 nsTableFrame::TableShrinkWidthToFit(nsRenderingContext *aRenderingContext,
1589 nscoord aWidthInCB)
1590 {
1591 // If we're a container for font size inflation, then shrink
1592 // wrapping inside of us should not apply font size inflation.
1593 AutoMaybeDisableFontInflation an(this);
1595 nscoord result;
1596 nscoord minWidth = GetMinWidth(aRenderingContext);
1597 if (minWidth > aWidthInCB) {
1598 result = minWidth;
1599 } else {
1600 // Tables shrink width to fit with a slightly different algorithm
1601 // from the one they use for their intrinsic widths (the difference
1602 // relates to handling of percentage widths on columns). So this
1603 // function differs from nsFrame::ShrinkWidthToFit by only the
1604 // following line.
1605 // Since we've already called GetMinWidth, we don't need to do any
1606 // of the other stuff GetPrefWidth does.
1607 nscoord prefWidth =
1608 LayoutStrategy()->GetPrefWidth(aRenderingContext, true);
1609 if (prefWidth > aWidthInCB) {
1610 result = aWidthInCB;
1611 } else {
1612 result = prefWidth;
1613 }
1614 }
1615 return result;
1616 }
1618 /* virtual */ nsSize
1619 nsTableFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
1620 nsSize aCBSize, nscoord aAvailableWidth,
1621 nsSize aMargin, nsSize aBorder, nsSize aPadding,
1622 bool aShrinkWrap)
1623 {
1624 // Tables always shrink-wrap.
1625 nscoord cbBased = aAvailableWidth - aMargin.width - aBorder.width -
1626 aPadding.width;
1627 return nsSize(TableShrinkWidthToFit(aRenderingContext, cbBased),
1628 NS_UNCONSTRAINEDSIZE);
1629 }
1631 // Return true if aParentReflowState.frame or any of its ancestors within
1632 // the containing table have non-auto height. (e.g. pct or fixed height)
1633 bool
1634 nsTableFrame::AncestorsHaveStyleHeight(const nsHTMLReflowState& aParentReflowState)
1635 {
1636 for (const nsHTMLReflowState* rs = &aParentReflowState;
1637 rs && rs->frame; rs = rs->parentReflowState) {
1638 nsIAtom* frameType = rs->frame->GetType();
1639 if (IS_TABLE_CELL(frameType) ||
1640 (nsGkAtoms::tableRowFrame == frameType) ||
1641 (nsGkAtoms::tableRowGroupFrame == frameType)) {
1642 const nsStyleCoord &height = rs->mStylePosition->mHeight;
1643 // calc() with percentages treated like 'auto' on internal table elements
1644 if (height.GetUnit() != eStyleUnit_Auto &&
1645 (!height.IsCalcUnit() || !height.HasPercent())) {
1646 return true;
1647 }
1648 }
1649 else if (nsGkAtoms::tableFrame == frameType) {
1650 // we reached the containing table, so always return
1651 return rs->mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto;
1652 }
1653 }
1654 return false;
1655 }
1657 // See if a special height reflow needs to occur and if so, call RequestSpecialHeightReflow
1658 void
1659 nsTableFrame::CheckRequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState)
1660 {
1661 NS_ASSERTION(IS_TABLE_CELL(aReflowState.frame->GetType()) ||
1662 aReflowState.frame->GetType() == nsGkAtoms::tableRowFrame ||
1663 aReflowState.frame->GetType() == nsGkAtoms::tableRowGroupFrame ||
1664 aReflowState.frame->GetType() == nsGkAtoms::tableFrame,
1665 "unexpected frame type");
1666 if (!aReflowState.frame->GetPrevInFlow() && // 1st in flow
1667 (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight() || // no computed height
1668 0 == aReflowState.ComputedHeight()) &&
1669 eStyleUnit_Percent == aReflowState.mStylePosition->mHeight.GetUnit() && // pct height
1670 nsTableFrame::AncestorsHaveStyleHeight(*aReflowState.parentReflowState)) {
1671 nsTableFrame::RequestSpecialHeightReflow(aReflowState);
1672 }
1673 }
1675 // Notify the frame and its ancestors (up to the containing table) that a special
1676 // height reflow will occur. During a special height reflow, a table, row group,
1677 // row, or cell returns the last size it was reflowed at. However, the table may
1678 // change the height of row groups, rows, cells in DistributeHeightToRows after.
1679 // And the row group can change the height of rows, cells in CalculateRowHeights.
1680 void
1681 nsTableFrame::RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState)
1682 {
1683 // notify the frame and its ancestors of the special reflow, stopping at the containing table
1684 for (const nsHTMLReflowState* rs = &aReflowState; rs && rs->frame; rs = rs->parentReflowState) {
1685 nsIAtom* frameType = rs->frame->GetType();
1686 NS_ASSERTION(IS_TABLE_CELL(frameType) ||
1687 nsGkAtoms::tableRowFrame == frameType ||
1688 nsGkAtoms::tableRowGroupFrame == frameType ||
1689 nsGkAtoms::tableFrame == frameType,
1690 "unexpected frame type");
1692 rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
1693 if (nsGkAtoms::tableFrame == frameType) {
1694 NS_ASSERTION(rs != &aReflowState,
1695 "should not request special height reflow for table");
1696 // always stop when we reach a table
1697 break;
1698 }
1699 }
1700 }
1702 /******************************************************************************************
1703 * Before reflow, intrinsic width calculation is done using GetMinWidth
1704 * and GetPrefWidth. This used to be known as pass 1 reflow.
1705 *
1706 * After the intrinsic width calculation, the table determines the
1707 * column widths using BalanceColumnWidths() and
1708 * then reflows each child again with a constrained avail width. This reflow is referred to
1709 * as the pass 2 reflow.
1710 *
1711 * A special height reflow (pass 3 reflow) can occur during an initial or resize reflow
1712 * if (a) a row group, row, cell, or a frame inside a cell has a percent height but no computed
1713 * height or (b) in paginated mode, a table has a height. (a) supports percent nested tables
1714 * contained inside cells whose heights aren't known until after the pass 2 reflow. (b) is
1715 * necessary because the table cannot split until after the pass 2 reflow. The mechanics of
1716 * the special height reflow (variety a) are as follows:
1717 *
1718 * 1) Each table related frame (table, row group, row, cell) implements NeedsSpecialReflow()
1719 * to indicate that it should get the reflow. It does this when it has a percent height but
1720 * no computed height by calling CheckRequestSpecialHeightReflow(). This method calls
1721 * RequestSpecialHeightReflow() which calls SetNeedSpecialReflow() on its ancestors until
1722 * it reaches the containing table and calls SetNeedToInitiateSpecialReflow() on it. For
1723 * percent height frames inside cells, during DidReflow(), the cell's NotifyPercentHeight()
1724 * is called (the cell is the reflow state's mPercentHeightObserver in this case).
1725 * NotifyPercentHeight() calls RequestSpecialHeightReflow().
1726 *
1727 * 2) After the pass 2 reflow, if the table's NeedToInitiateSpecialReflow(true) was called, it
1728 * will do the special height reflow, setting the reflow state's mFlags.mSpecialHeightReflow
1729 * to true and mSpecialHeightInitiator to itself. It won't do this if IsPrematureSpecialHeightReflow()
1730 * returns true because in that case another special height reflow will be coming along with the
1731 * containing table as the mSpecialHeightInitiator. It is only relevant to do the reflow when
1732 * the mSpecialHeightInitiator is the containing table, because if it is a remote ancestor, then
1733 * appropriate heights will not be known.
1734 *
1735 * 3) Since the heights of the table, row groups, rows, and cells was determined during the pass 2
1736 * reflow, they return their last desired sizes during the special height reflow. The reflow only
1737 * permits percent height frames inside the cells to resize based on the cells height and that height
1738 * was determined during the pass 2 reflow.
1739 *
1740 * So, in the case of deeply nested tables, all of the tables that were told to initiate a special
1741 * reflow will do so, but if a table is already in a special reflow, it won't inititate the reflow
1742 * until the current initiator is its containing table. Since these reflows are only received by
1743 * frames that need them and they don't cause any rebalancing of tables, the extra overhead is minimal.
1744 *
1745 * The type of special reflow that occurs during printing (variety b) follows the same mechanism except
1746 * that all frames will receive the reflow even if they don't really need them.
1747 *
1748 * Open issues with the special height reflow:
1749 *
1750 * 1) At some point there should be 2 kinds of special height reflows because (a) and (b) above are
1751 * really quite different. This would avoid unnecessary reflows during printing.
1752 * 2) When a cell contains frames whose percent heights > 100%, there is data loss (see bug 115245).
1753 * However, this can also occur if a cell has a fixed height and there is no special height reflow.
1754 *
1755 * XXXldb Special height reflow should really be its own method, not
1756 * part of nsIFrame::Reflow. It should then call nsIFrame::Reflow on
1757 * the contents of the cells to do the necessary vertical resizing.
1758 *
1759 ******************************************************************************************/
1761 /* Layout the entire inner table. */
1762 nsresult nsTableFrame::Reflow(nsPresContext* aPresContext,
1763 nsHTMLReflowMetrics& aDesiredSize,
1764 const nsHTMLReflowState& aReflowState,
1765 nsReflowStatus& aStatus)
1766 {
1767 DO_GLOBAL_REFLOW_COUNT("nsTableFrame");
1768 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
1769 bool isPaginated = aPresContext->IsPaginated();
1771 aStatus = NS_FRAME_COMPLETE;
1772 if (!GetPrevInFlow() && !mTableLayoutStrategy) {
1773 NS_ASSERTION(false, "strategy should have been created in Init");
1774 return NS_ERROR_NULL_POINTER;
1775 }
1776 nsresult rv = NS_OK;
1778 // see if collapsing borders need to be calculated
1779 if (!GetPrevInFlow() && IsBorderCollapse() && NeedToCalcBCBorders()) {
1780 CalcBCBorders();
1781 }
1783 aDesiredSize.Width() = aReflowState.AvailableWidth();
1785 // Check for an overflow list, and append any row group frames being pushed
1786 MoveOverflowToChildList();
1788 bool haveDesiredHeight = false;
1789 SetHaveReflowedColGroups(false);
1791 // Reflow the entire table (pass 2 and possibly pass 3). This phase is necessary during a
1792 // constrained initial reflow and other reflows which require either a strategy init or balance.
1793 // This isn't done during an unconstrained reflow, because it will occur later when the parent
1794 // reflows with a constrained width.
1795 if (NS_SUBTREE_DIRTY(this) ||
1796 aReflowState.ShouldReflowAllKids() ||
1797 IsGeometryDirty() ||
1798 aReflowState.mFlags.mVResize) {
1800 if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE ||
1801 // Also check mVResize, to handle the first Reflow preceding a
1802 // special height Reflow, when we've already had a special height
1803 // Reflow (where mComputedHeight would not be
1804 // NS_UNCONSTRAINEDSIZE, but without a style change in between).
1805 aReflowState.mFlags.mVResize) {
1806 // XXX Eventually, we should modify DistributeHeightToRows to use
1807 // nsTableRowFrame::GetHeight instead of nsIFrame::GetSize().height.
1808 // That way, it will make its calculations based on internal table
1809 // frame heights as they are before they ever had any extra height
1810 // distributed to them. In the meantime, this reflows all the
1811 // internal table frames, which restores them to their state before
1812 // DistributeHeightToRows was called.
1813 SetGeometryDirty();
1814 }
1816 bool needToInitiateSpecialReflow =
1817 !!(GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
1818 // see if an extra reflow will be necessary in pagination mode when there is a specified table height
1819 if (isPaginated && !GetPrevInFlow() && (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight())) {
1820 nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
1821 if ((tableSpecifiedHeight > 0) &&
1822 (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) {
1823 needToInitiateSpecialReflow = true;
1824 }
1825 }
1826 nsIFrame* lastChildReflowed = nullptr;
1828 NS_ASSERTION(!aReflowState.mFlags.mSpecialHeightReflow,
1829 "Shouldn't be in special height reflow here!");
1831 // do the pass 2 reflow unless this is a special height reflow and we will be
1832 // initiating a special height reflow
1833 // XXXldb I changed this. Should I change it back?
1835 // if we need to initiate a special height reflow, then don't constrain the
1836 // height of the reflow before that
1837 nscoord availHeight = needToInitiateSpecialReflow
1838 ? NS_UNCONSTRAINEDSIZE : aReflowState.AvailableHeight();
1840 ReflowTable(aDesiredSize, aReflowState, availHeight,
1841 lastChildReflowed, aStatus);
1843 // reevaluate special height reflow conditions
1844 if (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)
1845 needToInitiateSpecialReflow = true;
1847 // XXXldb Are all these conditions correct?
1848 if (needToInitiateSpecialReflow && NS_FRAME_IS_COMPLETE(aStatus)) {
1849 // XXXldb Do we need to set the mVResize flag on any reflow states?
1851 nsHTMLReflowState &mutable_rs =
1852 const_cast<nsHTMLReflowState&>(aReflowState);
1854 // distribute extra vertical space to rows
1855 CalcDesiredHeight(aReflowState, aDesiredSize);
1856 mutable_rs.mFlags.mSpecialHeightReflow = true;
1858 ReflowTable(aDesiredSize, aReflowState, aReflowState.AvailableHeight(),
1859 lastChildReflowed, aStatus);
1861 if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
1862 // if there is an incomplete child, then set the desired height to include it but not the next one
1863 nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
1864 aDesiredSize.Height() = borderPadding.bottom + GetCellSpacingY() +
1865 lastChildReflowed->GetRect().YMost();
1866 }
1867 haveDesiredHeight = true;
1869 mutable_rs.mFlags.mSpecialHeightReflow = false;
1870 }
1871 }
1872 else {
1873 // Calculate the overflow area contribution from our children.
1874 for (nsIFrame* kid = GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) {
1875 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kid);
1876 }
1877 }
1879 aDesiredSize.Width() = aReflowState.ComputedWidth() +
1880 aReflowState.ComputedPhysicalBorderPadding().LeftRight();
1881 if (!haveDesiredHeight) {
1882 CalcDesiredHeight(aReflowState, aDesiredSize);
1883 }
1884 if (IsRowInserted()) {
1885 ProcessRowInserted(aDesiredSize.Height());
1886 }
1888 nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
1889 SetColumnDimensions(aDesiredSize.Height(), borderPadding);
1890 if (NeedToCollapse() &&
1891 (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableWidth())) {
1892 AdjustForCollapsingRowsCols(aDesiredSize, borderPadding);
1893 }
1895 // If there are any relatively-positioned table parts, we need to reflow their
1896 // absolutely-positioned descendants now that their dimensions are final.
1897 FixupPositionedTableParts(aPresContext, aDesiredSize, aReflowState);
1899 // make sure the table overflow area does include the table rect.
1900 nsRect tableRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()) ;
1902 if (!ShouldApplyOverflowClipping(this, aReflowState.mStyleDisplay)) {
1903 // collapsed border may leak out
1904 nsMargin bcMargin = GetExcludedOuterBCBorder();
1905 tableRect.Inflate(bcMargin);
1906 }
1907 aDesiredSize.mOverflowAreas.UnionAllWith(tableRect);
1909 if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
1910 nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
1911 nsIFrame::InvalidateFrame();
1912 }
1914 FinishAndStoreOverflow(&aDesiredSize);
1915 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
1916 return rv;
1917 }
1919 void
1920 nsTableFrame::FixupPositionedTableParts(nsPresContext* aPresContext,
1921 nsHTMLReflowMetrics& aDesiredSize,
1922 const nsHTMLReflowState& aReflowState)
1923 {
1924 auto positionedParts =
1925 static_cast<FrameTArray*>(Properties().Get(PositionedTablePartArray()));
1926 if (!positionedParts) {
1927 return;
1928 }
1930 OverflowChangedTracker overflowTracker;
1931 overflowTracker.SetSubtreeRoot(this);
1933 for (size_t i = 0; i < positionedParts->Length(); ++i) {
1934 nsIFrame* positionedPart = positionedParts->ElementAt(i);
1936 // As we've already finished reflow, positionedParts's size and overflow
1937 // areas have already been assigned, so we just pull them back out.
1938 nsSize size(positionedPart->GetSize());
1939 nsHTMLReflowMetrics desiredSize(aReflowState.GetWritingMode());
1940 desiredSize.Width() = size.width;
1941 desiredSize.Height() = size.height;
1942 desiredSize.mOverflowAreas = positionedPart->GetOverflowAreasRelativeToSelf();
1944 // Construct a dummy reflow state and reflow status.
1945 // XXX(seth): Note that the dummy reflow state doesn't have a correct
1946 // chain of parent reflow states. It also doesn't necessarily have a
1947 // correct containing block.
1948 nsHTMLReflowState reflowState(aPresContext, positionedPart,
1949 aReflowState.rendContext,
1950 nsSize(size.width, NS_UNCONSTRAINEDSIZE),
1951 nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE);
1952 nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
1954 // Reflow absolutely-positioned descendants of the positioned part.
1955 // FIXME: Unconditionally using NS_UNCONSTRAINEDSIZE for the height and
1956 // ignoring any change to the reflow status aren't correct. We'll never
1957 // paginate absolutely positioned frames.
1958 overflowTracker.AddFrame(positionedPart,
1959 OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED);
1960 nsFrame* positionedFrame = static_cast<nsFrame*>(positionedPart);
1961 positionedFrame->FinishReflowWithAbsoluteFrames(PresContext(),
1962 desiredSize,
1963 reflowState,
1964 reflowStatus,
1965 true);
1966 }
1968 // Propagate updated overflow areas up the tree.
1969 overflowTracker.Flush();
1971 // Update our own overflow areas. (OverflowChangedTracker doesn't update the
1972 // subtree root itself.)
1973 aDesiredSize.SetOverflowAreasToDesiredBounds();
1974 nsLayoutUtils::UnionChildOverflow(this, aDesiredSize.mOverflowAreas);
1975 }
1977 bool
1978 nsTableFrame::UpdateOverflow()
1979 {
1980 nsRect bounds(nsPoint(0, 0), GetSize());
1982 // As above in Reflow, make sure the table overflow area includes the table
1983 // rect, and check for collapsed borders leaking out.
1984 if (!ShouldApplyOverflowClipping(this, StyleDisplay())) {
1985 nsMargin bcMargin = GetExcludedOuterBCBorder();
1986 bounds.Inflate(bcMargin);
1987 }
1989 nsOverflowAreas overflowAreas(bounds, bounds);
1990 nsLayoutUtils::UnionChildOverflow(this, overflowAreas);
1992 return FinishAndStoreOverflow(overflowAreas, GetSize());
1993 }
1995 nsresult
1996 nsTableFrame::ReflowTable(nsHTMLReflowMetrics& aDesiredSize,
1997 const nsHTMLReflowState& aReflowState,
1998 nscoord aAvailHeight,
1999 nsIFrame*& aLastChildReflowed,
2000 nsReflowStatus& aStatus)
2001 {
2002 nsresult rv = NS_OK;
2003 aLastChildReflowed = nullptr;
2005 if (!GetPrevInFlow()) {
2006 mTableLayoutStrategy->ComputeColumnWidths(aReflowState);
2007 }
2008 // Constrain our reflow width to the computed table width (of the 1st in flow).
2009 // and our reflow height to our avail height minus border, padding, cellspacing
2010 aDesiredSize.Width() = aReflowState.ComputedWidth() +
2011 aReflowState.ComputedPhysicalBorderPadding().LeftRight();
2012 nsTableReflowState reflowState(*PresContext(), aReflowState, *this,
2013 aDesiredSize.Width(), aAvailHeight);
2014 ReflowChildren(reflowState, aStatus, aLastChildReflowed,
2015 aDesiredSize.mOverflowAreas);
2017 ReflowColGroups(aReflowState.rendContext);
2018 return rv;
2019 }
2021 nsIFrame*
2022 nsTableFrame::GetFirstBodyRowGroupFrame()
2023 {
2024 nsIFrame* headerFrame = nullptr;
2025 nsIFrame* footerFrame = nullptr;
2027 for (nsIFrame* kidFrame = mFrames.FirstChild(); nullptr != kidFrame; ) {
2028 const nsStyleDisplay* childDisplay = kidFrame->StyleDisplay();
2030 // We expect the header and footer row group frames to be first, and we only
2031 // allow one header and one footer
2032 if (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == childDisplay->mDisplay) {
2033 if (headerFrame) {
2034 // We already have a header frame and so this header frame is treated
2035 // like an ordinary body row group frame
2036 return kidFrame;
2037 }
2038 headerFrame = kidFrame;
2040 } else if (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == childDisplay->mDisplay) {
2041 if (footerFrame) {
2042 // We already have a footer frame and so this footer frame is treated
2043 // like an ordinary body row group frame
2044 return kidFrame;
2045 }
2046 footerFrame = kidFrame;
2048 } else if (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == childDisplay->mDisplay) {
2049 return kidFrame;
2050 }
2052 // Get the next child
2053 kidFrame = kidFrame->GetNextSibling();
2054 }
2056 return nullptr;
2057 }
2059 // Table specific version that takes into account repeated header and footer
2060 // frames when continuing table frames
2061 void
2062 nsTableFrame::PushChildren(const RowGroupArray& aRowGroups,
2063 int32_t aPushFrom)
2064 {
2065 NS_PRECONDITION(aPushFrom > 0, "pushing first child");
2067 // extract the frames from the array into a sibling list
2068 nsFrameList frames;
2069 uint32_t childX;
2070 for (childX = aPushFrom; childX < aRowGroups.Length(); ++childX) {
2071 nsTableRowGroupFrame* rgFrame = aRowGroups[childX];
2072 if (!rgFrame->IsRepeatable()) {
2073 mFrames.RemoveFrame(rgFrame);
2074 frames.AppendFrame(nullptr, rgFrame);
2075 }
2076 }
2078 if (frames.IsEmpty()) {
2079 return;
2080 }
2082 nsTableFrame* nextInFlow = static_cast<nsTableFrame*>(GetNextInFlow());
2083 if (nextInFlow) {
2084 // Insert the frames after any repeated header and footer frames.
2085 nsIFrame* firstBodyFrame = nextInFlow->GetFirstBodyRowGroupFrame();
2086 nsIFrame* prevSibling = nullptr;
2087 if (firstBodyFrame) {
2088 prevSibling = firstBodyFrame->GetPrevSibling();
2089 }
2090 // When pushing and pulling frames we need to check for whether any
2091 // views need to be reparented.
2092 ReparentFrameViewList(frames, this, nextInFlow);
2093 nextInFlow->mFrames.InsertFrames(nextInFlow, prevSibling,
2094 frames);
2095 }
2096 else {
2097 // Add the frames to our overflow list.
2098 SetOverflowFrames(frames);
2099 }
2100 }
2102 // collapsing row groups, rows, col groups and cols are accounted for after both passes of
2103 // reflow so that it has no effect on the calculations of reflow.
2104 void
2105 nsTableFrame::AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize,
2106 nsMargin aBorderPadding)
2107 {
2108 nscoord yTotalOffset = 0; // total offset among all rows in all row groups
2110 // reset the bit, it will be set again if row/rowgroup or col/colgroup are
2111 // collapsed
2112 SetNeedToCollapse(false);
2114 // collapse the rows and/or row groups as necessary
2115 // Get the ordered children
2116 RowGroupArray rowGroups;
2117 OrderRowGroups(rowGroups);
2119 nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
2120 nscoord width = firstInFlow->GetCollapsedWidth(aBorderPadding);
2121 nscoord rgWidth = width - 2 * GetCellSpacingX();
2122 nsOverflowAreas overflow;
2123 // Walk the list of children
2124 for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) {
2125 nsTableRowGroupFrame* rgFrame = rowGroups[childX];
2126 NS_ASSERTION(rgFrame, "Must have row group frame here");
2127 yTotalOffset += rgFrame->CollapseRowGroupIfNecessary(yTotalOffset, rgWidth);
2128 ConsiderChildOverflow(overflow, rgFrame);
2129 }
2131 aDesiredSize.Height() -= yTotalOffset;
2132 aDesiredSize.Width() = width;
2133 overflow.UnionAllWith(nsRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()));
2134 FinishAndStoreOverflow(overflow,
2135 nsSize(aDesiredSize.Width(), aDesiredSize.Height()));
2136 }
2139 nscoord
2140 nsTableFrame::GetCollapsedWidth(nsMargin aBorderPadding)
2141 {
2142 NS_ASSERTION(!GetPrevInFlow(), "GetCollapsedWidth called on next in flow");
2143 nscoord cellSpacingX = GetCellSpacingX();
2144 nscoord width = cellSpacingX;
2145 width += aBorderPadding.left + aBorderPadding.right;
2146 for (nsIFrame* groupFrame = mColGroups.FirstChild(); groupFrame;
2147 groupFrame = groupFrame->GetNextSibling()) {
2148 const nsStyleVisibility* groupVis = groupFrame->StyleVisibility();
2149 bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
2150 nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame;
2151 for (nsTableColFrame* colFrame = cgFrame->GetFirstColumn(); colFrame;
2152 colFrame = colFrame->GetNextCol()) {
2153 const nsStyleDisplay* colDisplay = colFrame->StyleDisplay();
2154 int32_t colX = colFrame->GetColIndex();
2155 if (NS_STYLE_DISPLAY_TABLE_COLUMN == colDisplay->mDisplay) {
2156 const nsStyleVisibility* colVis = colFrame->StyleVisibility();
2157 bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
2158 int32_t colWidth = GetColumnWidth(colX);
2159 if (!collapseGroup && !collapseCol) {
2160 width += colWidth;
2161 if (ColumnHasCellSpacingBefore(colX))
2162 width += cellSpacingX;
2163 }
2164 else {
2165 SetNeedToCollapse(true);
2166 }
2167 }
2168 }
2169 }
2170 return width;
2171 }
2173 /* virtual */ void
2174 nsTableFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
2175 {
2176 nsContainerFrame::DidSetStyleContext(aOldStyleContext);
2178 if (!aOldStyleContext) //avoid this on init
2179 return;
2181 if (IsBorderCollapse() &&
2182 BCRecalcNeeded(aOldStyleContext, StyleContext())) {
2183 SetFullBCDamageArea();
2184 }
2186 //avoid this on init or nextinflow
2187 if (!mTableLayoutStrategy || GetPrevInFlow())
2188 return;
2190 bool isAuto = IsAutoLayout();
2191 if (isAuto != (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto)) {
2192 nsITableLayoutStrategy* temp;
2193 if (isAuto)
2194 temp = new BasicTableLayoutStrategy(this);
2195 else
2196 temp = new FixedTableLayoutStrategy(this);
2198 if (temp) {
2199 delete mTableLayoutStrategy;
2200 mTableLayoutStrategy = temp;
2201 }
2202 }
2203 }
2207 nsresult
2208 nsTableFrame::AppendFrames(ChildListID aListID,
2209 nsFrameList& aFrameList)
2210 {
2211 NS_ASSERTION(aListID == kPrincipalList || aListID == kColGroupList,
2212 "unexpected child list");
2214 // Because we actually have two child lists, one for col group frames and one
2215 // for everything else, we need to look at each frame individually
2216 // XXX The frame construction code should be separating out child frames
2217 // based on the type, bug 343048.
2218 while (!aFrameList.IsEmpty()) {
2219 nsIFrame* f = aFrameList.FirstChild();
2220 aFrameList.RemoveFrame(f);
2222 // See what kind of frame we have
2223 const nsStyleDisplay* display = f->StyleDisplay();
2225 if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) {
2226 nsTableColGroupFrame* lastColGroup =
2227 nsTableColGroupFrame::GetLastRealColGroup(this);
2228 int32_t startColIndex = (lastColGroup)
2229 ? lastColGroup->GetStartColumnIndex() + lastColGroup->GetColCount() : 0;
2230 mColGroups.InsertFrame(nullptr, lastColGroup, f);
2231 // Insert the colgroup and its cols into the table
2232 InsertColGroups(startColIndex,
2233 nsFrameList::Slice(mColGroups, f, f->GetNextSibling()));
2234 } else if (IsRowGroup(display->mDisplay)) {
2235 // Append the new row group frame to the sibling chain
2236 mFrames.AppendFrame(nullptr, f);
2238 // insert the row group and its rows into the table
2239 InsertRowGroups(nsFrameList::Slice(mFrames, f, nullptr));
2240 } else {
2241 // Nothing special to do, just add the frame to our child list
2242 NS_NOTREACHED("How did we get here? Frame construction screwed up");
2243 mFrames.AppendFrame(nullptr, f);
2244 }
2245 }
2247 #ifdef DEBUG_TABLE_CELLMAP
2248 printf("=== TableFrame::AppendFrames\n");
2249 Dump(true, true, true);
2250 #endif
2251 PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
2252 NS_FRAME_HAS_DIRTY_CHILDREN);
2253 SetGeometryDirty();
2255 return NS_OK;
2256 }
2258 // Needs to be at file scope or ArrayLength fails to compile.
2259 struct ChildListInsertions {
2260 nsIFrame::ChildListID mID;
2261 nsFrameList mList;
2262 };
2264 nsresult
2265 nsTableFrame::InsertFrames(ChildListID aListID,
2266 nsIFrame* aPrevFrame,
2267 nsFrameList& aFrameList)
2268 {
2269 // The frames in aFrameList can be a mix of row group frames and col group
2270 // frames. The problem is that they should go in separate child lists so
2271 // we need to deal with that here...
2272 // XXX The frame construction code should be separating out child frames
2273 // based on the type, bug 343048.
2275 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
2276 "inserting after sibling frame with different parent");
2278 if ((aPrevFrame && !aPrevFrame->GetNextSibling()) ||
2279 (!aPrevFrame && GetChildList(aListID).IsEmpty())) {
2280 // Treat this like an append; still a workaround for bug 343048.
2281 return AppendFrames(aListID, aFrameList);
2282 }
2284 // Collect ColGroupFrames into a separate list and insert those separately
2285 // from the other frames (bug 759249).
2286 ChildListInsertions insertions[2]; // ColGroup, other
2287 const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay();
2288 nsFrameList::FrameLinkEnumerator e(aFrameList);
2289 for (; !aFrameList.IsEmpty(); e.Next()) {
2290 nsIFrame* next = e.NextFrame();
2291 if (!next || next->StyleDisplay()->mDisplay != display->mDisplay) {
2292 nsFrameList head = aFrameList.ExtractHead(e);
2293 if (display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) {
2294 insertions[0].mID = kColGroupList;
2295 insertions[0].mList.AppendFrames(nullptr, head);
2296 } else {
2297 insertions[1].mID = kPrincipalList;
2298 insertions[1].mList.AppendFrames(nullptr, head);
2299 }
2300 if (!next) {
2301 break;
2302 }
2303 display = next->StyleDisplay();
2304 }
2305 }
2306 for (uint32_t i = 0; i < ArrayLength(insertions); ++i) {
2307 // We pass aPrevFrame for both ColGroup and other frames since
2308 // HomogenousInsertFrames will only use it if it's a suitable
2309 // prev-sibling for the frames in the frame list.
2310 if (!insertions[i].mList.IsEmpty()) {
2311 HomogenousInsertFrames(insertions[i].mID, aPrevFrame,
2312 insertions[i].mList);
2313 }
2314 }
2315 return NS_OK;
2316 }
2318 void
2319 nsTableFrame::HomogenousInsertFrames(ChildListID aListID,
2320 nsIFrame* aPrevFrame,
2321 nsFrameList& aFrameList)
2322 {
2323 // See what kind of frame we have
2324 const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay();
2325 #ifdef DEBUG
2326 // Verify that either all siblings have display:table-column-group, or they
2327 // all have display values different from table-column-group.
2328 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
2329 const nsStyleDisplay* nextDisplay = e.get()->StyleDisplay();
2330 MOZ_ASSERT((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) ==
2331 (nextDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP),
2332 "heterogenous childlist");
2333 }
2334 #endif
2335 if (aPrevFrame) {
2336 const nsStyleDisplay* prevDisplay = aPrevFrame->StyleDisplay();
2337 // Make sure they belong on the same frame list
2338 if ((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) !=
2339 (prevDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP)) {
2340 // the previous frame is not valid, see comment at ::AppendFrames
2341 // XXXbz Using content indices here means XBL will get screwed
2342 // over... Oh, well.
2343 nsIFrame* pseudoFrame = aFrameList.FirstChild();
2344 nsIContent* parentContent = GetContent();
2345 nsIContent* content;
2346 aPrevFrame = nullptr;
2347 while (pseudoFrame && (parentContent ==
2348 (content = pseudoFrame->GetContent()))) {
2349 pseudoFrame = pseudoFrame->GetFirstPrincipalChild();
2350 }
2351 nsCOMPtr<nsIContent> container = content->GetParent();
2352 if (MOZ_LIKELY(container)) { // XXX need this null-check, see bug 411823.
2353 int32_t newIndex = container->IndexOf(content);
2354 nsIFrame* kidFrame;
2355 bool isColGroup = (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP ==
2356 display->mDisplay);
2357 nsTableColGroupFrame* lastColGroup;
2358 if (isColGroup) {
2359 kidFrame = mColGroups.FirstChild();
2360 lastColGroup = nsTableColGroupFrame::GetLastRealColGroup(this);
2361 }
2362 else {
2363 kidFrame = mFrames.FirstChild();
2364 }
2365 // Important: need to start at a value smaller than all valid indices
2366 int32_t lastIndex = -1;
2367 while (kidFrame) {
2368 if (isColGroup) {
2369 if (kidFrame == lastColGroup) {
2370 aPrevFrame = kidFrame; // there is no real colgroup after this one
2371 break;
2372 }
2373 }
2374 pseudoFrame = kidFrame;
2375 while (pseudoFrame && (parentContent ==
2376 (content = pseudoFrame->GetContent()))) {
2377 pseudoFrame = pseudoFrame->GetFirstPrincipalChild();
2378 }
2379 int32_t index = container->IndexOf(content);
2380 if (index > lastIndex && index < newIndex) {
2381 lastIndex = index;
2382 aPrevFrame = kidFrame;
2383 }
2384 kidFrame = kidFrame->GetNextSibling();
2385 }
2386 }
2387 }
2388 }
2389 if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) {
2390 NS_ASSERTION(aListID == kColGroupList, "unexpected child list");
2391 // Insert the column group frames
2392 const nsFrameList::Slice& newColgroups =
2393 mColGroups.InsertFrames(nullptr, aPrevFrame, aFrameList);
2394 // find the starting col index for the first new col group
2395 int32_t startColIndex = 0;
2396 if (aPrevFrame) {
2397 nsTableColGroupFrame* prevColGroup =
2398 (nsTableColGroupFrame*)GetFrameAtOrBefore(this, aPrevFrame,
2399 nsGkAtoms::tableColGroupFrame);
2400 if (prevColGroup) {
2401 startColIndex = prevColGroup->GetStartColumnIndex() + prevColGroup->GetColCount();
2402 }
2403 }
2404 InsertColGroups(startColIndex, newColgroups);
2405 } else if (IsRowGroup(display->mDisplay)) {
2406 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
2407 // Insert the frames in the sibling chain
2408 const nsFrameList::Slice& newRowGroups =
2409 mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
2411 InsertRowGroups(newRowGroups);
2412 } else {
2413 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
2414 NS_NOTREACHED("How did we even get here?");
2415 // Just insert the frame and don't worry about reflowing it
2416 mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
2417 return;
2418 }
2420 PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
2421 NS_FRAME_HAS_DIRTY_CHILDREN);
2422 SetGeometryDirty();
2423 #ifdef DEBUG_TABLE_CELLMAP
2424 printf("=== TableFrame::InsertFrames\n");
2425 Dump(true, true, true);
2426 #endif
2427 return;
2428 }
2430 void
2431 nsTableFrame::DoRemoveFrame(ChildListID aListID,
2432 nsIFrame* aOldFrame)
2433 {
2434 if (aListID == kColGroupList) {
2435 nsIFrame* nextColGroupFrame = aOldFrame->GetNextSibling();
2436 nsTableColGroupFrame* colGroup = (nsTableColGroupFrame*)aOldFrame;
2437 int32_t firstColIndex = colGroup->GetStartColumnIndex();
2438 int32_t lastColIndex = firstColIndex + colGroup->GetColCount() - 1;
2439 mColGroups.DestroyFrame(aOldFrame);
2440 nsTableColGroupFrame::ResetColIndices(nextColGroupFrame, firstColIndex);
2441 // remove the cols from the table
2442 int32_t colX;
2443 for (colX = lastColIndex; colX >= firstColIndex; colX--) {
2444 nsTableColFrame* colFrame = mColFrames.SafeElementAt(colX);
2445 if (colFrame) {
2446 RemoveCol(colGroup, colX, true, false);
2447 }
2448 }
2450 int32_t numAnonymousColsToAdd = GetColCount() - mColFrames.Length();
2451 if (numAnonymousColsToAdd > 0) {
2452 // this sets the child list, updates the col cache and cell map
2453 AppendAnonymousColFrames(numAnonymousColsToAdd);
2454 }
2456 } else {
2457 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
2458 nsTableRowGroupFrame* rgFrame =
2459 static_cast<nsTableRowGroupFrame*>(aOldFrame);
2460 // remove the row group from the cell map
2461 nsTableCellMap* cellMap = GetCellMap();
2462 if (cellMap) {
2463 cellMap->RemoveGroupCellMap(rgFrame);
2464 }
2466 // remove the row group frame from the sibling chain
2467 mFrames.DestroyFrame(aOldFrame);
2469 // the removal of a row group changes the cellmap, the columns might change
2470 if (cellMap) {
2471 cellMap->Synchronize(this);
2472 // Create an empty slice
2473 ResetRowIndices(nsFrameList::Slice(mFrames, nullptr, nullptr));
2474 nsIntRect damageArea;
2475 cellMap->RebuildConsideringCells(nullptr, nullptr, 0, 0, false, damageArea);
2477 static_cast<nsTableFrame*>(FirstInFlow())->MatchCellMapToColCache(cellMap);
2478 }
2479 }
2480 }
2482 nsresult
2483 nsTableFrame::RemoveFrame(ChildListID aListID,
2484 nsIFrame* aOldFrame)
2485 {
2486 NS_ASSERTION(aListID == kColGroupList ||
2487 NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP !=
2488 aOldFrame->StyleDisplay()->mDisplay,
2489 "Wrong list name; use kColGroupList iff colgroup");
2490 nsIPresShell* shell = PresContext()->PresShell();
2491 nsTableFrame* lastParent = nullptr;
2492 while (aOldFrame) {
2493 nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
2494 nsTableFrame* parent = static_cast<nsTableFrame*>(aOldFrame->GetParent());
2495 if (parent != lastParent) {
2496 parent->DrainSelfOverflowList();
2497 }
2498 parent->DoRemoveFrame(aListID, aOldFrame);
2499 aOldFrame = oldFrameNextContinuation;
2500 if (parent != lastParent) {
2501 // for now, just bail and recalc all of the collapsing borders
2502 // as the cellmap changes we need to recalc
2503 if (parent->IsBorderCollapse()) {
2504 parent->SetFullBCDamageArea();
2505 }
2506 parent->SetGeometryDirty();
2507 shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange,
2508 NS_FRAME_HAS_DIRTY_CHILDREN);
2509 lastParent = parent;
2510 }
2511 }
2512 #ifdef DEBUG_TABLE_CELLMAP
2513 printf("=== TableFrame::RemoveFrame\n");
2514 Dump(true, true, true);
2515 #endif
2516 return NS_OK;
2517 }
2519 /* virtual */ nsMargin
2520 nsTableFrame::GetUsedBorder() const
2521 {
2522 if (!IsBorderCollapse())
2523 return nsContainerFrame::GetUsedBorder();
2525 return GetIncludedOuterBCBorder();
2526 }
2528 /* virtual */ nsMargin
2529 nsTableFrame::GetUsedPadding() const
2530 {
2531 if (!IsBorderCollapse())
2532 return nsContainerFrame::GetUsedPadding();
2534 return nsMargin(0,0,0,0);
2535 }
2537 /* virtual */ nsMargin
2538 nsTableFrame::GetUsedMargin() const
2539 {
2540 // The margin is inherited to the outer table frame via
2541 // the ::-moz-table-outer rule in ua.css.
2542 return nsMargin(0, 0, 0, 0);
2543 }
2545 // Destructor function for BCPropertyData properties
2546 static void
2547 DestroyBCProperty(void* aPropertyValue)
2548 {
2549 delete static_cast<BCPropertyData*>(aPropertyValue);
2550 }
2552 NS_DECLARE_FRAME_PROPERTY(TableBCProperty, DestroyBCProperty)
2554 BCPropertyData*
2555 nsTableFrame::GetBCProperty(bool aCreateIfNecessary) const
2556 {
2557 FrameProperties props = Properties();
2558 BCPropertyData* value = static_cast<BCPropertyData*>
2559 (props.Get(TableBCProperty()));
2560 if (!value && aCreateIfNecessary) {
2561 value = new BCPropertyData();
2562 props.Set(TableBCProperty(), value);
2563 }
2565 return value;
2566 }
2568 static void
2569 DivideBCBorderSize(BCPixelSize aPixelSize,
2570 BCPixelSize& aSmallHalf,
2571 BCPixelSize& aLargeHalf)
2572 {
2573 aSmallHalf = aPixelSize / 2;
2574 aLargeHalf = aPixelSize - aSmallHalf;
2575 }
2577 nsMargin
2578 nsTableFrame::GetOuterBCBorder() const
2579 {
2580 if (NeedToCalcBCBorders())
2581 const_cast<nsTableFrame*>(this)->CalcBCBorders();
2583 nsMargin border(0, 0, 0, 0);
2584 int32_t p2t = nsPresContext::AppUnitsPerCSSPixel();
2585 BCPropertyData* propData = GetBCProperty();
2586 if (propData) {
2587 border.top = BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth);
2588 border.right = BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightBorderWidth);
2589 border.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth);
2590 border.left = BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftBorderWidth);
2591 }
2592 return border;
2593 }
2595 nsMargin
2596 nsTableFrame::GetIncludedOuterBCBorder() const
2597 {
2598 if (NeedToCalcBCBorders())
2599 const_cast<nsTableFrame*>(this)->CalcBCBorders();
2601 nsMargin border(0, 0, 0, 0);
2602 int32_t p2t = nsPresContext::AppUnitsPerCSSPixel();
2603 BCPropertyData* propData = GetBCProperty();
2604 if (propData) {
2605 border.top += BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth);
2606 border.right += BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightCellBorderWidth);
2607 border.bottom += BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth);
2608 border.left += BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftCellBorderWidth);
2609 }
2610 return border;
2611 }
2613 nsMargin
2614 nsTableFrame::GetExcludedOuterBCBorder() const
2615 {
2616 return GetOuterBCBorder() - GetIncludedOuterBCBorder();
2617 }
2619 static
2620 void GetSeparateModelBorderPadding(const nsHTMLReflowState* aReflowState,
2621 nsStyleContext& aStyleContext,
2622 nsMargin& aBorderPadding)
2623 {
2624 // XXXbz Either we _do_ have a reflow state and then we can use its
2625 // mComputedBorderPadding or we don't and then we get the padding
2626 // wrong!
2627 const nsStyleBorder* border = aStyleContext.StyleBorder();
2628 aBorderPadding = border->GetComputedBorder();
2629 if (aReflowState) {
2630 aBorderPadding += aReflowState->ComputedPhysicalPadding();
2631 }
2632 }
2634 nsMargin
2635 nsTableFrame::GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const
2636 {
2637 nsMargin offset(0,0,0,0);
2638 if (IsBorderCollapse()) {
2639 offset = GetIncludedOuterBCBorder();
2640 }
2641 else {
2642 GetSeparateModelBorderPadding(aReflowState, *mStyleContext, offset);
2643 }
2644 return offset;
2645 }
2647 void
2648 nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState)
2649 {
2650 nsMargin collapseBorder;
2651 nsMargin padding(0,0,0,0);
2652 nsMargin* pCollapseBorder = nullptr;
2653 nsPresContext* presContext = PresContext();
2654 if (IsBorderCollapse()) {
2655 nsTableRowGroupFrame* rgFrame =
2656 static_cast<nsTableRowGroupFrame*>(aReflowState.frame);
2657 pCollapseBorder = rgFrame->GetBCBorderWidth(collapseBorder);
2658 }
2659 aReflowState.Init(presContext, -1, -1, pCollapseBorder, &padding);
2661 NS_ASSERTION(!mBits.mResizedColumns ||
2662 !aReflowState.parentReflowState->mFlags.mSpecialHeightReflow,
2663 "should not resize columns on special height reflow");
2664 if (mBits.mResizedColumns) {
2665 aReflowState.mFlags.mHResize = true;
2666 }
2667 }
2669 // Position and size aKidFrame and update our reflow state. The origin of
2670 // aKidRect is relative to the upper-left origin of our frame
2671 void nsTableFrame::PlaceChild(nsTableReflowState& aReflowState,
2672 nsIFrame* aKidFrame,
2673 nsHTMLReflowMetrics& aKidDesiredSize,
2674 const nsRect& aOriginalKidRect,
2675 const nsRect& aOriginalKidVisualOverflow)
2676 {
2677 bool isFirstReflow =
2678 (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
2680 // Place and size the child
2681 FinishReflowChild(aKidFrame, PresContext(), aKidDesiredSize, nullptr,
2682 aReflowState.x, aReflowState.y, 0);
2684 InvalidateTableFrame(aKidFrame, aOriginalKidRect, aOriginalKidVisualOverflow,
2685 isFirstReflow);
2687 // Adjust the running y-offset
2688 aReflowState.y += aKidDesiredSize.Height();
2690 // If our height is constrained, then update the available height
2691 if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
2692 aReflowState.availSize.height -= aKidDesiredSize.Height();
2693 }
2694 }
2696 void
2697 nsTableFrame::OrderRowGroups(RowGroupArray& aChildren,
2698 nsTableRowGroupFrame** aHead,
2699 nsTableRowGroupFrame** aFoot) const
2700 {
2701 aChildren.Clear();
2702 nsTableRowGroupFrame* head = nullptr;
2703 nsTableRowGroupFrame* foot = nullptr;
2705 nsIFrame* kidFrame = mFrames.FirstChild();
2706 while (kidFrame) {
2707 const nsStyleDisplay* kidDisplay = kidFrame->StyleDisplay();
2708 nsTableRowGroupFrame* rowGroup =
2709 static_cast<nsTableRowGroupFrame*>(kidFrame);
2711 switch (kidDisplay->mDisplay) {
2712 case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
2713 if (head) { // treat additional thead like tbody
2714 aChildren.AppendElement(rowGroup);
2715 }
2716 else {
2717 head = rowGroup;
2718 }
2719 break;
2720 case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
2721 if (foot) { // treat additional tfoot like tbody
2722 aChildren.AppendElement(rowGroup);
2723 }
2724 else {
2725 foot = rowGroup;
2726 }
2727 break;
2728 case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
2729 aChildren.AppendElement(rowGroup);
2730 break;
2731 default:
2732 NS_NOTREACHED("How did this produce an nsTableRowGroupFrame?");
2733 // Just ignore it
2734 break;
2735 }
2736 // Get the next sibling but skip it if it's also the next-in-flow, since
2737 // a next-in-flow will not be part of the current table.
2738 while (kidFrame) {
2739 nsIFrame* nif = kidFrame->GetNextInFlow();
2740 kidFrame = kidFrame->GetNextSibling();
2741 if (kidFrame != nif)
2742 break;
2743 }
2744 }
2746 // put the thead first
2747 if (head) {
2748 aChildren.InsertElementAt(0, head);
2749 }
2750 if (aHead)
2751 *aHead = head;
2752 // put the tfoot after the last tbody
2753 if (foot) {
2754 aChildren.AppendElement(foot);
2755 }
2756 if (aFoot)
2757 *aFoot = foot;
2758 }
2760 nsTableRowGroupFrame*
2761 nsTableFrame::GetTHead() const
2762 {
2763 nsIFrame* kidFrame = mFrames.FirstChild();
2764 while (kidFrame) {
2765 if (kidFrame->StyleDisplay()->mDisplay ==
2766 NS_STYLE_DISPLAY_TABLE_HEADER_GROUP) {
2767 return static_cast<nsTableRowGroupFrame*>(kidFrame);
2768 }
2770 // Get the next sibling but skip it if it's also the next-in-flow, since
2771 // a next-in-flow will not be part of the current table.
2772 while (kidFrame) {
2773 nsIFrame* nif = kidFrame->GetNextInFlow();
2774 kidFrame = kidFrame->GetNextSibling();
2775 if (kidFrame != nif)
2776 break;
2777 }
2778 }
2780 return nullptr;
2781 }
2783 nsTableRowGroupFrame*
2784 nsTableFrame::GetTFoot() const
2785 {
2786 nsIFrame* kidFrame = mFrames.FirstChild();
2787 while (kidFrame) {
2788 if (kidFrame->StyleDisplay()->mDisplay ==
2789 NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP) {
2790 return static_cast<nsTableRowGroupFrame*>(kidFrame);
2791 }
2793 // Get the next sibling but skip it if it's also the next-in-flow, since
2794 // a next-in-flow will not be part of the current table.
2795 while (kidFrame) {
2796 nsIFrame* nif = kidFrame->GetNextInFlow();
2797 kidFrame = kidFrame->GetNextSibling();
2798 if (kidFrame != nif)
2799 break;
2800 }
2801 }
2803 return nullptr;
2804 }
2806 static bool
2807 IsRepeatable(nscoord aFrameHeight, nscoord aPageHeight)
2808 {
2809 return aFrameHeight < (aPageHeight / 4);
2810 }
2812 nsresult
2813 nsTableFrame::SetupHeaderFooterChild(const nsTableReflowState& aReflowState,
2814 nsTableRowGroupFrame* aFrame,
2815 nscoord* aDesiredHeight)
2816 {
2817 nsPresContext* presContext = PresContext();
2818 nscoord pageHeight = presContext->GetPageSize().height;
2820 // Reflow the child with unconstrainted height
2821 nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState,
2822 aFrame,
2823 nsSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE),
2824 -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
2825 InitChildReflowState(kidReflowState);
2826 kidReflowState.mFlags.mIsTopOfPage = true;
2827 nsHTMLReflowMetrics desiredSize(aReflowState.reflowState);
2828 desiredSize.Width() = desiredSize.Height() = 0;
2829 nsReflowStatus status;
2830 nsresult rv = ReflowChild(aFrame, presContext, desiredSize, kidReflowState,
2831 aReflowState.x, aReflowState.y, 0, status);
2832 NS_ENSURE_SUCCESS(rv, rv);
2833 // The child will be reflowed again "for real" so no need to place it now
2835 aFrame->SetRepeatable(IsRepeatable(desiredSize.Height(), pageHeight));
2836 *aDesiredHeight = desiredSize.Height();
2837 return NS_OK;
2838 }
2840 void
2841 nsTableFrame::PlaceRepeatedFooter(nsTableReflowState& aReflowState,
2842 nsTableRowGroupFrame *aTfoot,
2843 nscoord aFooterHeight)
2844 {
2845 nsPresContext* presContext = PresContext();
2846 nsSize kidAvailSize(aReflowState.availSize);
2847 kidAvailSize.height = aFooterHeight;
2848 nsHTMLReflowState footerReflowState(presContext,
2849 aReflowState.reflowState,
2850 aTfoot, kidAvailSize,
2851 -1, -1,
2852 nsHTMLReflowState::CALLER_WILL_INIT);
2853 InitChildReflowState(footerReflowState);
2854 aReflowState.y += GetCellSpacingY();
2856 nsRect origTfootRect = aTfoot->GetRect();
2857 nsRect origTfootVisualOverflow = aTfoot->GetVisualOverflowRect();
2859 nsReflowStatus footerStatus;
2860 nsHTMLReflowMetrics desiredSize(aReflowState.reflowState);
2861 desiredSize.Width() = desiredSize.Height() = 0;
2862 ReflowChild(aTfoot, presContext, desiredSize, footerReflowState,
2863 aReflowState.x, aReflowState.y, 0, footerStatus);
2864 PlaceChild(aReflowState, aTfoot, desiredSize, origTfootRect,
2865 origTfootVisualOverflow);
2866 }
2868 // Reflow the children based on the avail size and reason in aReflowState
2869 // update aReflowMetrics a aStatus
2870 nsresult
2871 nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState,
2872 nsReflowStatus& aStatus,
2873 nsIFrame*& aLastChildReflowed,
2874 nsOverflowAreas& aOverflowAreas)
2875 {
2876 aStatus = NS_FRAME_COMPLETE;
2877 aLastChildReflowed = nullptr;
2879 nsIFrame* prevKidFrame = nullptr;
2880 nsresult rv = NS_OK;
2881 nscoord cellSpacingY = GetCellSpacingY();
2883 nsPresContext* presContext = PresContext();
2884 // XXXldb Should we be checking constrained height instead?
2885 // tables are not able to pull back children from its next inflow, so even
2886 // under paginated contexts tables are should not paginate if they are inside
2887 // column set
2888 bool isPaginated = presContext->IsPaginated() &&
2889 NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height &&
2890 aReflowState.reflowState.mFlags.mTableIsSplittable;
2892 aOverflowAreas.Clear();
2894 bool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() ||
2895 mBits.mResizedColumns ||
2896 IsGeometryDirty();
2898 RowGroupArray rowGroups;
2899 nsTableRowGroupFrame *thead, *tfoot;
2900 OrderRowGroups(rowGroups, &thead, &tfoot);
2901 bool pageBreak = false;
2902 nscoord footerHeight = 0;
2904 // Determine the repeatablility of headers and footers, and also the desired
2905 // height of any repeatable footer.
2906 // The repeatability of headers on continued tables is handled
2907 // when they are created in nsCSSFrameConstructor::CreateContinuingTableFrame.
2908 // We handle the repeatability of footers again here because we need to
2909 // determine the footer's height anyway. We could perhaps optimize by
2910 // using the footer's prev-in-flow's height instead of reflowing it again,
2911 // but there's no real need.
2912 if (isPaginated) {
2913 if (thead && !GetPrevInFlow()) {
2914 nscoord desiredHeight;
2915 rv = SetupHeaderFooterChild(aReflowState, thead, &desiredHeight);
2916 if (NS_FAILED(rv))
2917 return rv;
2918 }
2919 if (tfoot) {
2920 rv = SetupHeaderFooterChild(aReflowState, tfoot, &footerHeight);
2921 if (NS_FAILED(rv))
2922 return rv;
2923 }
2924 }
2925 // if the child is a tbody in paginated mode reduce the height by a repeated footer
2926 bool allowRepeatedFooter = false;
2927 for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) {
2928 nsIFrame* kidFrame = rowGroups[childX];
2929 // Get the frame state bits
2930 // See if we should only reflow the dirty child frames
2931 if (reflowAllKids ||
2932 NS_SUBTREE_DIRTY(kidFrame) ||
2933 (aReflowState.reflowState.mFlags.mSpecialHeightReflow &&
2934 (isPaginated || (kidFrame->GetStateBits() &
2935 NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) {
2936 if (pageBreak) {
2937 if (allowRepeatedFooter) {
2938 PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
2939 }
2940 else if (tfoot && tfoot->IsRepeatable()) {
2941 tfoot->SetRepeatable(false);
2942 }
2943 PushChildren(rowGroups, childX);
2944 aStatus = NS_FRAME_NOT_COMPLETE;
2945 break;
2946 }
2948 nsSize kidAvailSize(aReflowState.availSize);
2949 allowRepeatedFooter = false;
2950 if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.height)) {
2951 nsTableRowGroupFrame* kidRG =
2952 static_cast<nsTableRowGroupFrame*>(kidFrame);
2953 if (kidRG != thead && kidRG != tfoot && tfoot && tfoot->IsRepeatable()) {
2954 // the child is a tbody and there is a repeatable footer
2955 NS_ASSERTION(tfoot == rowGroups[rowGroups.Length() - 1], "Missing footer!");
2956 if (footerHeight + cellSpacingY < kidAvailSize.height) {
2957 allowRepeatedFooter = true;
2958 kidAvailSize.height -= footerHeight + cellSpacingY;
2959 }
2960 }
2961 }
2963 nsRect oldKidRect = kidFrame->GetRect();
2964 nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect();
2966 nsHTMLReflowMetrics desiredSize(aReflowState.reflowState);
2967 desiredSize.Width() = desiredSize.Height() = 0;
2969 // Reflow the child into the available space
2970 nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState,
2971 kidFrame, kidAvailSize,
2972 -1, -1,
2973 nsHTMLReflowState::CALLER_WILL_INIT);
2974 InitChildReflowState(kidReflowState);
2976 // If this isn't the first row group, and the previous row group has a
2977 // nonzero YMost, then we can't be at the top of the page.
2978 // We ignore a repeated head row group in this check to avoid causing
2979 // infinite loops in some circumstances - see bug 344883.
2980 if (childX > ((thead && IsRepeatedFrame(thead)) ? 1u : 0u) &&
2981 (rowGroups[childX - 1]->GetRect().YMost() > 0)) {
2982 kidReflowState.mFlags.mIsTopOfPage = false;
2983 }
2984 aReflowState.y += cellSpacingY;
2985 if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
2986 aReflowState.availSize.height -= cellSpacingY;
2987 }
2988 // record the presence of a next in flow, it might get destroyed so we
2989 // need to reorder the row group array
2990 bool reorder = false;
2991 if (kidFrame->GetNextInFlow())
2992 reorder = true;
2994 rv = ReflowChild(kidFrame, presContext, desiredSize, kidReflowState,
2995 aReflowState.x, aReflowState.y, 0, aStatus);
2997 if (reorder) {
2998 // reorder row groups the reflow may have changed the nextinflows
2999 OrderRowGroups(rowGroups, &thead, &tfoot);
3000 childX = rowGroups.IndexOf(kidFrame);
3001 if (childX == RowGroupArray::NoIndex) {
3002 // XXXbz can this happen?
3003 childX = rowGroups.Length();
3004 }
3005 }
3006 if (isPaginated && !NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
3007 ShouldAvoidBreakInside(aReflowState.reflowState)) {
3008 aStatus = NS_INLINE_LINE_BREAK_BEFORE();
3009 break;
3010 }
3011 // see if the rowgroup did not fit on this page might be pushed on
3012 // the next page
3013 if (isPaginated &&
3014 (NS_INLINE_IS_BREAK_BEFORE(aStatus) ||
3015 (NS_FRAME_IS_COMPLETE(aStatus) &&
3016 (NS_UNCONSTRAINEDSIZE != kidReflowState.AvailableHeight()) &&
3017 kidReflowState.AvailableHeight() < desiredSize.Height()))) {
3018 if (ShouldAvoidBreakInside(aReflowState.reflowState)) {
3019 aStatus = NS_INLINE_LINE_BREAK_BEFORE();
3020 break;
3021 }
3022 // if we are on top of the page place with dataloss
3023 if (kidReflowState.mFlags.mIsTopOfPage) {
3024 if (childX+1 < rowGroups.Length()) {
3025 nsIFrame* nextRowGroupFrame = rowGroups[childX + 1];
3026 if (nextRowGroupFrame) {
3027 PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
3028 oldKidVisualOverflow);
3029 if (allowRepeatedFooter) {
3030 PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
3031 }
3032 else if (tfoot && tfoot->IsRepeatable()) {
3033 tfoot->SetRepeatable(false);
3034 }
3035 aStatus = NS_FRAME_NOT_COMPLETE;
3036 PushChildren(rowGroups, childX + 1);
3037 aLastChildReflowed = kidFrame;
3038 break;
3039 }
3040 }
3041 }
3042 else { // we are not on top, push this rowgroup onto the next page
3043 if (prevKidFrame) { // we had a rowgroup before so push this
3044 if (allowRepeatedFooter) {
3045 PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
3046 }
3047 else if (tfoot && tfoot->IsRepeatable()) {
3048 tfoot->SetRepeatable(false);
3049 }
3050 aStatus = NS_FRAME_NOT_COMPLETE;
3051 PushChildren(rowGroups, childX);
3052 aLastChildReflowed = prevKidFrame;
3053 break;
3054 }
3055 else { // we can't push so lets make clear how much space we need
3056 PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
3057 oldKidVisualOverflow);
3058 aLastChildReflowed = kidFrame;
3059 if (allowRepeatedFooter) {
3060 PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
3061 aLastChildReflowed = tfoot;
3062 }
3063 break;
3064 }
3065 }
3066 }
3068 aLastChildReflowed = kidFrame;
3070 pageBreak = false;
3071 // see if there is a page break after this row group or before the next one
3072 if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated &&
3073 (NS_UNCONSTRAINEDSIZE != kidReflowState.AvailableHeight())) {
3074 nsIFrame* nextKid =
3075 (childX + 1 < rowGroups.Length()) ? rowGroups[childX + 1] : nullptr;
3076 pageBreak = PageBreakAfter(kidFrame, nextKid);
3077 }
3079 // Place the child
3080 PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
3081 oldKidVisualOverflow);
3083 // Remember where we just were in case we end up pushing children
3084 prevKidFrame = kidFrame;
3086 // Special handling for incomplete children
3087 if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
3088 nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
3089 if (!kidNextInFlow) {
3090 // The child doesn't have a next-in-flow so create a continuing
3091 // frame. This hooks the child into the flow
3092 kidNextInFlow = presContext->PresShell()->FrameConstructor()->
3093 CreateContinuingFrame(presContext, kidFrame, this);
3095 // Insert the kid's new next-in-flow into our sibling list...
3096 mFrames.InsertFrame(nullptr, kidFrame, kidNextInFlow);
3097 // and in rowGroups after childX so that it will get pushed below.
3098 rowGroups.InsertElementAt(childX + 1,
3099 static_cast<nsTableRowGroupFrame*>(kidNextInFlow));
3100 } else if (kidNextInFlow == kidFrame->GetNextSibling()) {
3101 // OrderRowGroups excludes NIFs in the child list from 'rowGroups'
3102 // so we deal with that here to make sure they get pushed.
3103 MOZ_ASSERT(!rowGroups.Contains(kidNextInFlow),
3104 "OrderRowGroups must not put our NIF in 'rowGroups'");
3105 rowGroups.InsertElementAt(childX + 1,
3106 static_cast<nsTableRowGroupFrame*>(kidNextInFlow));
3107 }
3109 // We've used up all of our available space so push the remaining
3110 // children.
3111 if (allowRepeatedFooter) {
3112 PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
3113 }
3114 else if (tfoot && tfoot->IsRepeatable()) {
3115 tfoot->SetRepeatable(false);
3116 }
3118 nsIFrame* nextSibling = kidFrame->GetNextSibling();
3119 if (nextSibling) {
3120 PushChildren(rowGroups, childX + 1);
3121 }
3122 break;
3123 }
3124 }
3125 else { // it isn't being reflowed
3126 aReflowState.y += cellSpacingY;
3127 nsRect kidRect = kidFrame->GetRect();
3128 if (kidRect.y != aReflowState.y) {
3129 // invalidate the old position
3130 kidFrame->InvalidateFrameSubtree();
3131 kidRect.y = aReflowState.y;
3132 kidFrame->SetRect(kidRect); // move to the new position
3133 RePositionViews(kidFrame);
3134 // invalidate the new position
3135 kidFrame->InvalidateFrameSubtree();
3136 }
3137 aReflowState.y += kidRect.height;
3139 // If our height is constrained then update the available height.
3140 if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
3141 aReflowState.availSize.height -= cellSpacingY + kidRect.height;
3142 }
3143 }
3144 ConsiderChildOverflow(aOverflowAreas, kidFrame);
3145 }
3147 // We've now propagated the column resizes and geometry changes to all
3148 // the children.
3149 mBits.mResizedColumns = false;
3150 ClearGeometryDirty();
3152 return rv;
3153 }
3155 void
3156 nsTableFrame::ReflowColGroups(nsRenderingContext *aRenderingContext)
3157 {
3158 if (!GetPrevInFlow() && !HaveReflowedColGroups()) {
3159 nsHTMLReflowMetrics kidMet(GetWritingMode());
3160 nsPresContext *presContext = PresContext();
3161 for (nsIFrame* kidFrame = mColGroups.FirstChild(); kidFrame;
3162 kidFrame = kidFrame->GetNextSibling()) {
3163 if (NS_SUBTREE_DIRTY(kidFrame)) {
3164 // The column groups don't care about dimensions or reflow states.
3165 nsHTMLReflowState kidReflowState(presContext, kidFrame,
3166 aRenderingContext, nsSize(0,0));
3167 nsReflowStatus cgStatus;
3168 ReflowChild(kidFrame, presContext, kidMet, kidReflowState, 0, 0, 0,
3169 cgStatus);
3170 FinishReflowChild(kidFrame, presContext, kidMet, nullptr, 0, 0, 0);
3171 }
3172 }
3173 SetHaveReflowedColGroups(true);
3174 }
3175 }
3177 void
3178 nsTableFrame::CalcDesiredHeight(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize)
3179 {
3180 nsTableCellMap* cellMap = GetCellMap();
3181 if (!cellMap) {
3182 NS_ASSERTION(false, "never ever call me until the cell map is built!");
3183 aDesiredSize.Height() = 0;
3184 return;
3185 }
3186 nscoord cellSpacingY = GetCellSpacingY();
3187 nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
3189 // get the natural height based on the last child's (row group) rect
3190 RowGroupArray rowGroups;
3191 OrderRowGroups(rowGroups);
3192 if (rowGroups.IsEmpty()) {
3193 // tables can be used as rectangular items without content
3194 nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
3195 if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedHeight) &&
3196 (tableSpecifiedHeight > 0) &&
3197 eCompatibility_NavQuirks != PresContext()->CompatibilityMode()) {
3198 // empty tables should not have a size in quirks mode
3199 aDesiredSize.Height() = tableSpecifiedHeight;
3200 }
3201 else
3202 aDesiredSize.Height() = 0;
3203 return;
3204 }
3205 int32_t rowCount = cellMap->GetRowCount();
3206 int32_t colCount = cellMap->GetColCount();
3207 nscoord desiredHeight = borderPadding.top + borderPadding.bottom;
3208 if (rowCount > 0 && colCount > 0) {
3209 desiredHeight += cellSpacingY;
3210 for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
3211 desiredHeight += rowGroups[rgX]->GetSize().height + cellSpacingY;
3212 }
3213 }
3215 // see if a specified table height requires dividing additional space to rows
3216 if (!GetPrevInFlow()) {
3217 nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
3218 if ((tableSpecifiedHeight > 0) &&
3219 (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) &&
3220 (tableSpecifiedHeight > desiredHeight)) {
3221 // proportionately distribute the excess height to unconstrained rows in each
3222 // unconstrained row group.
3223 DistributeHeightToRows(aReflowState, tableSpecifiedHeight - desiredHeight);
3224 // this might have changed the overflow area incorporate the childframe overflow area.
3225 for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) {
3226 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
3227 }
3228 desiredHeight = tableSpecifiedHeight;
3229 }
3230 }
3231 aDesiredSize.Height() = desiredHeight;
3232 }
3234 static
3235 void ResizeCells(nsTableFrame& aTableFrame)
3236 {
3237 nsTableFrame::RowGroupArray rowGroups;
3238 aTableFrame.OrderRowGroups(rowGroups);
3239 nsHTMLReflowMetrics tableDesiredSize(aTableFrame.GetWritingMode()); // ???
3240 nsRect tableRect = aTableFrame.GetRect();
3241 tableDesiredSize.Width() = tableRect.width;
3242 tableDesiredSize.Height() = tableRect.height;
3243 tableDesiredSize.SetOverflowAreasToDesiredBounds();
3245 for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
3246 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3248 nsRect rowGroupRect = rgFrame->GetRect();
3249 nsHTMLReflowMetrics groupDesiredSize(tableDesiredSize.GetWritingMode());
3250 groupDesiredSize.Width() = rowGroupRect.width;
3251 groupDesiredSize.Height() = rowGroupRect.height;
3252 groupDesiredSize.SetOverflowAreasToDesiredBounds();
3254 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3255 while (rowFrame) {
3256 rowFrame->DidResize();
3257 rgFrame->ConsiderChildOverflow(groupDesiredSize.mOverflowAreas, rowFrame);
3258 rowFrame = rowFrame->GetNextRow();
3259 }
3260 rgFrame->FinishAndStoreOverflow(&groupDesiredSize);
3261 tableDesiredSize.mOverflowAreas.UnionWith(groupDesiredSize.mOverflowAreas +
3262 rgFrame->GetPosition());
3263 }
3264 aTableFrame.FinishAndStoreOverflow(&tableDesiredSize);
3265 }
3267 void
3268 nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
3269 nscoord aAmount)
3270 {
3271 nscoord cellSpacingY = GetCellSpacingY();
3273 nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
3275 RowGroupArray rowGroups;
3276 OrderRowGroups(rowGroups);
3278 nscoord amountUsed = 0;
3279 // distribute space to each pct height row whose row group doesn't have a computed
3280 // height, and base the pct on the table height. If the row group had a computed
3281 // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights
3282 nscoord pctBasis = aReflowState.ComputedHeight() - (GetCellSpacingY() * (GetRowCount() + 1));
3283 nscoord yOriginRG = borderPadding.top + GetCellSpacingY();
3284 nscoord yEndRG = yOriginRG;
3285 uint32_t rgX;
3286 for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
3287 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3288 nscoord amountUsedByRG = 0;
3289 nscoord yOriginRow = 0;
3290 nsRect rgRect = rgFrame->GetRect();
3291 if (!rgFrame->HasStyleHeight()) {
3292 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3293 while (rowFrame) {
3294 nsRect rowRect = rowFrame->GetRect();
3295 if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) {
3296 nscoord pctHeight = rowFrame->GetHeight(pctBasis);
3297 nscoord amountForRow = std::min(aAmount - amountUsed, pctHeight - rowRect.height);
3298 if (amountForRow > 0) {
3299 nsRect oldRowRect = rowRect;
3300 rowRect.height += amountForRow;
3301 // XXXbz we don't need to change rowRect.y to be yOriginRow?
3302 rowFrame->SetRect(rowRect);
3303 yOriginRow += rowRect.height + cellSpacingY;
3304 yEndRG += rowRect.height + cellSpacingY;
3305 amountUsed += amountForRow;
3306 amountUsedByRG += amountForRow;
3307 //rowFrame->DidResize();
3308 nsTableFrame::RePositionViews(rowFrame);
3310 rgFrame->InvalidateFrameWithRect(oldRowRect);
3311 rgFrame->InvalidateFrame();
3312 }
3313 }
3314 else {
3315 if (amountUsed > 0 && yOriginRow != rowRect.y &&
3316 !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
3317 rowFrame->InvalidateFrameSubtree();
3318 rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
3319 nsTableFrame::RePositionViews(rowFrame);
3320 rowFrame->InvalidateFrameSubtree();
3321 }
3322 yOriginRow += rowRect.height + cellSpacingY;
3323 yEndRG += rowRect.height + cellSpacingY;
3324 }
3325 rowFrame = rowFrame->GetNextRow();
3326 }
3327 if (amountUsed > 0) {
3328 if (rgRect.y != yOriginRG) {
3329 rgFrame->InvalidateFrameSubtree();
3330 }
3332 nsRect origRgRect = rgRect;
3333 nsRect origRgVisualOverflow = rgFrame->GetVisualOverflowRect();
3335 rgRect.y = yOriginRG;
3336 rgRect.height += amountUsedByRG;
3338 rgFrame->SetRect(rgRect);
3340 nsTableFrame::InvalidateTableFrame(rgFrame, origRgRect,
3341 origRgVisualOverflow, false);
3342 }
3343 }
3344 else if (amountUsed > 0 && yOriginRG != rgRect.y) {
3345 rgFrame->InvalidateFrameSubtree();
3346 rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG));
3347 // Make sure child views are properly positioned
3348 nsTableFrame::RePositionViews(rgFrame);
3349 rgFrame->InvalidateFrameSubtree();
3350 }
3351 yOriginRG = yEndRG;
3352 }
3354 if (amountUsed >= aAmount) {
3355 ResizeCells(*this);
3356 return;
3357 }
3359 // get the first row without a style height where its row group has an
3360 // unconstrained height
3361 nsTableRowGroupFrame* firstUnStyledRG = nullptr;
3362 nsTableRowFrame* firstUnStyledRow = nullptr;
3363 for (rgX = 0; rgX < rowGroups.Length() && !firstUnStyledRG; rgX++) {
3364 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3365 if (!rgFrame->HasStyleHeight()) {
3366 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3367 while (rowFrame) {
3368 if (!rowFrame->HasStyleHeight()) {
3369 firstUnStyledRG = rgFrame;
3370 firstUnStyledRow = rowFrame;
3371 break;
3372 }
3373 rowFrame = rowFrame->GetNextRow();
3374 }
3375 }
3376 }
3378 nsTableRowFrame* lastEligibleRow = nullptr;
3379 // Accumulate the correct divisor. This will be the total total height of all
3380 // unstyled rows inside unstyled row groups, unless there are none, in which
3381 // case, it will be number of all rows. If the unstyled rows don't have a
3382 // height, divide the space equally among them.
3383 nscoord divisor = 0;
3384 int32_t eligibleRows = 0;
3385 bool expandEmptyRows = false;
3387 if (!firstUnStyledRow) {
3388 // there is no unstyled row
3389 divisor = GetRowCount();
3390 }
3391 else {
3392 for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
3393 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3394 if (!firstUnStyledRG || !rgFrame->HasStyleHeight()) {
3395 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3396 while (rowFrame) {
3397 if (!firstUnStyledRG || !rowFrame->HasStyleHeight()) {
3398 NS_ASSERTION(rowFrame->GetSize().height >= 0,
3399 "negative row frame height");
3400 divisor += rowFrame->GetSize().height;
3401 eligibleRows++;
3402 lastEligibleRow = rowFrame;
3403 }
3404 rowFrame = rowFrame->GetNextRow();
3405 }
3406 }
3407 }
3408 if (divisor <= 0) {
3409 if (eligibleRows > 0) {
3410 expandEmptyRows = true;
3411 }
3412 else {
3413 NS_ERROR("invalid divisor");
3414 return;
3415 }
3416 }
3417 }
3418 // allocate the extra height to the unstyled row groups and rows
3419 nscoord heightToDistribute = aAmount - amountUsed;
3420 yOriginRG = borderPadding.top + cellSpacingY;
3421 yEndRG = yOriginRG;
3422 for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
3423 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3424 nscoord amountUsedByRG = 0;
3425 nscoord yOriginRow = 0;
3426 nsRect rgRect = rgFrame->GetRect();
3427 nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect();
3428 // see if there is an eligible row group or we distribute to all rows
3429 if (!firstUnStyledRG || !rgFrame->HasStyleHeight() || !eligibleRows) {
3430 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3431 while (rowFrame) {
3432 nsRect rowRect = rowFrame->GetRect();
3433 nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
3434 // see if there is an eligible row or we distribute to all rows
3435 if (!firstUnStyledRow || !rowFrame->HasStyleHeight() || !eligibleRows) {
3436 float ratio;
3437 if (eligibleRows) {
3438 if (!expandEmptyRows) {
3439 // The amount of additional space each row gets is proportional to
3440 // its height
3441 ratio = float(rowRect.height) / float(divisor);
3442 } else {
3443 // empty rows get all the same additional space
3444 ratio = 1.0f / float(eligibleRows);
3445 }
3446 }
3447 else {
3448 // all rows get the same additional space
3449 ratio = 1.0f / float(divisor);
3450 }
3451 // give rows their additional space, except for the last row which
3452 // gets the remainder
3453 nscoord amountForRow = (rowFrame == lastEligibleRow)
3454 ? aAmount - amountUsed : NSToCoordRound(((float)(heightToDistribute)) * ratio);
3455 amountForRow = std::min(amountForRow, aAmount - amountUsed);
3457 if (yOriginRow != rowRect.y) {
3458 rowFrame->InvalidateFrameSubtree();
3459 }
3461 // update the row height
3462 nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width,
3463 rowRect.height + amountForRow);
3464 rowFrame->SetRect(newRowRect);
3466 yOriginRow += newRowRect.height + cellSpacingY;
3467 yEndRG += newRowRect.height + cellSpacingY;
3469 amountUsed += amountForRow;
3470 amountUsedByRG += amountForRow;
3471 NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation");
3472 //rowFrame->DidResize();
3473 nsTableFrame::RePositionViews(rowFrame);
3475 nsTableFrame::InvalidateTableFrame(rowFrame, rowRect, rowVisualOverflow,
3476 false);
3477 }
3478 else {
3479 if (amountUsed > 0 && yOriginRow != rowRect.y) {
3480 rowFrame->InvalidateFrameSubtree();
3481 rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
3482 nsTableFrame::RePositionViews(rowFrame);
3483 rowFrame->InvalidateFrameSubtree();
3484 }
3485 yOriginRow += rowRect.height + cellSpacingY;
3486 yEndRG += rowRect.height + cellSpacingY;
3487 }
3488 rowFrame = rowFrame->GetNextRow();
3489 }
3490 if (amountUsed > 0) {
3491 if (rgRect.y != yOriginRG) {
3492 rgFrame->InvalidateFrameSubtree();
3493 }
3495 rgFrame->SetRect(nsRect(rgRect.x, yOriginRG, rgRect.width,
3496 rgRect.height + amountUsedByRG));
3498 nsTableFrame::InvalidateTableFrame(rgFrame, rgRect, rgVisualOverflow,
3499 false);
3500 }
3501 // Make sure child views are properly positioned
3502 }
3503 else if (amountUsed > 0 && yOriginRG != rgRect.y) {
3504 rgFrame->InvalidateFrameSubtree();
3505 rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG));
3506 // Make sure child views are properly positioned
3507 nsTableFrame::RePositionViews(rgFrame);
3508 rgFrame->InvalidateFrameSubtree();
3509 }
3510 yOriginRG = yEndRG;
3511 }
3513 ResizeCells(*this);
3514 }
3516 int32_t nsTableFrame::GetColumnWidth(int32_t aColIndex)
3517 {
3518 nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
3519 if (this == firstInFlow) {
3520 nsTableColFrame* colFrame = GetColFrame(aColIndex);
3521 return colFrame ? colFrame->GetFinalWidth() : 0;
3522 }
3523 return firstInFlow->GetColumnWidth(aColIndex);
3524 }
3526 // XXX: could cache this. But be sure to check style changes if you do!
3527 nscoord nsTableFrame::GetCellSpacingX()
3528 {
3529 if (IsBorderCollapse())
3530 return 0;
3532 return StyleTableBorder()->mBorderSpacingX;
3533 }
3535 // XXX: could cache this. But be sure to check style changes if you do!
3536 nscoord nsTableFrame::GetCellSpacingY()
3537 {
3538 if (IsBorderCollapse())
3539 return 0;
3541 return StyleTableBorder()->mBorderSpacingY;
3542 }
3545 /* virtual */ nscoord
3546 nsTableFrame::GetBaseline() const
3547 {
3548 nscoord ascent = 0;
3549 RowGroupArray orderedRowGroups;
3550 OrderRowGroups(orderedRowGroups);
3551 nsTableRowFrame* firstRow = nullptr;
3552 for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
3553 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
3554 if (rgFrame->GetRowCount()) {
3555 firstRow = rgFrame->GetFirstRow();
3556 ascent = rgFrame->GetRect().y + firstRow->GetRect().y + firstRow->GetRowBaseline();
3557 break;
3558 }
3559 }
3560 if (!firstRow)
3561 ascent = GetRect().height;
3562 return ascent;
3563 }
3564 /* ----- global methods ----- */
3566 nsIFrame*
3567 NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
3568 {
3569 return new (aPresShell) nsTableFrame(aContext);
3570 }
3572 NS_IMPL_FRAMEARENA_HELPERS(nsTableFrame)
3574 nsTableFrame*
3575 nsTableFrame::GetTableFrame(nsIFrame* aFrame)
3576 {
3577 for (nsIFrame* ancestor = aFrame->GetParent(); ancestor;
3578 ancestor = ancestor->GetParent()) {
3579 if (nsGkAtoms::tableFrame == ancestor->GetType()) {
3580 return static_cast<nsTableFrame*>(ancestor);
3581 }
3582 }
3583 NS_RUNTIMEABORT("unable to find table parent");
3584 return nullptr;
3585 }
3587 nsTableFrame*
3588 nsTableFrame::GetTableFramePassingThrough(nsIFrame* aMustPassThrough,
3589 nsIFrame* aFrame)
3590 {
3591 MOZ_ASSERT(aMustPassThrough == aFrame ||
3592 nsLayoutUtils::IsProperAncestorFrame(aMustPassThrough, aFrame),
3593 "aMustPassThrough should be an ancestor");
3595 // Retrieve the table frame, and ensure that we hit aMustPassThrough on the
3596 // way. If we don't, just return null.
3597 bool hitPassThroughFrame = false;
3598 nsTableFrame* tableFrame = nullptr;
3599 for (nsIFrame* ancestor = aFrame; ancestor; ancestor = ancestor->GetParent()) {
3600 if (ancestor == aMustPassThrough) {
3601 hitPassThroughFrame = true;
3602 }
3603 if (nsGkAtoms::tableFrame == ancestor->GetType()) {
3604 tableFrame = static_cast<nsTableFrame*>(ancestor);
3605 break;
3606 }
3607 }
3609 MOZ_ASSERT(tableFrame, "Should have a table frame here");
3610 return hitPassThroughFrame ? tableFrame : nullptr;
3611 }
3613 bool
3614 nsTableFrame::IsAutoHeight()
3615 {
3616 const nsStyleCoord &height = StylePosition()->mHeight;
3617 // Don't consider calc() here like this quirk for percent.
3618 return height.GetUnit() == eStyleUnit_Auto ||
3619 (height.GetUnit() == eStyleUnit_Percent &&
3620 height.GetPercentValue() <= 0.0f);
3621 }
3623 nscoord
3624 nsTableFrame::CalcBorderBoxHeight(const nsHTMLReflowState& aState)
3625 {
3626 nscoord height = aState.ComputedHeight();
3627 if (NS_AUTOHEIGHT != height) {
3628 nsMargin borderPadding = GetChildAreaOffset(&aState);
3629 height += borderPadding.top + borderPadding.bottom;
3630 }
3631 height = std::max(0, height);
3633 return height;
3634 }
3636 bool
3637 nsTableFrame::IsAutoLayout()
3638 {
3639 if (StyleTable()->mLayoutStrategy == NS_STYLE_TABLE_LAYOUT_AUTO)
3640 return true;
3641 // a fixed-layout inline-table must have a width
3642 // and tables with 'width: -moz-max-content' must be auto-layout
3643 // (at least as long as FixedTableLayoutStrategy::GetPrefWidth returns
3644 // nscoord_MAX)
3645 const nsStyleCoord &width = StylePosition()->mWidth;
3646 return (width.GetUnit() == eStyleUnit_Auto) ||
3647 (width.GetUnit() == eStyleUnit_Enumerated &&
3648 width.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT);
3649 }
3651 #ifdef DEBUG_FRAME_DUMP
3652 nsresult
3653 nsTableFrame::GetFrameName(nsAString& aResult) const
3654 {
3655 return MakeFrameName(NS_LITERAL_STRING("Table"), aResult);
3656 }
3657 #endif
3659 // Find the closet sibling before aPriorChildFrame (including aPriorChildFrame) that
3660 // is of type aChildType
3661 nsIFrame*
3662 nsTableFrame::GetFrameAtOrBefore(nsIFrame* aParentFrame,
3663 nsIFrame* aPriorChildFrame,
3664 nsIAtom* aChildType)
3665 {
3666 nsIFrame* result = nullptr;
3667 if (!aPriorChildFrame) {
3668 return result;
3669 }
3670 if (aChildType == aPriorChildFrame->GetType()) {
3671 return aPriorChildFrame;
3672 }
3674 // aPriorChildFrame is not of type aChildType, so we need start from
3675 // the beginnng and find the closest one
3676 nsIFrame* lastMatchingFrame = nullptr;
3677 nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild();
3678 while (childFrame && (childFrame != aPriorChildFrame)) {
3679 if (aChildType == childFrame->GetType()) {
3680 lastMatchingFrame = childFrame;
3681 }
3682 childFrame = childFrame->GetNextSibling();
3683 }
3684 return lastMatchingFrame;
3685 }
3687 #ifdef DEBUG
3688 void
3689 nsTableFrame::DumpRowGroup(nsIFrame* aKidFrame)
3690 {
3691 if (!aKidFrame)
3692 return;
3694 nsIFrame* cFrame = aKidFrame->GetFirstPrincipalChild();
3695 while (cFrame) {
3696 nsTableRowFrame *rowFrame = do_QueryFrame(cFrame);
3697 if (rowFrame) {
3698 printf("row(%d)=%p ", rowFrame->GetRowIndex(),
3699 static_cast<void*>(rowFrame));
3700 nsIFrame* childFrame = cFrame->GetFirstPrincipalChild();
3701 while (childFrame) {
3702 nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
3703 if (cellFrame) {
3704 int32_t colIndex;
3705 cellFrame->GetColIndex(colIndex);
3706 printf("cell(%d)=%p ", colIndex, static_cast<void*>(childFrame));
3707 }
3708 childFrame = childFrame->GetNextSibling();
3709 }
3710 printf("\n");
3711 }
3712 else {
3713 DumpRowGroup(rowFrame);
3714 }
3715 cFrame = cFrame->GetNextSibling();
3716 }
3717 }
3719 void
3720 nsTableFrame::Dump(bool aDumpRows,
3721 bool aDumpCols,
3722 bool aDumpCellMap)
3723 {
3724 printf("***START TABLE DUMP*** \n");
3725 // dump the columns widths array
3726 printf("mColWidths=");
3727 int32_t numCols = GetColCount();
3728 int32_t colX;
3729 for (colX = 0; colX < numCols; colX++) {
3730 printf("%d ", GetColumnWidth(colX));
3731 }
3732 printf("\n");
3734 if (aDumpRows) {
3735 nsIFrame* kidFrame = mFrames.FirstChild();
3736 while (kidFrame) {
3737 DumpRowGroup(kidFrame);
3738 kidFrame = kidFrame->GetNextSibling();
3739 }
3740 }
3742 if (aDumpCols) {
3743 // output col frame cache
3744 printf("\n col frame cache ->");
3745 for (colX = 0; colX < numCols; colX++) {
3746 nsTableColFrame* colFrame = mColFrames.ElementAt(colX);
3747 if (0 == (colX % 8)) {
3748 printf("\n");
3749 }
3750 printf ("%d=%p ", colX, static_cast<void*>(colFrame));
3751 nsTableColType colType = colFrame->GetColType();
3752 switch (colType) {
3753 case eColContent:
3754 printf(" content ");
3755 break;
3756 case eColAnonymousCol:
3757 printf(" anonymous-column ");
3758 break;
3759 case eColAnonymousColGroup:
3760 printf(" anonymous-colgroup ");
3761 break;
3762 case eColAnonymousCell:
3763 printf(" anonymous-cell ");
3764 break;
3765 }
3766 }
3767 printf("\n colgroups->");
3768 for (nsIFrame* childFrame = mColGroups.FirstChild(); childFrame;
3769 childFrame = childFrame->GetNextSibling()) {
3770 if (nsGkAtoms::tableColGroupFrame == childFrame->GetType()) {
3771 nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame *)childFrame;
3772 colGroupFrame->Dump(1);
3773 }
3774 }
3775 for (colX = 0; colX < numCols; colX++) {
3776 printf("\n");
3777 nsTableColFrame* colFrame = GetColFrame(colX);
3778 colFrame->Dump(1);
3779 }
3780 }
3781 if (aDumpCellMap) {
3782 nsTableCellMap* cellMap = GetCellMap();
3783 cellMap->Dump();
3784 }
3785 printf(" ***END TABLE DUMP*** \n");
3786 }
3787 #endif
3789 // nsTableIterator
3790 nsTableIterator::nsTableIterator(nsIFrame& aSource)
3791 {
3792 nsIFrame* firstChild = aSource.GetFirstPrincipalChild();
3793 Init(firstChild);
3794 }
3796 nsTableIterator::nsTableIterator(nsFrameList& aSource)
3797 {
3798 nsIFrame* firstChild = aSource.FirstChild();
3799 Init(firstChild);
3800 }
3802 void nsTableIterator::Init(nsIFrame* aFirstChild)
3803 {
3804 mFirstListChild = aFirstChild;
3805 mFirstChild = aFirstChild;
3806 mCurrentChild = nullptr;
3807 mLeftToRight = true;
3808 mCount = -1;
3810 if (!mFirstChild) {
3811 return;
3812 }
3814 nsTableFrame* table = nsTableFrame::GetTableFrame(mFirstChild);
3815 mLeftToRight = (NS_STYLE_DIRECTION_LTR ==
3816 table->StyleVisibility()->mDirection);
3818 if (!mLeftToRight) {
3819 mCount = 0;
3820 nsIFrame* nextChild = mFirstChild->GetNextSibling();
3821 while (nullptr != nextChild) {
3822 mCount++;
3823 mFirstChild = nextChild;
3824 nextChild = nextChild->GetNextSibling();
3825 }
3826 }
3827 }
3829 nsIFrame* nsTableIterator::First()
3830 {
3831 mCurrentChild = mFirstChild;
3832 return mCurrentChild;
3833 }
3835 nsIFrame* nsTableIterator::Next()
3836 {
3837 if (!mCurrentChild) {
3838 return nullptr;
3839 }
3841 if (mLeftToRight) {
3842 mCurrentChild = mCurrentChild->GetNextSibling();
3843 return mCurrentChild;
3844 }
3845 else {
3846 nsIFrame* targetChild = mCurrentChild;
3847 mCurrentChild = nullptr;
3848 nsIFrame* child = mFirstListChild;
3849 while (child && (child != targetChild)) {
3850 mCurrentChild = child;
3851 child = child->GetNextSibling();
3852 }
3853 return mCurrentChild;
3854 }
3855 }
3857 bool nsTableIterator::IsLeftToRight()
3858 {
3859 return mLeftToRight;
3860 }
3862 int32_t nsTableIterator::Count()
3863 {
3864 if (-1 == mCount) {
3865 mCount = 0;
3866 nsIFrame* child = mFirstListChild;
3867 while (nullptr != child) {
3868 mCount++;
3869 child = child->GetNextSibling();
3870 }
3871 }
3872 return mCount;
3873 }
3875 bool
3876 nsTableFrame::ColumnHasCellSpacingBefore(int32_t aColIndex) const
3877 {
3878 // Since fixed-layout tables should not have their column sizes change
3879 // as they load, we assume that all columns are significant.
3880 if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Fixed)
3881 return true;
3882 // the first column is always significant
3883 if (aColIndex == 0)
3884 return true;
3885 nsTableCellMap* cellMap = GetCellMap();
3886 if (!cellMap)
3887 return false;
3888 return cellMap->GetNumCellsOriginatingInCol(aColIndex) > 0;
3889 }
3891 /********************************************************************************
3892 * Collapsing Borders
3893 *
3894 * The CSS spec says to resolve border conflicts in this order:
3895 * 1) any border with the style HIDDEN wins
3896 * 2) the widest border with a style that is not NONE wins
3897 * 3) the border styles are ranked in this order, highest to lowest precedence:
3898 * double, solid, dashed, dotted, ridge, outset, groove, inset
3899 * 4) borders that are of equal width and style (differ only in color) have this precedence:
3900 * cell, row, rowgroup, col, colgroup, table
3901 * 5) if all border styles are NONE, then that's the computed border style.
3902 *******************************************************************************/
3904 #ifdef DEBUG
3905 #define VerifyNonNegativeDamageRect(r) \
3906 NS_ASSERTION((r).x >= 0, "negative col index"); \
3907 NS_ASSERTION((r).y >= 0, "negative row index"); \
3908 NS_ASSERTION((r).width >= 0, "negative horizontal damage"); \
3909 NS_ASSERTION((r).height >= 0, "negative vertical damage");
3910 #define VerifyDamageRect(r) \
3911 VerifyNonNegativeDamageRect(r); \
3912 NS_ASSERTION((r).XMost() <= GetColCount(), \
3913 "horizontal damage extends outside table"); \
3914 NS_ASSERTION((r).YMost() <= GetRowCount(), \
3915 "vertical damage extends outside table");
3916 #endif
3918 void
3919 nsTableFrame::AddBCDamageArea(const nsIntRect& aValue)
3920 {
3921 NS_ASSERTION(IsBorderCollapse(), "invalid AddBCDamageArea call");
3922 #ifdef DEBUG
3923 VerifyDamageRect(aValue);
3924 #endif
3926 SetNeedToCalcBCBorders(true);
3927 // Get the property
3928 BCPropertyData* value = GetBCProperty(true);
3929 if (value) {
3930 #ifdef DEBUG
3931 VerifyNonNegativeDamageRect(value->mDamageArea);
3932 #endif
3933 // Clamp the old damage area to the current table area in case it shrunk.
3934 int32_t cols = GetColCount();
3935 if (value->mDamageArea.XMost() > cols) {
3936 if (value->mDamageArea.x > cols) {
3937 value->mDamageArea.x = cols;
3938 value->mDamageArea.width = 0;
3939 }
3940 else {
3941 value->mDamageArea.width = cols - value->mDamageArea.x;
3942 }
3943 }
3944 int32_t rows = GetRowCount();
3945 if (value->mDamageArea.YMost() > rows) {
3946 if (value->mDamageArea.y > rows) {
3947 value->mDamageArea.y = rows;
3948 value->mDamageArea.height = 0;
3949 }
3950 else {
3951 value->mDamageArea.height = rows - value->mDamageArea.y;
3952 }
3953 }
3955 // Construct a union of the new and old damage areas.
3956 value->mDamageArea.UnionRect(value->mDamageArea, aValue);
3957 }
3958 }
3961 void
3962 nsTableFrame::SetFullBCDamageArea()
3963 {
3964 NS_ASSERTION(IsBorderCollapse(), "invalid SetFullBCDamageArea call");
3966 SetNeedToCalcBCBorders(true);
3968 BCPropertyData* value = GetBCProperty(true);
3969 if (value) {
3970 value->mDamageArea = nsIntRect(0, 0, GetColCount(), GetRowCount());
3971 }
3972 }
3975 /* BCCellBorder represents a border segment which can be either a horizontal
3976 * or a vertical segment. For each segment we need to know the color, width,
3977 * style, who owns it and how long it is in cellmap coordinates.
3978 * Ownership of these segments is important to calculate which corners should
3979 * be bevelled. This structure has dual use, its used first to compute the
3980 * dominant border for horizontal and vertical segments and to store the
3981 * preliminary computed border results in the BCCellBorders structure.
3982 * This temporary storage is not symmetric with respect to horizontal and
3983 * vertical border segments, its always column oriented. For each column in
3984 * the cellmap there is a temporary stored vertical and horizontal segment.
3985 * XXX_Bernd this asymmetry is the root of those rowspan bc border errors
3986 */
3987 struct BCCellBorder
3988 {
3989 BCCellBorder() { Reset(0, 1); }
3990 void Reset(uint32_t aRowIndex, uint32_t aRowSpan);
3991 nscolor color; // border segment color
3992 BCPixelSize width; // border segment width in pixel coordinates !!
3993 uint8_t style; // border segment style, possible values are defined
3994 // in nsStyleConsts.h as NS_STYLE_BORDER_STYLE_*
3995 BCBorderOwner owner; // border segment owner, possible values are defined
3996 // in celldata.h. In the cellmap for each border
3997 // segment we store the owner and later when
3998 // painting we know the owner and can retrieve the
3999 // style info from the corresponding frame
4000 int32_t rowIndex; // rowIndex of temporary stored horizontal border
4001 // segments relative to the table
4002 int32_t rowSpan; // row span of temporary stored horizontal border
4003 // segments
4004 };
4006 void
4007 BCCellBorder::Reset(uint32_t aRowIndex,
4008 uint32_t aRowSpan)
4009 {
4010 style = NS_STYLE_BORDER_STYLE_NONE;
4011 color = 0;
4012 width = 0;
4013 owner = eTableOwner;
4014 rowIndex = aRowIndex;
4015 rowSpan = aRowSpan;
4016 }
4018 class BCMapCellIterator;
4020 /*****************************************************************
4021 * BCMapCellInfo
4022 * This structure stores information about the cellmap and all involved
4023 * table related frames that are used during the computation of winning borders
4024 * in CalcBCBorders so that they do need to be looked up again and again when
4025 * iterating over the cells.
4026 ****************************************************************/
4027 struct BCMapCellInfo
4028 {
4029 BCMapCellInfo(nsTableFrame* aTableFrame);
4030 void ResetCellInfo();
4031 void SetInfo(nsTableRowFrame* aNewRow,
4032 int32_t aColIndex,
4033 BCCellData* aCellData,
4034 BCMapCellIterator* aIter,
4035 nsCellMap* aCellMap = nullptr);
4036 // The BCMapCellInfo has functions to set the continous
4037 // border widths (see nsTablePainter.cpp for a description of the continous
4038 // borders concept). The widths are computed inside these functions based on
4039 // the current position inside the table and the cached frames that correspond
4040 // to this position. The widths are stored in member variables of the internal
4041 // table frames.
4042 void SetTableTopLeftContBCBorder();
4043 void SetRowGroupLeftContBCBorder();
4044 void SetRowGroupRightContBCBorder();
4045 void SetRowGroupBottomContBCBorder();
4046 void SetRowLeftContBCBorder();
4047 void SetRowRightContBCBorder();
4048 void SetColumnTopRightContBCBorder();
4049 void SetColumnBottomContBCBorder();
4050 void SetColGroupBottomContBCBorder();
4051 void SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup,
4052 nsTableRowFrame* aNextRow);
4054 // functions to set the border widths on the table related frames, where the
4055 // knowledge about the current position in the table is used.
4056 void SetTableTopBorderWidth(BCPixelSize aWidth);
4057 void SetTableLeftBorderWidth(int32_t aRowY, BCPixelSize aWidth);
4058 void SetTableRightBorderWidth(int32_t aRowY, BCPixelSize aWidth);
4059 void SetTableBottomBorderWidth(BCPixelSize aWidth);
4060 void SetLeftBorderWidths(BCPixelSize aWidth);
4061 void SetRightBorderWidths(BCPixelSize aWidth);
4062 void SetTopBorderWidths(BCPixelSize aWidth);
4063 void SetBottomBorderWidths(BCPixelSize aWidth);
4065 // functions to compute the borders; they depend on the
4066 // knowledge about the current position in the table. The edge functions
4067 // should be called if a table edge is involved, otherwise the internal
4068 // functions should be called.
4069 BCCellBorder GetTopEdgeBorder();
4070 BCCellBorder GetBottomEdgeBorder();
4071 BCCellBorder GetLeftEdgeBorder();
4072 BCCellBorder GetRightEdgeBorder();
4073 BCCellBorder GetRightInternalBorder();
4074 BCCellBorder GetLeftInternalBorder();
4075 BCCellBorder GetTopInternalBorder();
4076 BCCellBorder GetBottomInternalBorder();
4078 // functions to set the interal position information
4079 void SetColumn(int32_t aColX);
4080 // Increment the row as we loop over the rows of a rowspan
4081 void IncrementRow(bool aResetToTopRowOfCell = false);
4083 // Helper functions to get extent of the cell
4084 int32_t GetCellEndRowIndex() const;
4085 int32_t GetCellEndColIndex() const;
4087 // storage of table information
4088 nsTableFrame* mTableFrame;
4089 int32_t mNumTableRows;
4090 int32_t mNumTableCols;
4091 BCPropertyData* mTableBCData;
4093 // storage of table ltr information, the border collapse code swaps the sides
4094 // to account for rtl tables, this is done through mStartSide and mEndSide
4095 bool mTableIsLTR;
4096 mozilla::css::Side mStartSide;
4097 mozilla::css::Side mEndSide;
4099 // a cell can only belong to one rowgroup
4100 nsTableRowGroupFrame* mRowGroup;
4102 // a cell with a rowspan has a top and a bottom row, and rows in between
4103 nsTableRowFrame* mTopRow;
4104 nsTableRowFrame* mBottomRow;
4105 nsTableRowFrame* mCurrentRowFrame;
4107 // a cell with a colspan has a left and right column and columns in between
4108 // they can belong to different colgroups
4109 nsTableColGroupFrame* mColGroup;
4110 nsTableColGroupFrame* mCurrentColGroupFrame;
4112 nsTableColFrame* mLeftCol;
4113 nsTableColFrame* mRightCol;
4114 nsTableColFrame* mCurrentColFrame;
4116 // cell information
4117 BCCellData* mCellData;
4118 nsBCTableCellFrame* mCell;
4120 int32_t mRowIndex;
4121 int32_t mRowSpan;
4122 int32_t mColIndex;
4123 int32_t mColSpan;
4125 // flags to describe the position of the cell with respect to the row- and
4126 // colgroups, for instance mRgAtTop documents that the top cell border hits
4127 // a rowgroup border
4128 bool mRgAtTop;
4129 bool mRgAtBottom;
4130 bool mCgAtLeft;
4131 bool mCgAtRight;
4133 };
4136 BCMapCellInfo::BCMapCellInfo(nsTableFrame* aTableFrame)
4137 {
4138 mTableFrame = aTableFrame;
4139 mTableIsLTR =
4140 aTableFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
4141 if (mTableIsLTR) {
4142 mStartSide = NS_SIDE_LEFT;
4143 mEndSide = NS_SIDE_RIGHT;
4144 }
4145 else {
4146 mStartSide = NS_SIDE_RIGHT;
4147 mEndSide = NS_SIDE_LEFT;
4148 }
4149 mNumTableRows = mTableFrame->GetRowCount();
4150 mNumTableCols = mTableFrame->GetColCount();
4151 mTableBCData = static_cast<BCPropertyData*>
4152 (mTableFrame->Properties().Get(TableBCProperty()));
4154 ResetCellInfo();
4155 }
4157 void BCMapCellInfo::ResetCellInfo()
4158 {
4159 mCellData = nullptr;
4160 mRowGroup = nullptr;
4161 mTopRow = nullptr;
4162 mBottomRow = nullptr;
4163 mColGroup = nullptr;
4164 mLeftCol = nullptr;
4165 mRightCol = nullptr;
4166 mCell = nullptr;
4167 mRowIndex = mRowSpan = mColIndex = mColSpan = 0;
4168 mRgAtTop = mRgAtBottom = mCgAtLeft = mCgAtRight = false;
4169 }
4171 inline int32_t BCMapCellInfo::GetCellEndRowIndex() const
4172 {
4173 return mRowIndex + mRowSpan - 1;
4174 }
4176 inline int32_t BCMapCellInfo::GetCellEndColIndex() const
4177 {
4178 return mColIndex + mColSpan - 1;
4179 }
4182 class BCMapCellIterator
4183 {
4184 public:
4185 BCMapCellIterator(nsTableFrame* aTableFrame,
4186 const nsIntRect& aDamageArea);
4188 void First(BCMapCellInfo& aMapCellInfo);
4190 void Next(BCMapCellInfo& aMapCellInfo);
4192 void PeekRight(BCMapCellInfo& aRefInfo,
4193 uint32_t aRowIndex,
4194 BCMapCellInfo& aAjaInfo);
4196 void PeekBottom(BCMapCellInfo& aRefInfo,
4197 uint32_t aColIndex,
4198 BCMapCellInfo& aAjaInfo);
4200 bool IsNewRow() { return mIsNewRow; }
4202 nsTableRowFrame* GetPrevRow() const { return mPrevRow; }
4203 nsTableRowFrame* GetCurrentRow() const { return mRow; }
4204 nsTableRowGroupFrame* GetCurrentRowGroup() const { return mRowGroup;}
4206 int32_t mRowGroupStart;
4207 int32_t mRowGroupEnd;
4208 bool mAtEnd;
4209 nsCellMap* mCellMap;
4211 private:
4212 bool SetNewRow(nsTableRowFrame* row = nullptr);
4213 bool SetNewRowGroup(bool aFindFirstDamagedRow);
4215 nsTableFrame* mTableFrame;
4216 nsTableCellMap* mTableCellMap;
4217 nsTableFrame::RowGroupArray mRowGroups;
4218 nsTableRowGroupFrame* mRowGroup;
4219 int32_t mRowGroupIndex;
4220 uint32_t mNumTableRows;
4221 nsTableRowFrame* mRow;
4222 nsTableRowFrame* mPrevRow;
4223 bool mIsNewRow;
4224 int32_t mRowIndex;
4225 uint32_t mNumTableCols;
4226 int32_t mColIndex;
4227 nsPoint mAreaStart;
4228 nsPoint mAreaEnd;
4229 };
4231 BCMapCellIterator::BCMapCellIterator(nsTableFrame* aTableFrame,
4232 const nsIntRect& aDamageArea)
4233 :mTableFrame(aTableFrame)
4234 {
4235 mTableCellMap = aTableFrame->GetCellMap();
4237 mAreaStart.x = aDamageArea.x;
4238 mAreaStart.y = aDamageArea.y;
4239 mAreaEnd.y = aDamageArea.y + aDamageArea.height - 1;
4240 mAreaEnd.x = aDamageArea.x + aDamageArea.width - 1;
4242 mNumTableRows = mTableFrame->GetRowCount();
4243 mRow = nullptr;
4244 mRowIndex = 0;
4245 mNumTableCols = mTableFrame->GetColCount();
4246 mColIndex = 0;
4247 mRowGroupIndex = -1;
4249 // Get the ordered row groups
4250 aTableFrame->OrderRowGroups(mRowGroups);
4252 mAtEnd = true; // gets reset when First() is called
4253 }
4255 // fill fields that we need for border collapse computation on a given cell
4256 void
4257 BCMapCellInfo::SetInfo(nsTableRowFrame* aNewRow,
4258 int32_t aColIndex,
4259 BCCellData* aCellData,
4260 BCMapCellIterator* aIter,
4261 nsCellMap* aCellMap)
4262 {
4263 // fill the cell information
4264 mCellData = aCellData;
4265 mColIndex = aColIndex;
4267 // initialize the row information if it was not previously set for cells in
4268 // this row
4269 mRowIndex = 0;
4270 if (aNewRow) {
4271 mTopRow = aNewRow;
4272 mRowIndex = aNewRow->GetRowIndex();
4273 }
4275 // fill cell frame info and row information
4276 mCell = nullptr;
4277 mRowSpan = 1;
4278 mColSpan = 1;
4279 if (aCellData) {
4280 mCell = static_cast<nsBCTableCellFrame*>(aCellData->GetCellFrame());
4281 if (mCell) {
4282 if (!mTopRow) {
4283 mTopRow = static_cast<nsTableRowFrame*>(mCell->GetParent());
4284 if (!mTopRow) ABORT0();
4285 mRowIndex = mTopRow->GetRowIndex();
4286 }
4287 mColSpan = mTableFrame->GetEffectiveColSpan(*mCell, aCellMap);
4288 mRowSpan = mTableFrame->GetEffectiveRowSpan(*mCell, aCellMap);
4289 }
4290 }
4292 if (!mTopRow) {
4293 mTopRow = aIter->GetCurrentRow();
4294 }
4295 if (1 == mRowSpan) {
4296 mBottomRow = mTopRow;
4297 }
4298 else {
4299 mBottomRow = mTopRow->GetNextRow();
4300 if (mBottomRow) {
4301 for (int32_t spanY = 2; mBottomRow && (spanY < mRowSpan); spanY++) {
4302 mBottomRow = mBottomRow->GetNextRow();
4303 }
4304 NS_ASSERTION(mBottomRow, "spanned row not found");
4305 }
4306 else {
4307 NS_ASSERTION(false, "error in cell map");
4308 mRowSpan = 1;
4309 mBottomRow = mTopRow;
4310 }
4311 }
4312 // row group frame info
4313 // try to reuse the rgStart and rgEnd from the iterator as calls to
4314 // GetRowCount() are computationally expensive and should be avoided if
4315 // possible
4316 uint32_t rgStart = aIter->mRowGroupStart;
4317 uint32_t rgEnd = aIter->mRowGroupEnd;
4318 mRowGroup = static_cast<nsTableRowGroupFrame*>(mTopRow->GetParent());
4319 if (mRowGroup != aIter->GetCurrentRowGroup()) {
4320 rgStart = mRowGroup->GetStartRowIndex();
4321 rgEnd = rgStart + mRowGroup->GetRowCount() - 1;
4322 }
4323 uint32_t rowIndex = mTopRow->GetRowIndex();
4324 mRgAtTop = (rgStart == rowIndex);
4325 mRgAtBottom = (rgEnd == rowIndex + mRowSpan - 1);
4327 // col frame info
4328 mLeftCol = mTableFrame->GetColFrame(aColIndex);
4329 if (!mLeftCol) ABORT0();
4331 mRightCol = mLeftCol;
4332 if (mColSpan > 1) {
4333 nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex +
4334 mColSpan -1);
4335 if (!colFrame) ABORT0();
4336 mRightCol = colFrame;
4337 }
4339 // col group frame info
4340 mColGroup = static_cast<nsTableColGroupFrame*>(mLeftCol->GetParent());
4341 int32_t cgStart = mColGroup->GetStartColumnIndex();
4342 int32_t cgEnd = std::max(0, cgStart + mColGroup->GetColCount() - 1);
4343 mCgAtLeft = (cgStart == aColIndex);
4344 mCgAtRight = (cgEnd == aColIndex + mColSpan - 1);
4345 }
4347 bool
4348 BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow)
4349 {
4350 mAtEnd = true;
4351 mPrevRow = mRow;
4352 if (aRow) {
4353 mRow = aRow;
4354 }
4355 else if (mRow) {
4356 mRow = mRow->GetNextRow();
4357 }
4358 if (mRow) {
4359 mRowIndex = mRow->GetRowIndex();
4360 // get to the first entry with an originating cell
4361 int32_t rgRowIndex = mRowIndex - mRowGroupStart;
4362 if (uint32_t(rgRowIndex) >= mCellMap->mRows.Length())
4363 ABORT1(false);
4364 const nsCellMap::CellDataArray& row = mCellMap->mRows[rgRowIndex];
4366 for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) {
4367 CellData* cellData = row.SafeElementAt(mColIndex);
4368 if (!cellData) { // add a dead cell data
4369 nsIntRect damageArea;
4370 cellData = mCellMap->AppendCell(*mTableCellMap, nullptr, rgRowIndex,
4371 false, 0, damageArea);
4372 if (!cellData) ABORT1(false);
4373 }
4374 if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
4375 break;
4376 }
4377 }
4378 mIsNewRow = true;
4379 mAtEnd = false;
4380 }
4381 else ABORT1(false);
4383 return !mAtEnd;
4384 }
4386 bool
4387 BCMapCellIterator::SetNewRowGroup(bool aFindFirstDamagedRow)
4388 {
4389 mAtEnd = true;
4390 int32_t numRowGroups = mRowGroups.Length();
4391 mCellMap = nullptr;
4392 for (mRowGroupIndex++; mRowGroupIndex < numRowGroups; mRowGroupIndex++) {
4393 mRowGroup = mRowGroups[mRowGroupIndex];
4394 int32_t rowCount = mRowGroup->GetRowCount();
4395 mRowGroupStart = mRowGroup->GetStartRowIndex();
4396 mRowGroupEnd = mRowGroupStart + rowCount - 1;
4397 if (rowCount > 0) {
4398 mCellMap = mTableCellMap->GetMapFor(mRowGroup, mCellMap);
4399 if (!mCellMap) ABORT1(false);
4400 nsTableRowFrame* firstRow = mRowGroup->GetFirstRow();
4401 if (aFindFirstDamagedRow) {
4402 if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
4403 // the damage area starts in the row group
4404 if (aFindFirstDamagedRow) {
4405 // find the correct first damaged row
4406 int32_t numRows = mAreaStart.y - mRowGroupStart;
4407 for (int32_t i = 0; i < numRows; i++) {
4408 firstRow = firstRow->GetNextRow();
4409 if (!firstRow) ABORT1(false);
4410 }
4411 }
4412 }
4413 else {
4414 continue;
4415 }
4416 }
4417 if (SetNewRow(firstRow)) { // sets mAtEnd
4418 break;
4419 }
4420 }
4421 }
4423 return !mAtEnd;
4424 }
4426 void
4427 BCMapCellIterator::First(BCMapCellInfo& aMapInfo)
4428 {
4429 aMapInfo.ResetCellInfo();
4431 SetNewRowGroup(true); // sets mAtEnd
4432 while (!mAtEnd) {
4433 if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
4434 BCCellData* cellData =
4435 static_cast<BCCellData*>(mCellMap->GetDataAt(mAreaStart.y -
4436 mRowGroupStart,
4437 mAreaStart.x));
4438 if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
4439 aMapInfo.SetInfo(mRow, mAreaStart.x, cellData, this);
4440 return;
4441 }
4442 else {
4443 NS_ASSERTION(((0 == mAreaStart.x) && (mRowGroupStart == mAreaStart.y)) ,
4444 "damage area expanded incorrectly");
4445 }
4446 }
4447 SetNewRowGroup(true); // sets mAtEnd
4448 }
4449 }
4451 void
4452 BCMapCellIterator::Next(BCMapCellInfo& aMapInfo)
4453 {
4454 if (mAtEnd) ABORT0();
4455 aMapInfo.ResetCellInfo();
4457 mIsNewRow = false;
4458 mColIndex++;
4459 while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) {
4460 for (; mColIndex <= mAreaEnd.x; mColIndex++) {
4461 int32_t rgRowIndex = mRowIndex - mRowGroupStart;
4462 BCCellData* cellData =
4463 static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, mColIndex));
4464 if (!cellData) { // add a dead cell data
4465 nsIntRect damageArea;
4466 cellData =
4467 static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr,
4468 rgRowIndex, false, 0,
4469 damageArea));
4470 if (!cellData) ABORT0();
4471 }
4472 if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
4473 aMapInfo.SetInfo(mRow, mColIndex, cellData, this);
4474 return;
4475 }
4476 }
4477 if (mRowIndex >= mRowGroupEnd) {
4478 SetNewRowGroup(false); // could set mAtEnd
4479 }
4480 else {
4481 SetNewRow(); // could set mAtEnd
4482 }
4483 }
4484 mAtEnd = true;
4485 }
4487 void
4488 BCMapCellIterator::PeekRight(BCMapCellInfo& aRefInfo,
4489 uint32_t aRowIndex,
4490 BCMapCellInfo& aAjaInfo)
4491 {
4492 aAjaInfo.ResetCellInfo();
4493 int32_t colIndex = aRefInfo.mColIndex + aRefInfo.mColSpan;
4494 uint32_t rgRowIndex = aRowIndex - mRowGroupStart;
4496 BCCellData* cellData =
4497 static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex));
4498 if (!cellData) { // add a dead cell data
4499 NS_ASSERTION(colIndex < mTableCellMap->GetColCount(), "program error");
4500 nsIntRect damageArea;
4501 cellData =
4502 static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr,
4503 rgRowIndex, false, 0,
4504 damageArea));
4505 if (!cellData) ABORT0();
4506 }
4507 nsTableRowFrame* row = nullptr;
4508 if (cellData->IsRowSpan()) {
4509 rgRowIndex -= cellData->GetRowSpanOffset();
4510 cellData =
4511 static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex));
4512 if (!cellData)
4513 ABORT0();
4514 }
4515 else {
4516 row = mRow;
4517 }
4518 aAjaInfo.SetInfo(row, colIndex, cellData, this);
4519 }
4521 void
4522 BCMapCellIterator::PeekBottom(BCMapCellInfo& aRefInfo,
4523 uint32_t aColIndex,
4524 BCMapCellInfo& aAjaInfo)
4525 {
4526 aAjaInfo.ResetCellInfo();
4527 int32_t rowIndex = aRefInfo.mRowIndex + aRefInfo.mRowSpan;
4528 int32_t rgRowIndex = rowIndex - mRowGroupStart;
4529 nsTableRowGroupFrame* rg = mRowGroup;
4530 nsCellMap* cellMap = mCellMap;
4531 nsTableRowFrame* nextRow = nullptr;
4532 if (rowIndex > mRowGroupEnd) {
4533 int32_t nextRgIndex = mRowGroupIndex;
4534 do {
4535 nextRgIndex++;
4536 rg = mRowGroups.SafeElementAt(nextRgIndex);
4537 if (rg) {
4538 cellMap = mTableCellMap->GetMapFor(rg, cellMap); if (!cellMap) ABORT0();
4539 rgRowIndex = 0;
4540 nextRow = rg->GetFirstRow();
4541 }
4542 }
4543 while (rg && !nextRow);
4544 if(!rg) return;
4545 }
4546 else {
4547 // get the row within the same row group
4548 nextRow = mRow;
4549 for (int32_t i = 0; i < aRefInfo.mRowSpan; i++) {
4550 nextRow = nextRow->GetNextRow(); if (!nextRow) ABORT0();
4551 }
4552 }
4554 BCCellData* cellData =
4555 static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
4556 if (!cellData) { // add a dead cell data
4557 NS_ASSERTION(rgRowIndex < cellMap->GetRowCount(), "program error");
4558 nsIntRect damageArea;
4559 cellData =
4560 static_cast<BCCellData*>(cellMap->AppendCell(*mTableCellMap, nullptr,
4561 rgRowIndex, false, 0,
4562 damageArea));
4563 if (!cellData) ABORT0();
4564 }
4565 if (cellData->IsColSpan()) {
4566 aColIndex -= cellData->GetColSpanOffset();
4567 cellData =
4568 static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
4569 }
4570 aAjaInfo.SetInfo(nextRow, aColIndex, cellData, this, cellMap);
4571 }
4573 // Assign priorities to border styles. For example, styleToPriority(NS_STYLE_BORDER_STYLE_SOLID)
4574 // will return the priority of NS_STYLE_BORDER_STYLE_SOLID.
4575 static uint8_t styleToPriority[13] = { 0, // NS_STYLE_BORDER_STYLE_NONE
4576 2, // NS_STYLE_BORDER_STYLE_GROOVE
4577 4, // NS_STYLE_BORDER_STYLE_RIDGE
4578 5, // NS_STYLE_BORDER_STYLE_DOTTED
4579 6, // NS_STYLE_BORDER_STYLE_DASHED
4580 7, // NS_STYLE_BORDER_STYLE_SOLID
4581 8, // NS_STYLE_BORDER_STYLE_DOUBLE
4582 1, // NS_STYLE_BORDER_STYLE_INSET
4583 3, // NS_STYLE_BORDER_STYLE_OUTSET
4584 9 };// NS_STYLE_BORDER_STYLE_HIDDEN
4585 // priority rules follow CSS 2.1 spec
4586 // 'hidden', 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove',
4587 // and the lowest: 'inset'. none is even weaker
4588 #define CELL_CORNER true
4590 /** return the border style, border color for a given frame and side
4591 * @param aFrame - query the info for this frame
4592 * @param aSide - the side of the frame
4593 * @param aStyle - the border style
4594 * @param aColor - the border color
4595 * @param aTableIsLTR - table direction is LTR
4596 */
4597 static void
4598 GetColorAndStyle(const nsIFrame* aFrame,
4599 mozilla::css::Side aSide,
4600 uint8_t& aStyle,
4601 nscolor& aColor,
4602 bool aTableIsLTR)
4603 {
4604 NS_PRECONDITION(aFrame, "null frame");
4605 // initialize out arg
4606 aColor = 0;
4607 const nsStyleBorder* styleData = aFrame->StyleBorder();
4608 if(!aTableIsLTR) { // revert the directions
4609 if (NS_SIDE_RIGHT == aSide) {
4610 aSide = NS_SIDE_LEFT;
4611 }
4612 else if (NS_SIDE_LEFT == aSide) {
4613 aSide = NS_SIDE_RIGHT;
4614 }
4615 }
4616 aStyle = styleData->GetBorderStyle(aSide);
4618 if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) ||
4619 (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) {
4620 return;
4621 }
4622 aColor = aFrame->StyleContext()->GetVisitedDependentColor(
4623 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[aSide]);
4624 }
4626 /** coerce the paint style as required by CSS2.1
4627 * @param aFrame - query the info for this frame
4628 * @param aSide - the side of the frame
4629 * @param aStyle - the border style
4630 * @param aColor - the border color
4631 * @param aTableIsLTR - table direction is LTR
4632 */
4633 static void
4634 GetPaintStyleInfo(const nsIFrame* aFrame,
4635 mozilla::css::Side aSide,
4636 uint8_t& aStyle,
4637 nscolor& aColor,
4638 bool aTableIsLTR)
4639 {
4640 GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR);
4641 if (NS_STYLE_BORDER_STYLE_INSET == aStyle) {
4642 aStyle = NS_STYLE_BORDER_STYLE_RIDGE;
4643 }
4644 else if (NS_STYLE_BORDER_STYLE_OUTSET == aStyle) {
4645 aStyle = NS_STYLE_BORDER_STYLE_GROOVE;
4646 }
4647 }
4649 /** return the border style, border color and the width in pixel for a given
4650 * frame and side
4651 * @param aFrame - query the info for this frame
4652 * @param aSide - the side of the frame
4653 * @param aStyle - the border style
4654 * @param aColor - the border color
4655 * @param aTableIsLTR - table direction is LTR
4656 * @param aWidth - the border width in px.
4657 * @param aTwipsToPixels - conversion factor from twips to pixel
4658 */
4659 static void
4660 GetColorAndStyle(const nsIFrame* aFrame,
4661 mozilla::css::Side aSide,
4662 uint8_t& aStyle,
4663 nscolor& aColor,
4664 bool aTableIsLTR,
4665 BCPixelSize& aWidth)
4666 {
4667 GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR);
4668 if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) ||
4669 (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) {
4670 aWidth = 0;
4671 return;
4672 }
4673 const nsStyleBorder* styleData = aFrame->StyleBorder();
4674 nscoord width;
4675 if(!aTableIsLTR) { // revert the directions
4676 if (NS_SIDE_RIGHT == aSide) {
4677 aSide = NS_SIDE_LEFT;
4678 }
4679 else if (NS_SIDE_LEFT == aSide) {
4680 aSide = NS_SIDE_RIGHT;
4681 }
4682 }
4683 width = styleData->GetComputedBorderWidth(aSide);
4684 aWidth = nsPresContext::AppUnitsToIntCSSPixels(width);
4685 }
4687 class nsDelayedCalcBCBorders : public nsRunnable {
4688 public:
4689 nsDelayedCalcBCBorders(nsIFrame* aFrame) :
4690 mFrame(aFrame) {}
4692 NS_IMETHOD Run() MOZ_OVERRIDE {
4693 if (mFrame) {
4694 nsTableFrame* tableFrame = static_cast <nsTableFrame*>(mFrame.GetFrame());
4695 if (tableFrame->NeedToCalcBCBorders()) {
4696 tableFrame->CalcBCBorders();
4697 }
4698 }
4699 return NS_OK;
4700 }
4701 private:
4702 nsWeakFrame mFrame;
4703 };
4705 bool
4706 nsTableFrame::BCRecalcNeeded(nsStyleContext* aOldStyleContext,
4707 nsStyleContext* aNewStyleContext)
4708 {
4709 // Attention: the old style context is the one we're forgetting,
4710 // and hence possibly completely bogus for GetStyle* purposes.
4711 // We use PeekStyleData instead.
4713 const nsStyleBorder* oldStyleData = aOldStyleContext->PeekStyleBorder();
4714 if (!oldStyleData)
4715 return false;
4717 const nsStyleBorder* newStyleData = aNewStyleContext->StyleBorder();
4718 nsChangeHint change = newStyleData->CalcDifference(*oldStyleData);
4719 if (!change)
4720 return false;
4721 if (change & nsChangeHint_NeedReflow)
4722 return true; // the caller only needs to mark the bc damage area
4723 if (change & nsChangeHint_RepaintFrame) {
4724 // we need to recompute the borders and the caller needs to mark
4725 // the bc damage area
4726 // XXX In principle this should only be necessary for border style changes
4727 // However the bc painting code tries to maximize the drawn border segments
4728 // so it stores in the cellmap where a new border segment starts and this
4729 // introduces a unwanted cellmap data dependence on color
4730 nsCOMPtr<nsIRunnable> evt = new nsDelayedCalcBCBorders(this);
4731 NS_DispatchToCurrentThread(evt);
4732 return true;
4733 }
4734 return false;
4735 }
4738 // Compare two border segments, this comparison depends whether the two
4739 // segments meet at a corner and whether the second segment is horizontal.
4740 // The return value is whichever of aBorder1 or aBorder2 dominates.
4741 static const BCCellBorder&
4742 CompareBorders(bool aIsCorner, // Pass true for corner calculations
4743 const BCCellBorder& aBorder1,
4744 const BCCellBorder& aBorder2,
4745 bool aSecondIsHorizontal,
4746 bool* aFirstDominates = nullptr)
4747 {
4748 bool firstDominates = true;
4750 if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder1.style) {
4751 firstDominates = (aIsCorner) ? false : true;
4752 }
4753 else if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder2.style) {
4754 firstDominates = (aIsCorner) ? true : false;
4755 }
4756 else if (aBorder1.width < aBorder2.width) {
4757 firstDominates = false;
4758 }
4759 else if (aBorder1.width == aBorder2.width) {
4760 if (styleToPriority[aBorder1.style] < styleToPriority[aBorder2.style]) {
4761 firstDominates = false;
4762 }
4763 else if (styleToPriority[aBorder1.style] == styleToPriority[aBorder2.style]) {
4764 if (aBorder1.owner == aBorder2.owner) {
4765 firstDominates = !aSecondIsHorizontal;
4766 }
4767 else if (aBorder1.owner < aBorder2.owner) {
4768 firstDominates = false;
4769 }
4770 }
4771 }
4773 if (aFirstDominates)
4774 *aFirstDominates = firstDominates;
4776 if (firstDominates)
4777 return aBorder1;
4778 return aBorder2;
4779 }
4781 /** calc the dominant border by considering the table, row/col group, row/col,
4782 * cell.
4783 * Depending on whether the side is vertical or horizontal and whether
4784 * adjacent frames are taken into account the ownership of a single border
4785 * segment is defined. The return value is the dominating border
4786 * The cellmap stores only top and left borders for each cellmap position.
4787 * If the cell border is owned by the cell that is left of the border
4788 * it will be an adjacent owner aka eAjaCellOwner. See celldata.h for the other
4789 * scenarios with a adjacent owner.
4790 * @param xxxFrame - the frame for style information, might be zero if
4791 * it should not be considered
4792 * @param aSide - side of the frames that should be considered
4793 * @param aAja - the border comparison takes place from the point of
4794 * a frame that is adjacent to the cellmap entry, for
4795 * when a cell owns its lower border it will be the
4796 * adjacent owner as in the cellmap only top and left
4797 * borders are stored.
4798 * @param aTwipsToPixels - conversion factor as borders need to be drawn pixel
4799 * aligned.
4800 */
4801 static BCCellBorder
4802 CompareBorders(const nsIFrame* aTableFrame,
4803 const nsIFrame* aColGroupFrame,
4804 const nsIFrame* aColFrame,
4805 const nsIFrame* aRowGroupFrame,
4806 const nsIFrame* aRowFrame,
4807 const nsIFrame* aCellFrame,
4808 bool aTableIsLTR,
4809 mozilla::css::Side aSide,
4810 bool aAja)
4811 {
4812 BCCellBorder border, tempBorder;
4813 bool horizontal = (NS_SIDE_TOP == aSide) || (NS_SIDE_BOTTOM == aSide);
4815 // start with the table as dominant if present
4816 if (aTableFrame) {
4817 GetColorAndStyle(aTableFrame, aSide, border.style, border.color, aTableIsLTR, border.width);
4818 border.owner = eTableOwner;
4819 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4820 return border;
4821 }
4822 }
4823 // see if the colgroup is dominant
4824 if (aColGroupFrame) {
4825 GetColorAndStyle(aColGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
4826 tempBorder.owner = (aAja && !horizontal) ? eAjaColGroupOwner : eColGroupOwner;
4827 // pass here and below false for aSecondIsHorizontal as it is only used for corner calculations.
4828 border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
4829 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4830 return border;
4831 }
4832 }
4833 // see if the col is dominant
4834 if (aColFrame) {
4835 GetColorAndStyle(aColFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
4836 tempBorder.owner = (aAja && !horizontal) ? eAjaColOwner : eColOwner;
4837 border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
4838 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4839 return border;
4840 }
4841 }
4842 // see if the rowgroup is dominant
4843 if (aRowGroupFrame) {
4844 GetColorAndStyle(aRowGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
4845 tempBorder.owner = (aAja && horizontal) ? eAjaRowGroupOwner : eRowGroupOwner;
4846 border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
4847 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4848 return border;
4849 }
4850 }
4851 // see if the row is dominant
4852 if (aRowFrame) {
4853 GetColorAndStyle(aRowFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
4854 tempBorder.owner = (aAja && horizontal) ? eAjaRowOwner : eRowOwner;
4855 border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
4856 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4857 return border;
4858 }
4859 }
4860 // see if the cell is dominant
4861 if (aCellFrame) {
4862 GetColorAndStyle(aCellFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
4863 tempBorder.owner = (aAja) ? eAjaCellOwner : eCellOwner;
4864 border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
4865 }
4866 return border;
4867 }
4869 static bool
4870 Perpendicular(mozilla::css::Side aSide1,
4871 mozilla::css::Side aSide2)
4872 {
4873 switch (aSide1) {
4874 case NS_SIDE_TOP:
4875 return (NS_SIDE_BOTTOM != aSide2);
4876 case NS_SIDE_RIGHT:
4877 return (NS_SIDE_LEFT != aSide2);
4878 case NS_SIDE_BOTTOM:
4879 return (NS_SIDE_TOP != aSide2);
4880 default: // NS_SIDE_LEFT
4881 return (NS_SIDE_RIGHT != aSide2);
4882 }
4883 }
4885 // XXX allocate this as number-of-cols+1 instead of number-of-cols+1 * number-of-rows+1
4886 struct BCCornerInfo
4887 {
4888 BCCornerInfo() { ownerColor = 0; ownerWidth = subWidth = ownerElem = subSide =
4889 subElem = hasDashDot = numSegs = bevel = 0; ownerSide = NS_SIDE_TOP;
4890 ownerStyle = 0xFF; subStyle = NS_STYLE_BORDER_STYLE_SOLID; }
4891 void Set(mozilla::css::Side aSide,
4892 BCCellBorder border);
4894 void Update(mozilla::css::Side aSide,
4895 BCCellBorder border);
4897 nscolor ownerColor; // color of borderOwner
4898 uint16_t ownerWidth; // pixel width of borderOwner
4899 uint16_t subWidth; // pixel width of the largest border intersecting the border perpendicular
4900 // to ownerSide
4901 uint32_t ownerSide:2; // mozilla::css::Side (e.g NS_SIDE_TOP, NS_SIDE_RIGHT, etc) of the border
4902 // owning the corner relative to the corner
4903 uint32_t ownerElem:3; // elem type (e.g. eTable, eGroup, etc) owning the corner
4904 uint32_t ownerStyle:8; // border style of ownerElem
4905 uint32_t subSide:2; // side of border with subWidth relative to the corner
4906 uint32_t subElem:3; // elem type (e.g. eTable, eGroup, etc) of sub owner
4907 uint32_t subStyle:8; // border style of subElem
4908 uint32_t hasDashDot:1; // does a dashed, dotted segment enter the corner, they cannot be beveled
4909 uint32_t numSegs:3; // number of segments entering corner
4910 uint32_t bevel:1; // is the corner beveled (uses the above two fields together with subWidth)
4911 uint32_t unused:1;
4912 };
4914 void
4915 BCCornerInfo::Set(mozilla::css::Side aSide,
4916 BCCellBorder aBorder)
4917 {
4918 ownerElem = aBorder.owner;
4919 ownerStyle = aBorder.style;
4920 ownerWidth = aBorder.width;
4921 ownerColor = aBorder.color;
4922 ownerSide = aSide;
4923 hasDashDot = 0;
4924 numSegs = 0;
4925 if (aBorder.width > 0) {
4926 numSegs++;
4927 hasDashDot = (NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
4928 (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style);
4929 }
4930 bevel = 0;
4931 subWidth = 0;
4932 // the following will get set later
4933 subSide = ((aSide == NS_SIDE_LEFT) || (aSide == NS_SIDE_RIGHT)) ? NS_SIDE_TOP : NS_SIDE_LEFT;
4934 subElem = eTableOwner;
4935 subStyle = NS_STYLE_BORDER_STYLE_SOLID;
4936 }
4938 void
4939 BCCornerInfo::Update(mozilla::css::Side aSide,
4940 BCCellBorder aBorder)
4941 {
4942 bool existingWins = false;
4943 if (0xFF == ownerStyle) { // initial value indiating that it hasn't been set yet
4944 Set(aSide, aBorder);
4945 }
4946 else {
4947 bool horizontal = (NS_SIDE_LEFT == aSide) || (NS_SIDE_RIGHT == aSide); // relative to the corner
4948 BCCellBorder oldBorder, tempBorder;
4949 oldBorder.owner = (BCBorderOwner) ownerElem;
4950 oldBorder.style = ownerStyle;
4951 oldBorder.width = ownerWidth;
4952 oldBorder.color = ownerColor;
4954 mozilla::css::Side oldSide = mozilla::css::Side(ownerSide);
4956 tempBorder = CompareBorders(CELL_CORNER, oldBorder, aBorder, horizontal, &existingWins);
4958 ownerElem = tempBorder.owner;
4959 ownerStyle = tempBorder.style;
4960 ownerWidth = tempBorder.width;
4961 ownerColor = tempBorder.color;
4962 if (existingWins) { // existing corner is dominant
4963 if (::Perpendicular(mozilla::css::Side(ownerSide), aSide)) {
4964 // see if the new sub info replaces the old
4965 BCCellBorder subBorder;
4966 subBorder.owner = (BCBorderOwner) subElem;
4967 subBorder.style = subStyle;
4968 subBorder.width = subWidth;
4969 subBorder.color = 0; // we are not interested in subBorder color
4970 bool firstWins;
4972 tempBorder = CompareBorders(CELL_CORNER, subBorder, aBorder, horizontal, &firstWins);
4974 subElem = tempBorder.owner;
4975 subStyle = tempBorder.style;
4976 subWidth = tempBorder.width;
4977 if (!firstWins) {
4978 subSide = aSide;
4979 }
4980 }
4981 }
4982 else { // input args are dominant
4983 ownerSide = aSide;
4984 if (::Perpendicular(oldSide, mozilla::css::Side(ownerSide))) {
4985 subElem = oldBorder.owner;
4986 subStyle = oldBorder.style;
4987 subWidth = oldBorder.width;
4988 subSide = oldSide;
4989 }
4990 }
4991 if (aBorder.width > 0) {
4992 numSegs++;
4993 if (!hasDashDot && ((NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
4994 (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style))) {
4995 hasDashDot = 1;
4996 }
4997 }
4999 // bevel the corner if only two perpendicular non dashed/dotted segments enter the corner
5000 bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot);
5001 }
5002 }
5004 struct BCCorners
5005 {
5006 BCCorners(int32_t aNumCorners,
5007 int32_t aStartIndex);
5009 ~BCCorners() { delete [] corners; }
5011 BCCornerInfo& operator [](int32_t i) const
5012 { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
5013 return corners[clamped(i, startIndex, endIndex) - startIndex]; }
5015 int32_t startIndex;
5016 int32_t endIndex;
5017 BCCornerInfo* corners;
5018 };
5020 BCCorners::BCCorners(int32_t aNumCorners,
5021 int32_t aStartIndex)
5022 {
5023 NS_ASSERTION((aNumCorners > 0) && (aStartIndex >= 0), "program error");
5024 startIndex = aStartIndex;
5025 endIndex = aStartIndex + aNumCorners - 1;
5026 corners = new BCCornerInfo[aNumCorners];
5027 }
5030 struct BCCellBorders
5031 {
5032 BCCellBorders(int32_t aNumBorders,
5033 int32_t aStartIndex);
5035 ~BCCellBorders() { delete [] borders; }
5037 BCCellBorder& operator [](int32_t i) const
5038 { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
5039 return borders[clamped(i, startIndex, endIndex) - startIndex]; }
5041 int32_t startIndex;
5042 int32_t endIndex;
5043 BCCellBorder* borders;
5044 };
5046 BCCellBorders::BCCellBorders(int32_t aNumBorders,
5047 int32_t aStartIndex)
5048 {
5049 NS_ASSERTION((aNumBorders > 0) && (aStartIndex >= 0), "program error");
5050 startIndex = aStartIndex;
5051 endIndex = aStartIndex + aNumBorders - 1;
5052 borders = new BCCellBorder[aNumBorders];
5053 }
5055 // this function sets the new border properties and returns true if the border
5056 // segment will start a new segment and not be accumulated into the previous
5057 // segment.
5058 static bool
5059 SetBorder(const BCCellBorder& aNewBorder,
5060 BCCellBorder& aBorder)
5061 {
5062 bool changed = (aNewBorder.style != aBorder.style) ||
5063 (aNewBorder.width != aBorder.width) ||
5064 (aNewBorder.color != aBorder.color);
5065 aBorder.color = aNewBorder.color;
5066 aBorder.width = aNewBorder.width;
5067 aBorder.style = aNewBorder.style;
5068 aBorder.owner = aNewBorder.owner;
5070 return changed;
5071 }
5073 // this function will set the horizontal border. It will return true if the
5074 // existing segment will not be continued. Having a vertical owner of a corner
5075 // should also start a new segment.
5076 static bool
5077 SetHorBorder(const BCCellBorder& aNewBorder,
5078 const BCCornerInfo& aCorner,
5079 BCCellBorder& aBorder)
5080 {
5081 bool startSeg = ::SetBorder(aNewBorder, aBorder);
5082 if (!startSeg) {
5083 startSeg = ((NS_SIDE_LEFT != aCorner.ownerSide) && (NS_SIDE_RIGHT != aCorner.ownerSide));
5084 }
5085 return startSeg;
5086 }
5088 // Make the damage area larger on the top and bottom by at least one row and on the left and right
5089 // at least one column. This is done so that adjacent elements are part of the border calculations.
5090 // The extra segments and borders outside the actual damage area will not be updated in the cell map,
5091 // because they in turn would need info from adjacent segments outside the damage area to be accurate.
5092 void
5093 nsTableFrame::ExpandBCDamageArea(nsIntRect& aRect) const
5094 {
5095 int32_t numRows = GetRowCount();
5096 int32_t numCols = GetColCount();
5098 int32_t dStartX = aRect.x;
5099 int32_t dEndX = aRect.XMost() - 1;
5100 int32_t dStartY = aRect.y;
5101 int32_t dEndY = aRect.YMost() - 1;
5103 // expand the damage area in each direction
5104 if (dStartX > 0) {
5105 dStartX--;
5106 }
5107 if (dEndX < (numCols - 1)) {
5108 dEndX++;
5109 }
5110 if (dStartY > 0) {
5111 dStartY--;
5112 }
5113 if (dEndY < (numRows - 1)) {
5114 dEndY++;
5115 }
5116 // Check the damage area so that there are no cells spanning in or out. If there are any then
5117 // make the damage area as big as the table, similarly to the way the cell map decides whether
5118 // to rebuild versus expand. This could be optimized to expand to the smallest area that contains
5119 // no spanners, but it may not be worth the effort in general, and it would need to be done in the
5120 // cell map as well.
5121 bool haveSpanner = false;
5122 if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) {
5123 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
5124 // Get the ordered row groups
5125 RowGroupArray rowGroups;
5126 OrderRowGroups(rowGroups);
5128 // Scope outside loop to be used as hint.
5129 nsCellMap* cellMap = nullptr;
5130 for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
5131 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
5132 int32_t rgStartY = rgFrame->GetStartRowIndex();
5133 int32_t rgEndY = rgStartY + rgFrame->GetRowCount() - 1;
5134 if (dEndY < rgStartY)
5135 break;
5136 cellMap = tableCellMap->GetMapFor(rgFrame, cellMap);
5137 if (!cellMap) ABORT0();
5138 // check for spanners from above and below
5139 if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) {
5140 if (uint32_t(dStartY - rgStartY) >= cellMap->mRows.Length())
5141 ABORT0();
5142 const nsCellMap::CellDataArray& row =
5143 cellMap->mRows[dStartY - rgStartY];
5144 for (int32_t x = dStartX; x <= dEndX; x++) {
5145 CellData* cellData = row.SafeElementAt(x);
5146 if (cellData && (cellData->IsRowSpan())) {
5147 haveSpanner = true;
5148 break;
5149 }
5150 }
5151 if (dEndY < rgEndY) {
5152 if (uint32_t(dEndY + 1 - rgStartY) >= cellMap->mRows.Length())
5153 ABORT0();
5154 const nsCellMap::CellDataArray& row2 =
5155 cellMap->mRows[dEndY + 1 - rgStartY];
5156 for (int32_t x = dStartX; x <= dEndX; x++) {
5157 CellData* cellData = row2.SafeElementAt(x);
5158 if (cellData && (cellData->IsRowSpan())) {
5159 haveSpanner = true;
5160 break;
5161 }
5162 }
5163 }
5164 }
5165 // check for spanners on the left and right
5166 int32_t iterStartY = -1;
5167 int32_t iterEndY = -1;
5168 if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) {
5169 // the damage area starts in the row group
5170 iterStartY = dStartY;
5171 iterEndY = std::min(dEndY, rgEndY);
5172 }
5173 else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) {
5174 // the damage area ends in the row group
5175 iterStartY = rgStartY;
5176 iterEndY = dEndY;
5177 }
5178 else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) {
5179 // the damage area contains the row group
5180 iterStartY = rgStartY;
5181 iterEndY = rgEndY;
5182 }
5183 if ((iterStartY >= 0) && (iterEndY >= 0)) {
5184 for (int32_t y = iterStartY; y <= iterEndY; y++) {
5185 if (uint32_t(y - rgStartY) >= cellMap->mRows.Length())
5186 ABORT0();
5187 const nsCellMap::CellDataArray& row =
5188 cellMap->mRows[y - rgStartY];
5189 CellData* cellData = row.SafeElementAt(dStartX);
5190 if (cellData && (cellData->IsColSpan())) {
5191 haveSpanner = true;
5192 break;
5193 }
5194 if (dEndX < (numCols - 1)) {
5195 cellData = row.SafeElementAt(dEndX + 1);
5196 if (cellData && (cellData->IsColSpan())) {
5197 haveSpanner = true;
5198 break;
5199 }
5200 }
5201 }
5202 }
5203 }
5204 }
5205 if (haveSpanner) {
5206 // make the damage area the whole table
5207 aRect.x = 0;
5208 aRect.y = 0;
5209 aRect.width = numCols;
5210 aRect.height = numRows;
5211 }
5212 else {
5213 aRect.x = dStartX;
5214 aRect.y = dStartY;
5215 aRect.width = 1 + dEndX - dStartX;
5216 aRect.height = 1 + dEndY - dStartY;
5217 }
5218 }
5221 #define ADJACENT true
5222 #define HORIZONTAL true
5224 void
5225 BCMapCellInfo::SetTableTopLeftContBCBorder()
5226 {
5227 BCCellBorder currentBorder;
5228 //calculate continuous top first row & rowgroup border: special case
5229 //because it must include the table in the collapse
5230 if (mTopRow) {
5231 currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup,
5232 mTopRow, nullptr, mTableIsLTR,
5233 NS_SIDE_TOP, !ADJACENT);
5234 mTopRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
5235 }
5236 if (mCgAtRight && mColGroup) {
5237 //calculate continuous top colgroup border once per colgroup
5238 currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup,
5239 mTopRow, nullptr, mTableIsLTR,
5240 NS_SIDE_TOP, !ADJACENT);
5241 mColGroup->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
5242 }
5243 if (0 == mColIndex) {
5244 currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, nullptr,
5245 nullptr, nullptr, mTableIsLTR, NS_SIDE_LEFT,
5246 !ADJACENT);
5247 mTableFrame->SetContinuousLeftBCBorderWidth(currentBorder.width);
5248 }
5249 }
5251 void
5252 BCMapCellInfo::SetRowGroupLeftContBCBorder()
5253 {
5254 BCCellBorder currentBorder;
5255 //get row group continuous borders
5256 if (mRgAtBottom && mRowGroup) { //once per row group, so check for bottom
5257 currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup,
5258 nullptr, nullptr, mTableIsLTR, NS_SIDE_LEFT,
5259 !ADJACENT);
5260 mRowGroup->SetContinuousBCBorderWidth(mStartSide, currentBorder.width);
5261 }
5262 }
5264 void
5265 BCMapCellInfo::SetRowGroupRightContBCBorder()
5266 {
5267 BCCellBorder currentBorder;
5268 //get row group continuous borders
5269 if (mRgAtBottom && mRowGroup) { //once per mRowGroup, so check for bottom
5270 currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup,
5271 nullptr, nullptr, mTableIsLTR, NS_SIDE_RIGHT,
5272 ADJACENT);
5273 mRowGroup->SetContinuousBCBorderWidth(mEndSide, currentBorder.width);
5274 }
5275 }
5277 void
5278 BCMapCellInfo::SetColumnTopRightContBCBorder()
5279 {
5280 BCCellBorder currentBorder;
5281 //calculate column continuous borders
5282 //we only need to do this once, so we'll do it only on the first row
5283 currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
5284 mCurrentColFrame, mRowGroup, mTopRow, nullptr,
5285 mTableIsLTR, NS_SIDE_TOP, !ADJACENT);
5286 ((nsTableColFrame*) mCurrentColFrame)->SetContinuousBCBorderWidth(NS_SIDE_TOP,
5287 currentBorder.width);
5288 if (mNumTableCols == GetCellEndColIndex() + 1) {
5289 currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
5290 mCurrentColFrame, nullptr, nullptr, nullptr,
5291 mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT);
5292 }
5293 else {
5294 currentBorder = CompareBorders(nullptr, mCurrentColGroupFrame,
5295 mCurrentColFrame, nullptr,nullptr, nullptr,
5296 mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT);
5297 }
5298 mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_RIGHT,
5299 currentBorder.width);
5300 }
5302 void
5303 BCMapCellInfo::SetColumnBottomContBCBorder()
5304 {
5305 BCCellBorder currentBorder;
5306 //get col continuous border
5307 currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
5308 mCurrentColFrame, mRowGroup, mBottomRow,
5309 nullptr, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT);
5310 mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM,
5311 currentBorder.width);
5312 }
5314 void
5315 BCMapCellInfo::SetColGroupBottomContBCBorder()
5316 {
5317 BCCellBorder currentBorder;
5318 if (mColGroup) {
5319 currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup,
5320 mBottomRow, nullptr, mTableIsLTR,
5321 NS_SIDE_BOTTOM, ADJACENT);
5322 mColGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
5323 }
5324 }
5326 void
5327 BCMapCellInfo::SetRowGroupBottomContBCBorder()
5328 {
5329 BCCellBorder currentBorder;
5330 if (mRowGroup) {
5331 currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup,
5332 mBottomRow, nullptr, mTableIsLTR,
5333 NS_SIDE_BOTTOM, ADJACENT);
5334 mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
5335 }
5336 }
5338 void
5339 BCMapCellInfo::SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup,
5340 nsTableRowFrame* aNextRow)
5341 {
5342 BCCellBorder currentBorder, adjacentBorder;
5344 const nsIFrame* rowgroup = (mRgAtBottom) ? mRowGroup : nullptr;
5345 currentBorder = CompareBorders(nullptr, nullptr, nullptr, rowgroup, mBottomRow,
5346 nullptr, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT);
5348 adjacentBorder = CompareBorders(nullptr, nullptr, nullptr, aNextRowGroup,
5349 aNextRow, nullptr, mTableIsLTR, NS_SIDE_TOP,
5350 !ADJACENT);
5351 currentBorder = CompareBorders(false, currentBorder, adjacentBorder,
5352 HORIZONTAL);
5353 if (aNextRow) {
5354 aNextRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
5355 }
5356 if (mRgAtBottom && mRowGroup) {
5357 mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
5358 }
5359 }
5361 void
5362 BCMapCellInfo::SetRowLeftContBCBorder()
5363 {
5364 //get row continuous borders
5365 if (mCurrentRowFrame) {
5366 BCCellBorder currentBorder;
5367 currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup,
5368 mCurrentRowFrame, nullptr, mTableIsLTR,
5369 NS_SIDE_LEFT, !ADJACENT);
5370 mCurrentRowFrame->SetContinuousBCBorderWidth(mStartSide,
5371 currentBorder.width);
5372 }
5373 }
5375 void
5376 BCMapCellInfo::SetRowRightContBCBorder()
5377 {
5378 if (mCurrentRowFrame) {
5379 BCCellBorder currentBorder;
5380 currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup,
5381 mCurrentRowFrame, nullptr, mTableIsLTR,
5382 NS_SIDE_RIGHT, ADJACENT);
5383 mCurrentRowFrame->SetContinuousBCBorderWidth(mEndSide,
5384 currentBorder.width);
5385 }
5386 }
5387 void
5388 BCMapCellInfo::SetTableTopBorderWidth(BCPixelSize aWidth)
5389 {
5390 mTableBCData->mTopBorderWidth = std::max(mTableBCData->mTopBorderWidth, aWidth);
5391 }
5393 void
5394 BCMapCellInfo::SetTableLeftBorderWidth(int32_t aRowY, BCPixelSize aWidth)
5395 {
5396 // update the left/right first cell border
5397 if (aRowY == 0) {
5398 if (mTableIsLTR) {
5399 mTableBCData->mLeftCellBorderWidth = aWidth;
5400 }
5401 else {
5402 mTableBCData->mRightCellBorderWidth = aWidth;
5403 }
5404 }
5405 mTableBCData->mLeftBorderWidth = std::max(mTableBCData->mLeftBorderWidth,
5406 aWidth);
5407 }
5409 void
5410 BCMapCellInfo::SetTableRightBorderWidth(int32_t aRowY, BCPixelSize aWidth)
5411 {
5412 // update the left/right first cell border
5413 if (aRowY == 0) {
5414 if (mTableIsLTR) {
5415 mTableBCData->mRightCellBorderWidth = aWidth;
5416 }
5417 else {
5418 mTableBCData->mLeftCellBorderWidth = aWidth;
5419 }
5420 }
5421 mTableBCData->mRightBorderWidth = std::max(mTableBCData->mRightBorderWidth,
5422 aWidth);
5423 }
5425 void
5426 BCMapCellInfo::SetRightBorderWidths(BCPixelSize aWidth)
5427 {
5428 // update the borders of the cells and cols affected
5429 if (mCell) {
5430 mCell->SetBorderWidth(mEndSide, std::max(aWidth,
5431 mCell->GetBorderWidth(mEndSide)));
5432 }
5433 if (mRightCol) {
5434 BCPixelSize half = BC_BORDER_LEFT_HALF(aWidth);
5435 mRightCol->SetRightBorderWidth(std::max(nscoord(half),
5436 mRightCol->GetRightBorderWidth()));
5437 }
5438 }
5440 void
5441 BCMapCellInfo::SetBottomBorderWidths(BCPixelSize aWidth)
5442 {
5443 // update the borders of the affected cells and rows
5444 if (mCell) {
5445 mCell->SetBorderWidth(NS_SIDE_BOTTOM, std::max(aWidth,
5446 mCell->GetBorderWidth(NS_SIDE_BOTTOM)));
5447 }
5448 if (mBottomRow) {
5449 BCPixelSize half = BC_BORDER_TOP_HALF(aWidth);
5450 mBottomRow->SetBottomBCBorderWidth(std::max(nscoord(half),
5451 mBottomRow->GetBottomBCBorderWidth()));
5452 }
5453 }
5454 void
5455 BCMapCellInfo::SetTopBorderWidths(BCPixelSize aWidth)
5456 {
5457 if (mCell) {
5458 mCell->SetBorderWidth(NS_SIDE_TOP, std::max(aWidth,
5459 mCell->GetBorderWidth(NS_SIDE_TOP)));
5460 }
5461 if (mTopRow) {
5462 BCPixelSize half = BC_BORDER_BOTTOM_HALF(aWidth);
5463 mTopRow->SetTopBCBorderWidth(std::max(nscoord(half),
5464 mTopRow->GetTopBCBorderWidth()));
5465 }
5466 }
5467 void
5468 BCMapCellInfo::SetLeftBorderWidths(BCPixelSize aWidth)
5469 {
5470 if (mCell) {
5471 mCell->SetBorderWidth(mStartSide, std::max(aWidth,
5472 mCell->GetBorderWidth(mStartSide)));
5473 }
5474 if (mLeftCol) {
5475 BCPixelSize half = BC_BORDER_RIGHT_HALF(aWidth);
5476 mLeftCol->SetLeftBorderWidth(std::max(nscoord(half),
5477 mLeftCol->GetLeftBorderWidth()));
5478 }
5479 }
5481 void
5482 BCMapCellInfo::SetTableBottomBorderWidth(BCPixelSize aWidth)
5483 {
5484 mTableBCData->mBottomBorderWidth = std::max(mTableBCData->mBottomBorderWidth,
5485 aWidth);
5486 }
5488 void
5489 BCMapCellInfo::SetColumn(int32_t aColX)
5490 {
5491 mCurrentColFrame = mTableFrame->GetColFrame(aColX);
5492 if (!mCurrentColFrame) {
5493 NS_ERROR("null mCurrentColFrame");
5494 }
5495 mCurrentColGroupFrame = static_cast<nsTableColGroupFrame*>
5496 (mCurrentColFrame->GetParent());
5497 if (!mCurrentColGroupFrame) {
5498 NS_ERROR("null mCurrentColGroupFrame");
5499 }
5500 }
5502 void
5503 BCMapCellInfo::IncrementRow(bool aResetToTopRowOfCell)
5504 {
5505 mCurrentRowFrame = (aResetToTopRowOfCell) ? mTopRow :
5506 mCurrentRowFrame->GetNextRow();
5507 }
5509 BCCellBorder
5510 BCMapCellInfo::GetTopEdgeBorder()
5511 {
5512 return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
5513 mRowGroup, mTopRow, mCell, mTableIsLTR, NS_SIDE_TOP,
5514 !ADJACENT);
5515 }
5517 BCCellBorder
5518 BCMapCellInfo::GetBottomEdgeBorder()
5519 {
5520 return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
5521 mRowGroup, mBottomRow, mCell, mTableIsLTR,
5522 NS_SIDE_BOTTOM, ADJACENT);
5523 }
5524 BCCellBorder
5525 BCMapCellInfo::GetLeftEdgeBorder()
5526 {
5527 return CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup,
5528 mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_LEFT,
5529 !ADJACENT);
5530 }
5531 BCCellBorder
5532 BCMapCellInfo::GetRightEdgeBorder()
5533 {
5534 return CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup,
5535 mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_RIGHT,
5536 ADJACENT);
5537 }
5538 BCCellBorder
5539 BCMapCellInfo::GetRightInternalBorder()
5540 {
5541 const nsIFrame* cg = (mCgAtRight) ? mColGroup : nullptr;
5542 return CompareBorders(nullptr, cg, mRightCol, nullptr, nullptr, mCell,
5543 mTableIsLTR, NS_SIDE_RIGHT, ADJACENT);
5544 }
5546 BCCellBorder
5547 BCMapCellInfo::GetLeftInternalBorder()
5548 {
5549 const nsIFrame* cg = (mCgAtLeft) ? mColGroup : nullptr;
5550 return CompareBorders(nullptr, cg, mLeftCol, nullptr, nullptr, mCell,
5551 mTableIsLTR, NS_SIDE_LEFT, !ADJACENT);
5552 }
5554 BCCellBorder
5555 BCMapCellInfo::GetBottomInternalBorder()
5556 {
5557 const nsIFrame* rg = (mRgAtBottom) ? mRowGroup : nullptr;
5558 return CompareBorders(nullptr, nullptr, nullptr, rg, mBottomRow, mCell,
5559 mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT);
5560 }
5562 BCCellBorder
5563 BCMapCellInfo::GetTopInternalBorder()
5564 {
5565 const nsIFrame* rg = (mRgAtTop) ? mRowGroup : nullptr;
5566 return CompareBorders(nullptr, nullptr, nullptr, rg, mTopRow, mCell,
5567 mTableIsLTR, NS_SIDE_TOP, !ADJACENT);
5568 }
5570 /* Here is the order for storing border edges in the cell map as a cell is processed. There are
5571 n=colspan top and bottom border edges per cell and n=rowspan left and right border edges per cell.
5573 1) On the top edge of the table, store the top edge. Never store the top edge otherwise, since
5574 a bottom edge from a cell above will take care of it.
5575 2) On the left edge of the table, store the left edge. Never store the left edge othewise, since
5576 a right edge from a cell to the left will take care of it.
5577 3) Store the right edge (or edges if a row span)
5578 4) Store the bottom edge (or edges if a col span)
5580 Since corners are computed with only an array of BCCornerInfo indexed by the number-of-cols, corner
5581 calculations are somewhat complicated. Using an array with number-of-rows * number-of-col entries
5582 would simplify this, but at an extra in memory cost of nearly 12 bytes per cell map entry. Collapsing
5583 borders already have about an extra 8 byte per cell map entry overhead (this could be
5584 reduced to 4 bytes if we are willing to not store border widths in nsTableCellFrame), Here are the
5585 rules in priority order for storing cornes in the cell map as a cell is processed. top-left means the
5586 left endpoint of the border edge on the top of the cell. There are n=colspan top and bottom border
5587 edges per cell and n=rowspan left and right border edges per cell.
5589 1) On the top edge of the table, store the top-left corner, unless on the left edge of the table.
5590 Never store the top-right corner, since it will get stored as a right-top corner.
5591 2) On the left edge of the table, store the left-top corner. Never store the left-bottom corner,
5592 since it will get stored as a bottom-left corner.
5593 3) Store the right-top corner if (a) it is the top right corner of the table or (b) it is not on
5594 the top edge of the table. Never store the right-bottom corner since it will get stored as a
5595 bottom-right corner.
5596 4) Store the bottom-right corner, if it is the bottom right corner of the table. Never store it
5597 otherwise, since it will get stored as either a right-top corner by a cell below or
5598 a bottom-left corner from a cell to the right.
5599 5) Store the bottom-left corner, if (a) on the bottom edge of the table or (b) if the left edge hits
5600 the top side of a colspan in its interior. Never store the corner otherwise, since it will
5601 get stored as a right-top corner by a cell from below.
5603 XXX the BC-RTL hack - The correct fix would be a rewrite as described in bug 203686.
5604 In order to draw borders in rtl conditions somehow correct, the existing structure which relies
5605 heavily on the assumption that the next cell sibling will be on the right side, has been modified.
5606 We flip the border during painting and during style lookup. Look for tableIsLTR for places where
5607 the flipping is done.
5608 */
5612 // Calc the dominant border at every cell edge and corner within the current damage area
5613 void
5614 nsTableFrame::CalcBCBorders()
5615 {
5616 NS_ASSERTION(IsBorderCollapse(),
5617 "calling CalcBCBorders on separated-border table");
5618 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
5619 int32_t numRows = GetRowCount();
5620 int32_t numCols = GetColCount();
5621 if (!numRows || !numCols)
5622 return; // nothing to do
5624 // Get the property holding the table damage area and border widths
5625 BCPropertyData* propData = GetBCProperty();
5626 if (!propData) ABORT0();
5630 // calculate an expanded damage area
5631 nsIntRect damageArea(propData->mDamageArea);
5632 ExpandBCDamageArea(damageArea);
5634 // segments that are on the table border edges need
5635 // to be initialized only once
5636 bool tableBorderReset[4];
5637 for (uint32_t sideX = NS_SIDE_TOP; sideX <= NS_SIDE_LEFT; sideX++) {
5638 tableBorderReset[sideX] = false;
5639 }
5641 // vertical borders indexed in x-direction (cols)
5642 BCCellBorders lastVerBorders(damageArea.width + 1, damageArea.x);
5643 if (!lastVerBorders.borders) ABORT0();
5644 BCCellBorder lastTopBorder, lastBottomBorder;
5645 // horizontal borders indexed in x-direction (cols)
5646 BCCellBorders lastBottomBorders(damageArea.width + 1, damageArea.x);
5647 if (!lastBottomBorders.borders) ABORT0();
5648 bool startSeg;
5649 bool gotRowBorder = false;
5651 BCMapCellInfo info(this), ajaInfo(this);
5653 BCCellBorder currentBorder, adjacentBorder;
5654 BCCorners topCorners(damageArea.width + 1, damageArea.x);
5655 if (!topCorners.corners) ABORT0();
5656 BCCorners bottomCorners(damageArea.width + 1, damageArea.x);
5657 if (!bottomCorners.corners) ABORT0();
5659 BCMapCellIterator iter(this, damageArea);
5660 for (iter.First(info); !iter.mAtEnd; iter.Next(info)) {
5661 // see if lastTopBorder, lastBottomBorder need to be reset
5662 if (iter.IsNewRow()) {
5663 gotRowBorder = false;
5664 lastTopBorder.Reset(info.mRowIndex, info.mRowSpan);
5665 lastBottomBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
5666 }
5667 else if (info.mColIndex > damageArea.x) {
5668 lastBottomBorder = lastBottomBorders[info.mColIndex - 1];
5669 if (info.mRowIndex >
5670 (lastBottomBorder.rowIndex - lastBottomBorder.rowSpan)) {
5671 // the top border's left edge butts against the middle of a rowspan
5672 lastTopBorder.Reset(info.mRowIndex, info.mRowSpan);
5673 }
5674 if (lastBottomBorder.rowIndex > (info.GetCellEndRowIndex() + 1)) {
5675 // the bottom border's left edge butts against the middle of a rowspan
5676 lastBottomBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
5677 }
5678 }
5680 // find the dominant border considering the cell's top border and the table,
5681 // row group, row if the border is at the top of the table, otherwise it was
5682 // processed in a previous row
5683 if (0 == info.mRowIndex) {
5684 if (!tableBorderReset[NS_SIDE_TOP]) {
5685 propData->mTopBorderWidth = 0;
5686 tableBorderReset[NS_SIDE_TOP] = true;
5687 }
5688 for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex();
5689 colX++) {
5690 info.SetColumn(colX);
5691 currentBorder = info.GetTopEdgeBorder();
5692 // update/store the top left & top right corners of the seg
5693 BCCornerInfo& tlCorner = topCorners[colX]; // top left
5694 if (0 == colX) {
5695 // we are on right hand side of the corner
5696 tlCorner.Set(NS_SIDE_RIGHT, currentBorder);
5697 }
5698 else {
5699 tlCorner.Update(NS_SIDE_RIGHT, currentBorder);
5700 tableCellMap->SetBCBorderCorner(eTopLeft, *iter.mCellMap, 0, 0, colX,
5701 mozilla::css::Side(tlCorner.ownerSide),
5702 tlCorner.subWidth,
5703 tlCorner.bevel);
5704 }
5705 topCorners[colX + 1].Set(NS_SIDE_LEFT, currentBorder); // top right
5706 // update lastTopBorder and see if a new segment starts
5707 startSeg = SetHorBorder(currentBorder, tlCorner, lastTopBorder);
5708 // store the border segment in the cell map
5709 tableCellMap->SetBCBorderEdge(NS_SIDE_TOP, *iter.mCellMap, 0, 0, colX,
5710 1, currentBorder.owner,
5711 currentBorder.width, startSeg);
5713 info.SetTableTopBorderWidth(currentBorder.width);
5714 info.SetTopBorderWidths(currentBorder.width);
5715 info.SetColumnTopRightContBCBorder();
5716 }
5717 info.SetTableTopLeftContBCBorder();
5718 }
5719 else {
5720 // see if the top border needs to be the start of a segment due to a
5721 // vertical border owning the corner
5722 if (info.mColIndex > 0) {
5723 BCData& data = info.mCellData->mData;
5724 if (!data.IsTopStart()) {
5725 mozilla::css::Side cornerSide;
5726 bool bevel;
5727 data.GetCorner(cornerSide, bevel);
5728 if ((NS_SIDE_TOP == cornerSide) || (NS_SIDE_BOTTOM == cornerSide)) {
5729 data.SetTopStart(true);
5730 }
5731 }
5732 }
5733 }
5735 // find the dominant border considering the cell's left border and the
5736 // table, col group, col if the border is at the left of the table,
5737 // otherwise it was processed in a previous col
5738 if (0 == info.mColIndex) {
5739 if (!tableBorderReset[NS_SIDE_LEFT]) {
5740 propData->mLeftBorderWidth = 0;
5741 tableBorderReset[NS_SIDE_LEFT] = true;
5742 }
5743 info.mCurrentRowFrame = nullptr;
5744 for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex();
5745 rowY++) {
5746 info.IncrementRow(rowY == info.mRowIndex);
5747 currentBorder = info.GetLeftEdgeBorder();
5748 BCCornerInfo& tlCorner = (0 == rowY) ? topCorners[0] : bottomCorners[0];
5749 tlCorner.Update(NS_SIDE_BOTTOM, currentBorder);
5750 tableCellMap->SetBCBorderCorner(eTopLeft, *iter.mCellMap,
5751 iter.mRowGroupStart, rowY, 0,
5752 mozilla::css::Side(tlCorner.ownerSide),
5753 tlCorner.subWidth,
5754 tlCorner.bevel);
5755 bottomCorners[0].Set(NS_SIDE_TOP, currentBorder); // bottom left
5757 // update lastVerBordersBorder and see if a new segment starts
5758 startSeg = SetBorder(currentBorder, lastVerBorders[0]);
5759 // store the border segment in the cell map
5760 tableCellMap->SetBCBorderEdge(NS_SIDE_LEFT, *iter.mCellMap,
5761 iter.mRowGroupStart, rowY, info.mColIndex,
5762 1, currentBorder.owner,
5763 currentBorder.width, startSeg);
5764 info.SetTableLeftBorderWidth(rowY , currentBorder.width);
5765 info.SetLeftBorderWidths(currentBorder.width);
5766 info.SetRowLeftContBCBorder();
5767 }
5768 info.SetRowGroupLeftContBCBorder();
5769 }
5771 // find the dominant border considering the cell's right border, adjacent
5772 // cells and the table, row group, row
5773 if (info.mNumTableCols == info.GetCellEndColIndex() + 1) {
5774 // touches right edge of table
5775 if (!tableBorderReset[NS_SIDE_RIGHT]) {
5776 propData->mRightBorderWidth = 0;
5777 tableBorderReset[NS_SIDE_RIGHT] = true;
5778 }
5779 info.mCurrentRowFrame = nullptr;
5780 for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex();
5781 rowY++) {
5782 info.IncrementRow(rowY == info.mRowIndex);
5783 currentBorder = info.GetRightEdgeBorder();
5784 // update/store the top right & bottom right corners
5785 BCCornerInfo& trCorner = (0 == rowY) ?
5786 topCorners[info.GetCellEndColIndex() + 1] :
5787 bottomCorners[info.GetCellEndColIndex() + 1];
5788 trCorner.Update(NS_SIDE_BOTTOM, currentBorder); // top right
5789 tableCellMap->SetBCBorderCorner(eTopRight, *iter.mCellMap,
5790 iter.mRowGroupStart, rowY,
5791 info.GetCellEndColIndex(),
5792 mozilla::css::Side(trCorner.ownerSide),
5793 trCorner.subWidth,
5794 trCorner.bevel);
5795 BCCornerInfo& brCorner = bottomCorners[info.GetCellEndColIndex() + 1];
5796 brCorner.Set(NS_SIDE_TOP, currentBorder); // bottom right
5797 tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap,
5798 iter.mRowGroupStart, rowY,
5799 info.GetCellEndColIndex(),
5800 mozilla::css::Side(brCorner.ownerSide),
5801 brCorner.subWidth,
5802 brCorner.bevel);
5803 // update lastVerBorders and see if a new segment starts
5804 startSeg = SetBorder(currentBorder,
5805 lastVerBorders[info.GetCellEndColIndex() + 1]);
5806 // store the border segment in the cell map and update cellBorders
5807 tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *iter.mCellMap,
5808 iter.mRowGroupStart, rowY,
5809 info.GetCellEndColIndex(), 1,
5810 currentBorder.owner, currentBorder.width,
5811 startSeg);
5812 info.SetTableRightBorderWidth(rowY, currentBorder.width);
5813 info.SetRightBorderWidths(currentBorder.width);
5814 info.SetRowRightContBCBorder();
5815 }
5816 info.SetRowGroupRightContBCBorder();
5817 }
5818 else {
5819 int32_t segLength = 0;
5820 BCMapCellInfo priorAjaInfo(this);
5821 for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex();
5822 rowY += segLength) {
5823 iter.PeekRight(info, rowY, ajaInfo);
5824 currentBorder = info.GetRightInternalBorder();
5825 adjacentBorder = ajaInfo.GetLeftInternalBorder();
5826 currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
5827 adjacentBorder, !HORIZONTAL);
5829 segLength = std::max(1, ajaInfo.mRowIndex + ajaInfo.mRowSpan - rowY);
5830 segLength = std::min(segLength, info.mRowIndex + info.mRowSpan - rowY);
5832 // update lastVerBorders and see if a new segment starts
5833 startSeg = SetBorder(currentBorder,
5834 lastVerBorders[info.GetCellEndColIndex() + 1]);
5835 // store the border segment in the cell map and update cellBorders
5836 if (info.GetCellEndColIndex() < damageArea.XMost() &&
5837 rowY >= damageArea.y && rowY < damageArea.YMost()) {
5838 tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *iter.mCellMap,
5839 iter.mRowGroupStart, rowY,
5840 info.GetCellEndColIndex(), segLength,
5841 currentBorder.owner,
5842 currentBorder.width, startSeg);
5843 info.SetRightBorderWidths(currentBorder.width);
5844 ajaInfo.SetLeftBorderWidths(currentBorder.width);
5845 }
5846 // update the top right corner
5847 bool hitsSpanOnRight = (rowY > ajaInfo.mRowIndex) &&
5848 (rowY < ajaInfo.mRowIndex + ajaInfo.mRowSpan);
5849 BCCornerInfo* trCorner = ((0 == rowY) || hitsSpanOnRight) ?
5850 &topCorners[info.GetCellEndColIndex() + 1] :
5851 &bottomCorners[info.GetCellEndColIndex() + 1];
5852 trCorner->Update(NS_SIDE_BOTTOM, currentBorder);
5853 // if this is not the first time through,
5854 // consider the segment to the right
5855 if (rowY != info.mRowIndex) {
5856 currentBorder = priorAjaInfo.GetBottomInternalBorder();
5857 adjacentBorder = ajaInfo.GetTopInternalBorder();
5858 currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
5859 adjacentBorder, HORIZONTAL);
5860 trCorner->Update(NS_SIDE_RIGHT, currentBorder);
5861 }
5862 // store the top right corner in the cell map
5863 if (info.GetCellEndColIndex() < damageArea.XMost() &&
5864 rowY >= damageArea.y) {
5865 if (0 != rowY) {
5866 tableCellMap->SetBCBorderCorner(eTopRight, *iter.mCellMap,
5867 iter.mRowGroupStart, rowY,
5868 info.GetCellEndColIndex(),
5869 mozilla::css::Side(trCorner->ownerSide),
5870 trCorner->subWidth,
5871 trCorner->bevel);
5872 }
5873 // store any corners this cell spans together with the aja cell
5874 for (int32_t rX = rowY + 1; rX < rowY + segLength; rX++) {
5875 tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap,
5876 iter.mRowGroupStart, rX,
5877 info.GetCellEndColIndex(),
5878 mozilla::css::Side(trCorner->ownerSide),
5879 trCorner->subWidth, false);
5880 }
5881 }
5882 // update bottom right corner, topCorners, bottomCorners
5883 hitsSpanOnRight = (rowY + segLength <
5884 ajaInfo.mRowIndex + ajaInfo.mRowSpan);
5885 BCCornerInfo& brCorner = (hitsSpanOnRight) ?
5886 topCorners[info.GetCellEndColIndex() + 1] :
5887 bottomCorners[info.GetCellEndColIndex() + 1];
5888 brCorner.Set(NS_SIDE_TOP, currentBorder);
5889 priorAjaInfo = ajaInfo;
5890 }
5891 }
5892 for (int32_t colX = info.mColIndex + 1; colX <= info.GetCellEndColIndex();
5893 colX++) {
5894 lastVerBorders[colX].Reset(0,1);
5895 }
5897 // find the dominant border considering the cell's bottom border, adjacent
5898 // cells and the table, row group, row
5899 if (info.mNumTableRows == info.GetCellEndRowIndex() + 1) {
5900 // touches bottom edge of table
5901 if (!tableBorderReset[NS_SIDE_BOTTOM]) {
5902 propData->mBottomBorderWidth = 0;
5903 tableBorderReset[NS_SIDE_BOTTOM] = true;
5904 }
5905 for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex();
5906 colX++) {
5907 info.SetColumn(colX);
5908 currentBorder = info.GetBottomEdgeBorder();
5909 // update/store the bottom left & bottom right corners
5910 BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left
5911 blCorner.Update(NS_SIDE_RIGHT, currentBorder);
5912 tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap,
5913 iter.mRowGroupStart,
5914 info.GetCellEndRowIndex(),
5915 colX,
5916 mozilla::css::Side(blCorner.ownerSide),
5917 blCorner.subWidth, blCorner.bevel);
5918 BCCornerInfo& brCorner = bottomCorners[colX + 1]; // bottom right
5919 brCorner.Update(NS_SIDE_LEFT, currentBorder);
5920 if (info.mNumTableCols == colX + 1) { // lower right corner of the table
5921 tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap,
5922 iter.mRowGroupStart,
5923 info.GetCellEndRowIndex(),colX,
5924 mozilla::css::Side(brCorner.ownerSide),
5925 brCorner.subWidth,
5926 brCorner.bevel, true);
5927 }
5928 // update lastBottomBorder and see if a new segment starts
5929 startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder);
5930 if (!startSeg) {
5931 // make sure that we did not compare apples to oranges i.e. the
5932 // current border should be a continuation of the lastBottomBorder,
5933 // as it is a bottom border
5934 // add 1 to the info.GetCellEndRowIndex()
5935 startSeg = (lastBottomBorder.rowIndex !=
5936 (info.GetCellEndRowIndex() + 1));
5937 }
5938 // store the border segment in the cell map and update cellBorders
5939 tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *iter.mCellMap,
5940 iter.mRowGroupStart,
5941 info.GetCellEndRowIndex(),
5942 colX, 1, currentBorder.owner,
5943 currentBorder.width, startSeg);
5944 // update lastBottomBorders
5945 lastBottomBorder.rowIndex = info.GetCellEndRowIndex() + 1;
5946 lastBottomBorder.rowSpan = info.mRowSpan;
5947 lastBottomBorders[colX] = lastBottomBorder;
5949 info.SetBottomBorderWidths(currentBorder.width);
5950 info.SetTableBottomBorderWidth(currentBorder.width);
5951 info.SetColumnBottomContBCBorder();
5952 }
5953 info.SetRowGroupBottomContBCBorder();
5954 info.SetColGroupBottomContBCBorder();
5955 }
5956 else {
5957 int32_t segLength = 0;
5958 for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex();
5959 colX += segLength) {
5960 iter.PeekBottom(info, colX, ajaInfo);
5961 currentBorder = info.GetBottomInternalBorder();
5962 adjacentBorder = ajaInfo.GetTopInternalBorder();
5963 currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
5964 adjacentBorder, HORIZONTAL);
5965 segLength = std::max(1, ajaInfo.mColIndex + ajaInfo.mColSpan - colX);
5966 segLength = std::min(segLength, info.mColIndex + info.mColSpan - colX);
5968 // update, store the bottom left corner
5969 BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left
5970 bool hitsSpanBelow = (colX > ajaInfo.mColIndex) &&
5971 (colX < ajaInfo.mColIndex + ajaInfo.mColSpan);
5972 bool update = true;
5973 if ((colX == info.mColIndex) && (colX > damageArea.x)) {
5974 int32_t prevRowIndex = lastBottomBorders[colX - 1].rowIndex;
5975 if (prevRowIndex > info.GetCellEndRowIndex() + 1) {
5976 // hits a rowspan on the right
5977 update = false;
5978 // the corner was taken care of during the cell on the left
5979 }
5980 else if (prevRowIndex < info.GetCellEndRowIndex() + 1) {
5981 // spans below the cell to the left
5982 topCorners[colX] = blCorner;
5983 blCorner.Set(NS_SIDE_RIGHT, currentBorder);
5984 update = false;
5985 }
5986 }
5987 if (update) {
5988 blCorner.Update(NS_SIDE_RIGHT, currentBorder);
5989 }
5990 if (info.GetCellEndRowIndex() < damageArea.YMost() &&
5991 (colX >= damageArea.x)) {
5992 if (hitsSpanBelow) {
5993 tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap,
5994 iter.mRowGroupStart,
5995 info.GetCellEndRowIndex(), colX,
5996 mozilla::css::Side(blCorner.ownerSide),
5997 blCorner.subWidth, blCorner.bevel);
5998 }
5999 // store any corners this cell spans together with the aja cell
6000 for (int32_t cX = colX + 1; cX < colX + segLength; cX++) {
6001 BCCornerInfo& corner = bottomCorners[cX];
6002 corner.Set(NS_SIDE_RIGHT, currentBorder);
6003 tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap,
6004 iter.mRowGroupStart,
6005 info.GetCellEndRowIndex(), cX,
6006 mozilla::css::Side(corner.ownerSide),
6007 corner.subWidth,
6008 false);
6009 }
6010 }
6011 // update lastBottomBorders and see if a new segment starts
6012 startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder);
6013 if (!startSeg) {
6014 // make sure that we did not compare apples to oranges i.e. the
6015 // current border should be a continuation of the lastBottomBorder,
6016 // as it is a bottom border
6017 // add 1 to the info.GetCellEndRowIndex()
6018 startSeg = (lastBottomBorder.rowIndex !=
6019 info.GetCellEndRowIndex() + 1);
6020 }
6021 lastBottomBorder.rowIndex = info.GetCellEndRowIndex() + 1;
6022 lastBottomBorder.rowSpan = info.mRowSpan;
6023 for (int32_t cX = colX; cX < colX + segLength; cX++) {
6024 lastBottomBorders[cX] = lastBottomBorder;
6025 }
6027 // store the border segment the cell map and update cellBorders
6028 if (info.GetCellEndRowIndex() < damageArea.YMost() &&
6029 (colX >= damageArea.x) &&
6030 (colX < damageArea.XMost())) {
6031 tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *iter.mCellMap,
6032 iter.mRowGroupStart,
6033 info.GetCellEndRowIndex(),
6034 colX, segLength, currentBorder.owner,
6035 currentBorder.width, startSeg);
6036 info.SetBottomBorderWidths(currentBorder.width);
6037 ajaInfo.SetTopBorderWidths(currentBorder.width);
6038 }
6039 // update bottom right corner
6040 BCCornerInfo& brCorner = bottomCorners[colX + segLength];
6041 brCorner.Update(NS_SIDE_LEFT, currentBorder);
6042 }
6043 if (!gotRowBorder && 1 == info.mRowSpan &&
6044 (ajaInfo.mTopRow || info.mRgAtBottom)) {
6045 //get continuous row/row group border
6046 //we need to check the row group's bottom border if this is
6047 //the last row in the row group, but only a cell with rowspan=1
6048 //will know whether *this* row is at the bottom
6049 const nsIFrame* nextRowGroup = (ajaInfo.mRgAtTop) ? ajaInfo.mRowGroup :
6050 nullptr;
6051 info.SetInnerRowGroupBottomContBCBorder(nextRowGroup, ajaInfo.mTopRow);
6052 gotRowBorder = true;
6053 }
6054 }
6056 // see if the cell to the right had a rowspan and its lower left border
6057 // needs be joined with this one's bottom
6058 // if there is a cell to the right and the cell to right was a rowspan
6059 if ((info.mNumTableCols != info.GetCellEndColIndex() + 1) &&
6060 (lastBottomBorders[info.GetCellEndColIndex() + 1].rowSpan > 1)) {
6061 BCCornerInfo& corner = bottomCorners[info.GetCellEndColIndex() + 1];
6062 if ((NS_SIDE_TOP != corner.ownerSide) &&
6063 (NS_SIDE_BOTTOM != corner.ownerSide)) {
6064 // not a vertical owner
6065 BCCellBorder& thisBorder = lastBottomBorder;
6066 BCCellBorder& nextBorder = lastBottomBorders[info.mColIndex + 1];
6067 if ((thisBorder.color == nextBorder.color) &&
6068 (thisBorder.width == nextBorder.width) &&
6069 (thisBorder.style == nextBorder.style)) {
6070 // set the flag on the next border indicating it is not the start of a
6071 // new segment
6072 if (iter.mCellMap) {
6073 tableCellMap->ResetTopStart(NS_SIDE_BOTTOM, *iter.mCellMap,
6074 info.GetCellEndRowIndex(),
6075 info.GetCellEndColIndex() + 1);
6076 }
6077 }
6078 }
6079 }
6080 } // for (iter.First(info); info.mCell; iter.Next(info)) {
6081 // reset the bc flag and damage area
6082 SetNeedToCalcBCBorders(false);
6083 propData->mDamageArea = nsIntRect(0,0,0,0);
6084 #ifdef DEBUG_TABLE_CELLMAP
6085 mCellMap->Dump();
6086 #endif
6087 }
6089 class BCPaintBorderIterator;
6091 struct BCVerticalSeg
6092 {
6093 BCVerticalSeg();
6095 void Start(BCPaintBorderIterator& aIter,
6096 BCBorderOwner aBorderOwner,
6097 BCPixelSize aVerSegWidth,
6098 BCPixelSize aHorSegHeight);
6100 void Initialize(BCPaintBorderIterator& aIter);
6101 void GetBottomCorner(BCPaintBorderIterator& aIter,
6102 BCPixelSize aHorSegHeight);
6105 void Paint(BCPaintBorderIterator& aIter,
6106 nsRenderingContext& aRenderingContext,
6107 BCPixelSize aHorSegHeight);
6108 void AdvanceOffsetY();
6109 void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
6112 union {
6113 nsTableColFrame* mCol;
6114 int32_t mColWidth;
6115 };
6116 nscoord mOffsetX; // x-offset with respect to the table edge
6117 nscoord mOffsetY; // y-offset with respect to the table edge
6118 nscoord mLength; // vertical length including corners
6119 BCPixelSize mWidth; // width in pixels
6121 nsTableCellFrame* mAjaCell; // previous sibling to the first cell
6122 // where the segment starts, it can be
6123 // the owner of a segment
6124 nsTableCellFrame* mFirstCell; // cell at the start of the segment
6125 nsTableRowGroupFrame* mFirstRowGroup; // row group at the start of the segment
6126 nsTableRowFrame* mFirstRow; // row at the start of the segment
6127 nsTableCellFrame* mLastCell; // cell at the current end of the
6128 // segment
6131 uint8_t mOwner; // owner of the border, defines the
6132 // style
6133 mozilla::css::Side mTopBevelSide; // direction to bevel at the top
6134 nscoord mTopBevelOffset; // how much to bevel at the top
6135 BCPixelSize mBottomHorSegHeight; // height of the crossing
6136 //horizontal border
6137 nscoord mBottomOffset; // how much longer is the segment due
6138 // to the horizontal border, by this
6139 // amount the next segment needs to be
6140 // shifted.
6141 bool mIsBottomBevel; // should we bevel at the bottom
6142 };
6144 struct BCHorizontalSeg
6145 {
6146 BCHorizontalSeg();
6148 void Start(BCPaintBorderIterator& aIter,
6149 BCBorderOwner aBorderOwner,
6150 BCPixelSize aBottomVerSegWidth,
6151 BCPixelSize aHorSegHeight);
6152 void GetRightCorner(BCPaintBorderIterator& aIter,
6153 BCPixelSize aLeftSegWidth);
6154 void AdvanceOffsetX(int32_t aIncrement);
6155 void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
6156 void Paint(BCPaintBorderIterator& aIter,
6157 nsRenderingContext& aRenderingContext);
6159 nscoord mOffsetX; // x-offset with respect to the table edge
6160 nscoord mOffsetY; // y-offset with respect to the table edge
6161 nscoord mLength; // horizontal length including corners
6162 BCPixelSize mWidth; // border width in pixels
6163 nscoord mLeftBevelOffset; // how much to bevel at the left
6164 mozilla::css::Side mLeftBevelSide; // direction to bevel at the left
6165 bool mIsRightBevel; // should we bevel at the right end
6166 nscoord mRightBevelOffset; // how much to bevel at the right
6167 mozilla::css::Side mRightBevelSide; // direction to bevel at the right
6168 nscoord mEndOffset; // how much longer is the segment due
6169 // to the vertical border, by this
6170 // amount the next segment needs to be
6171 // shifted.
6172 uint8_t mOwner; // owner of the border, defines the
6173 // style
6174 nsTableCellFrame* mFirstCell; // cell at the start of the segment
6175 nsTableCellFrame* mAjaCell; // neighboring cell to the first cell
6176 // where the segment starts, it can be
6177 // the owner of a segment
6178 };
6180 // Iterates over borders (left border, corner, top border) in the cell map within a damage area
6181 // from left to right, top to bottom. All members are in terms of the 1st in flow frames, except
6182 // where suffixed by InFlow.
6183 class BCPaintBorderIterator
6184 {
6185 public:
6188 BCPaintBorderIterator(nsTableFrame* aTable);
6189 ~BCPaintBorderIterator() { if (mVerInfo) {
6190 delete [] mVerInfo;
6191 }}
6192 void Reset();
6194 /**
6195 * Determine the damage area in terms of rows and columns and finalize
6196 * mInitialOffsetX and mInitialOffsetY.
6197 * @param aDirtyRect - dirty rect in table coordinates
6198 * @return - true if we need to paint something given dirty rect
6199 */
6200 bool SetDamageArea(const nsRect& aDamageRect);
6201 void First();
6202 void Next();
6203 void AccumulateOrPaintHorizontalSegment(nsRenderingContext& aRenderingContext);
6204 void AccumulateOrPaintVerticalSegment(nsRenderingContext& aRenderingContext);
6205 void ResetVerInfo();
6206 void StoreColumnWidth(int32_t aIndex);
6207 bool VerticalSegmentOwnsCorner();
6209 nsTableFrame* mTable;
6210 nsTableFrame* mTableFirstInFlow;
6211 nsTableCellMap* mTableCellMap;
6212 nsCellMap* mCellMap;
6213 bool mTableIsLTR;
6214 int32_t mColInc; // +1 for ltr -1 for rtl
6215 const nsStyleBackground* mTableBgColor;
6216 nsTableFrame::RowGroupArray mRowGroups;
6218 nsTableRowGroupFrame* mPrevRg;
6219 nsTableRowGroupFrame* mRg;
6220 bool mIsRepeatedHeader;
6221 bool mIsRepeatedFooter;
6222 nsTableRowGroupFrame* mStartRg; // first row group in the damagearea
6223 int32_t mRgIndex; // current row group index in the
6224 // mRowgroups array
6225 int32_t mFifRgFirstRowIndex; // start row index of the first in
6226 // flow of the row group
6227 int32_t mRgFirstRowIndex; // row index of the first row in the
6228 // row group
6229 int32_t mRgLastRowIndex; // row index of the last row in the row
6230 // group
6231 int32_t mNumTableRows; // number of rows in the table and all
6232 // continuations
6233 int32_t mNumTableCols; // number of columns in the table
6234 int32_t mColIndex; // with respect to the table
6235 int32_t mRowIndex; // with respect to the table
6236 int32_t mRepeatedHeaderRowIndex; // row index in a repeated
6237 //header, it's equivalent to
6238 // mRowIndex when we're in a repeated
6239 // header, and set to the last row
6240 // index of a repeated header when
6241 // we're not
6242 bool mIsNewRow;
6243 bool mAtEnd; // the iterator cycled over all
6244 // borders
6245 nsTableRowFrame* mPrevRow;
6246 nsTableRowFrame* mRow;
6247 nsTableRowFrame* mStartRow; //first row in a inside the damagearea
6250 // cell properties
6251 nsTableCellFrame* mPrevCell;
6252 nsTableCellFrame* mCell;
6253 BCCellData* mPrevCellData;
6254 BCCellData* mCellData;
6255 BCData* mBCData;
6257 bool IsTableTopMost() {return (mRowIndex == 0) && !mTable->GetPrevInFlow();}
6258 bool IsTableRightMost() {return (mColIndex >= mNumTableCols);}
6259 bool IsTableBottomMost() {return (mRowIndex >= mNumTableRows) && !mTable->GetNextInFlow();}
6260 bool IsTableLeftMost() {return (mColIndex == 0);}
6261 bool IsDamageAreaTopMost() {return (mRowIndex == mDamageArea.y);}
6262 bool IsDamageAreaRightMost() {return (mColIndex >= mDamageArea.XMost());}
6263 bool IsDamageAreaBottomMost() {return (mRowIndex >= mDamageArea.YMost());}
6264 bool IsDamageAreaLeftMost() {return (mColIndex == mDamageArea.x);}
6265 int32_t GetRelativeColIndex() {return (mColIndex - mDamageArea.x);}
6267 nsIntRect mDamageArea; // damageArea in cellmap coordinates
6268 bool IsAfterRepeatedHeader() { return !mIsRepeatedHeader && (mRowIndex == (mRepeatedHeaderRowIndex + 1));}
6269 bool StartRepeatedFooter() {return mIsRepeatedFooter && (mRowIndex == mRgFirstRowIndex) && (mRowIndex != mDamageArea.y);}
6270 nscoord mInitialOffsetX; // offsetX of the first border with
6271 // respect to the table
6272 nscoord mInitialOffsetY; // offsetY of the first border with
6273 // respect to the table
6274 nscoord mNextOffsetY; // offsetY of the next segment
6275 BCVerticalSeg* mVerInfo; // this array is used differently when
6276 // horizontal and vertical borders are drawn
6277 // When horizontal border are drawn we cache
6278 // the column widths and the width of the
6279 // vertical borders that arrive from top
6280 // When we draw vertical borders we store
6281 // lengths and width for vertical borders
6282 // before they are drawn while we move over
6283 // the columns in the damage area
6284 // It has one more elements than columns are
6285 //in the table.
6286 BCHorizontalSeg mHorSeg; // the horizontal segment while we
6287 // move over the colums
6288 BCPixelSize mPrevHorSegHeight; // the height of the previous
6289 // horizontal border
6291 private:
6293 bool SetNewRow(nsTableRowFrame* aRow = nullptr);
6294 bool SetNewRowGroup();
6295 void SetNewData(int32_t aRowIndex, int32_t aColIndex);
6297 };
6301 BCPaintBorderIterator::BCPaintBorderIterator(nsTableFrame* aTable)
6302 {
6303 mTable = aTable;
6304 mVerInfo = nullptr;
6305 nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr);
6306 mTableFirstInFlow = static_cast<nsTableFrame*>(mTable->FirstInFlow());
6307 mTableCellMap = mTable->GetCellMap();
6308 // y position of first row in damage area
6309 mInitialOffsetY = mTable->GetPrevInFlow() ? 0 : childAreaOffset.top;
6310 mNumTableRows = mTable->GetRowCount();
6311 mNumTableCols = mTable->GetColCount();
6313 // Get the ordered row groups
6314 mTable->OrderRowGroups(mRowGroups);
6315 // initialize to a non existing index
6316 mRepeatedHeaderRowIndex = -99;
6318 mTableIsLTR = mTable->StyleVisibility()->mDirection ==
6319 NS_STYLE_DIRECTION_LTR;
6320 mColInc = (mTableIsLTR) ? 1 : -1;
6322 nsIFrame* bgFrame =
6323 nsCSSRendering::FindNonTransparentBackgroundFrame(aTable);
6324 mTableBgColor = bgFrame->StyleBackground();
6325 }
6327 bool
6328 BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect)
6329 {
6331 uint32_t startRowIndex, endRowIndex, startColIndex, endColIndex;
6332 startRowIndex = endRowIndex = startColIndex = endColIndex = 0;
6333 bool done = false;
6334 bool haveIntersect = false;
6335 // find startRowIndex, endRowIndex
6336 nscoord rowY = mInitialOffsetY;
6337 for (uint32_t rgX = 0; rgX < mRowGroups.Length() && !done; rgX++) {
6338 nsTableRowGroupFrame* rgFrame = mRowGroups[rgX];
6339 for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame;
6340 rowFrame = rowFrame->GetNextRow()) {
6341 // conservatively estimate the half border widths outside the row
6342 nscoord topBorderHalf = (mTable->GetPrevInFlow()) ? 0 :
6343 nsPresContext::CSSPixelsToAppUnits(rowFrame->GetTopBCBorderWidth() + 1);
6344 nscoord bottomBorderHalf = (mTable->GetNextInFlow()) ? 0 :
6345 nsPresContext::CSSPixelsToAppUnits(rowFrame->GetBottomBCBorderWidth() + 1);
6346 // get the row rect relative to the table rather than the row group
6347 nsSize rowSize = rowFrame->GetSize();
6348 if (haveIntersect) {
6349 if (aDirtyRect.YMost() >= (rowY - topBorderHalf)) {
6350 nsTableRowFrame* fifRow =
6351 static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow());
6352 endRowIndex = fifRow->GetRowIndex();
6353 }
6354 else done = true;
6355 }
6356 else {
6357 if ((rowY + rowSize.height + bottomBorderHalf) >= aDirtyRect.y) {
6358 mStartRg = rgFrame;
6359 mStartRow = rowFrame;
6360 nsTableRowFrame* fifRow =
6361 static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow());
6362 startRowIndex = endRowIndex = fifRow->GetRowIndex();
6363 haveIntersect = true;
6364 }
6365 else {
6366 mInitialOffsetY += rowSize.height;
6367 }
6368 }
6369 rowY += rowSize.height;
6370 }
6371 }
6372 mNextOffsetY = mInitialOffsetY;
6374 // XXX comment refers to the obsolete NS_FRAME_OUTSIDE_CHILDREN flag
6375 // XXX but I don't understand it, so not changing it for now
6376 // outer table borders overflow the table, so the table might be
6377 // target to other areas as the NS_FRAME_OUTSIDE_CHILDREN is set
6378 // on the table
6379 if (!haveIntersect)
6380 return false;
6381 // find startColIndex, endColIndex, startColX
6382 haveIntersect = false;
6383 if (0 == mNumTableCols)
6384 return false;
6385 int32_t leftCol, rightCol; // columns are in the range [leftCol, rightCol)
6387 nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr);
6388 if (mTableIsLTR) {
6389 mInitialOffsetX = childAreaOffset.left; // x position of first col in
6390 // damage area
6391 leftCol = 0;
6392 rightCol = mNumTableCols;
6393 } else {
6394 // x position of first col in damage area
6395 mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right;
6396 leftCol = mNumTableCols-1;
6397 rightCol = -1;
6398 }
6399 nscoord x = 0;
6400 int32_t colX;
6401 for (colX = leftCol; colX != rightCol; colX += mColInc) {
6402 nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(colX);
6403 if (!colFrame) ABORT1(false);
6404 // get the col rect relative to the table rather than the col group
6405 nsSize size = colFrame->GetSize();
6406 if (haveIntersect) {
6407 // conservatively estimate the left half border width outside the col
6408 nscoord leftBorderHalf =
6409 nsPresContext::CSSPixelsToAppUnits(colFrame->GetLeftBorderWidth() + 1);
6410 if (aDirtyRect.XMost() >= (x - leftBorderHalf)) {
6411 endColIndex = colX;
6412 }
6413 else break;
6414 }
6415 else {
6416 // conservatively estimate the right half border width outside the col
6417 nscoord rightBorderHalf =
6418 nsPresContext::CSSPixelsToAppUnits(colFrame->GetRightBorderWidth() + 1);
6419 if ((x + size.width + rightBorderHalf) >= aDirtyRect.x) {
6420 startColIndex = endColIndex = colX;
6421 haveIntersect = true;
6422 }
6423 else {
6424 mInitialOffsetX += mColInc * size.width;
6425 }
6426 }
6427 x += size.width;
6428 }
6429 if (!mTableIsLTR) {
6430 uint32_t temp;
6431 mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right;
6432 temp = startColIndex; startColIndex = endColIndex; endColIndex = temp;
6433 for (uint32_t column = 0; column < startColIndex; column++) {
6434 nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(column);
6435 if (!colFrame) ABORT1(false);
6436 nsSize size = colFrame->GetSize();
6437 mInitialOffsetX += mColInc * size.width;
6438 }
6439 }
6440 if (!haveIntersect)
6441 return false;
6442 mDamageArea = nsIntRect(startColIndex, startRowIndex,
6443 1 + DeprecatedAbs<int32_t>(endColIndex - startColIndex),
6444 1 + endRowIndex - startRowIndex);
6446 Reset();
6447 mVerInfo = new BCVerticalSeg[mDamageArea.width + 1];
6448 if (!mVerInfo)
6449 return false;
6450 return true;
6451 }
6453 void
6454 BCPaintBorderIterator::Reset()
6455 {
6456 mAtEnd = true; // gets reset when First() is called
6457 mRg = mStartRg;
6458 mPrevRow = nullptr;
6459 mRow = mStartRow;
6460 mRowIndex = 0;
6461 mColIndex = 0;
6462 mRgIndex = -1;
6463 mPrevCell = nullptr;
6464 mCell = nullptr;
6465 mPrevCellData = nullptr;
6466 mCellData = nullptr;
6467 mBCData = nullptr;
6468 ResetVerInfo();
6469 }
6471 /**
6472 * Set the iterator data to a new cellmap coordinate
6473 * @param aRowIndex - the row index
6474 * @param aColIndex - the col index
6475 */
6476 void
6477 BCPaintBorderIterator::SetNewData(int32_t aY,
6478 int32_t aX)
6479 {
6480 if (!mTableCellMap || !mTableCellMap->mBCInfo) ABORT0();
6482 mColIndex = aX;
6483 mRowIndex = aY;
6484 mPrevCellData = mCellData;
6485 if (IsTableRightMost() && IsTableBottomMost()) {
6486 mCell = nullptr;
6487 mBCData = &mTableCellMap->mBCInfo->mLowerRightCorner;
6488 }
6489 else if (IsTableRightMost()) {
6490 mCellData = nullptr;
6491 mBCData = &mTableCellMap->mBCInfo->mRightBorders.ElementAt(aY);
6492 }
6493 else if (IsTableBottomMost()) {
6494 mCellData = nullptr;
6495 mBCData = &mTableCellMap->mBCInfo->mBottomBorders.ElementAt(aX);
6496 }
6497 else {
6498 if (uint32_t(mRowIndex - mFifRgFirstRowIndex) < mCellMap->mRows.Length()) {
6499 mBCData = nullptr;
6500 mCellData =
6501 (BCCellData*)mCellMap->mRows[mRowIndex - mFifRgFirstRowIndex].SafeElementAt(mColIndex);
6502 if (mCellData) {
6503 mBCData = &mCellData->mData;
6504 if (!mCellData->IsOrig()) {
6505 if (mCellData->IsRowSpan()) {
6506 aY -= mCellData->GetRowSpanOffset();
6507 }
6508 if (mCellData->IsColSpan()) {
6509 aX -= mCellData->GetColSpanOffset();
6510 }
6511 if ((aX >= 0) && (aY >= 0)) {
6512 mCellData = (BCCellData*)mCellMap->mRows[aY - mFifRgFirstRowIndex][aX];
6513 }
6514 }
6515 if (mCellData->IsOrig()) {
6516 mPrevCell = mCell;
6517 mCell = mCellData->GetCellFrame();
6518 }
6519 }
6520 }
6521 }
6522 }
6524 /**
6525 * Set the iterator to a new row
6526 * @param aRow - the new row frame, if null the iterator will advance to the
6527 * next row
6528 */
6529 bool
6530 BCPaintBorderIterator::SetNewRow(nsTableRowFrame* aRow)
6531 {
6532 mPrevRow = mRow;
6533 mRow = (aRow) ? aRow : mRow->GetNextRow();
6534 if (mRow) {
6535 mIsNewRow = true;
6536 mRowIndex = mRow->GetRowIndex();
6537 mColIndex = mDamageArea.x;
6538 mPrevHorSegHeight = 0;
6539 if (mIsRepeatedHeader) {
6540 mRepeatedHeaderRowIndex = mRowIndex;
6541 }
6542 }
6543 else {
6544 mAtEnd = true;
6545 }
6546 return !mAtEnd;
6547 }
6549 /**
6550 * Advance the iterator to the next row group
6551 */
6552 bool
6553 BCPaintBorderIterator::SetNewRowGroup()
6554 {
6556 mRgIndex++;
6558 mIsRepeatedHeader = false;
6559 mIsRepeatedFooter = false;
6561 NS_ASSERTION(mRgIndex >= 0, "mRgIndex out of bounds");
6562 if (uint32_t(mRgIndex) < mRowGroups.Length()) {
6563 mPrevRg = mRg;
6564 mRg = mRowGroups[mRgIndex];
6565 nsTableRowGroupFrame* fifRg =
6566 static_cast<nsTableRowGroupFrame*>(mRg->FirstInFlow());
6567 mFifRgFirstRowIndex = fifRg->GetStartRowIndex();
6568 mRgFirstRowIndex = mRg->GetStartRowIndex();
6569 mRgLastRowIndex = mRgFirstRowIndex + mRg->GetRowCount() - 1;
6571 if (SetNewRow(mRg->GetFirstRow())) {
6572 mCellMap = mTableCellMap->GetMapFor(fifRg, nullptr);
6573 if (!mCellMap) ABORT1(false);
6574 }
6575 if (mRg && mTable->GetPrevInFlow() && !mRg->GetPrevInFlow()) {
6576 // if mRowGroup doesn't have a prev in flow, then it may be a repeated
6577 // header or footer
6578 const nsStyleDisplay* display = mRg->StyleDisplay();
6579 if (mRowIndex == mDamageArea.y) {
6580 mIsRepeatedHeader = (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay);
6581 }
6582 else {
6583 mIsRepeatedFooter = (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == display->mDisplay);
6584 }
6585 }
6586 }
6587 else {
6588 mAtEnd = true;
6589 }
6590 return !mAtEnd;
6591 }
6593 /**
6594 * Move the iterator to the first position in the damageArea
6595 */
6596 void
6597 BCPaintBorderIterator::First()
6598 {
6599 if (!mTable || (mDamageArea.x >= mNumTableCols) ||
6600 (mDamageArea.y >= mNumTableRows)) ABORT0();
6602 mAtEnd = false;
6604 uint32_t numRowGroups = mRowGroups.Length();
6605 for (uint32_t rgY = 0; rgY < numRowGroups; rgY++) {
6606 nsTableRowGroupFrame* rowG = mRowGroups[rgY];
6607 int32_t start = rowG->GetStartRowIndex();
6608 int32_t end = start + rowG->GetRowCount() - 1;
6609 if ((mDamageArea.y >= start) && (mDamageArea.y <= end)) {
6610 mRgIndex = rgY - 1; // SetNewRowGroup increments rowGroupIndex
6611 if (SetNewRowGroup()) {
6612 while ((mRowIndex < mDamageArea.y) && !mAtEnd) {
6613 SetNewRow();
6614 }
6615 if (!mAtEnd) {
6616 SetNewData(mDamageArea.y, mDamageArea.x);
6617 }
6618 }
6619 return;
6620 }
6621 }
6622 mAtEnd = true;
6623 }
6625 /**
6626 * Advance the iterator to the next position
6627 */
6628 void
6629 BCPaintBorderIterator::Next()
6630 {
6631 if (mAtEnd) ABORT0();
6632 mIsNewRow = false;
6634 mColIndex++;
6635 if (mColIndex > mDamageArea.XMost()) {
6636 mRowIndex++;
6637 if (mRowIndex == mDamageArea.YMost()) {
6638 mColIndex = mDamageArea.x;
6639 }
6640 else if (mRowIndex < mDamageArea.YMost()) {
6641 if (mRowIndex <= mRgLastRowIndex) {
6642 SetNewRow();
6643 }
6644 else {
6645 SetNewRowGroup();
6646 }
6647 }
6648 else {
6649 mAtEnd = true;
6650 }
6651 }
6652 if (!mAtEnd) {
6653 SetNewData(mRowIndex, mColIndex);
6654 }
6655 }
6657 // XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine
6658 // them
6659 /** Compute the vertical offset of a vertical border segment
6660 * @param aCornerOwnerSide - which side owns the corner
6661 * @param aCornerSubWidth - how wide is the nonwinning side of the corner
6662 * @param aHorWidth - how wide is the horizontal edge of the corner
6663 * @param aIsStartOfSeg - does this corner start a new segment
6664 * @param aIsBevel - is this corner beveled
6665 * @return - offset in twips
6666 */
6667 static nscoord
6668 CalcVerCornerOffset(mozilla::css::Side aCornerOwnerSide,
6669 BCPixelSize aCornerSubWidth,
6670 BCPixelSize aHorWidth,
6671 bool aIsStartOfSeg,
6672 bool aIsBevel)
6673 {
6674 nscoord offset = 0;
6675 // XXX These should be replaced with appropriate side-specific macros (which?)
6676 BCPixelSize smallHalf, largeHalf;
6677 if ((NS_SIDE_TOP == aCornerOwnerSide) ||
6678 (NS_SIDE_BOTTOM == aCornerOwnerSide)) {
6679 DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
6680 if (aIsBevel) {
6681 offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
6682 }
6683 else {
6684 offset = (NS_SIDE_TOP == aCornerOwnerSide) ? smallHalf : -largeHalf;
6685 }
6686 }
6687 else {
6688 DivideBCBorderSize(aHorWidth, smallHalf, largeHalf);
6689 if (aIsBevel) {
6690 offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
6691 }
6692 else {
6693 offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
6694 }
6695 }
6696 return nsPresContext::CSSPixelsToAppUnits(offset);
6697 }
6699 /** Compute the horizontal offset of a horizontal border segment
6700 * @param aCornerOwnerSide - which side owns the corner
6701 * @param aCornerSubWidth - how wide is the nonwinning side of the corner
6702 * @param aVerWidth - how wide is the vertical edge of the corner
6703 * @param aIsStartOfSeg - does this corner start a new segment
6704 * @param aIsBevel - is this corner beveled
6705 * @param aTableIsLTR - direction, the computation depends on ltr or rtl
6706 * @return - offset in twips
6707 */
6708 static nscoord
6709 CalcHorCornerOffset(mozilla::css::Side aCornerOwnerSide,
6710 BCPixelSize aCornerSubWidth,
6711 BCPixelSize aVerWidth,
6712 bool aIsStartOfSeg,
6713 bool aIsBevel,
6714 bool aTableIsLTR)
6715 {
6716 nscoord offset = 0;
6717 // XXX These should be replaced with appropriate side-specific macros (which?)
6718 BCPixelSize smallHalf, largeHalf;
6719 if ((NS_SIDE_LEFT == aCornerOwnerSide) ||
6720 (NS_SIDE_RIGHT == aCornerOwnerSide)) {
6721 if (aTableIsLTR) {
6722 DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
6723 }
6724 else {
6725 DivideBCBorderSize(aCornerSubWidth, largeHalf, smallHalf);
6726 }
6727 if (aIsBevel) {
6728 offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
6729 }
6730 else {
6731 offset = (NS_SIDE_LEFT == aCornerOwnerSide) ? smallHalf : -largeHalf;
6732 }
6733 }
6734 else {
6735 if (aTableIsLTR) {
6736 DivideBCBorderSize(aVerWidth, smallHalf, largeHalf);
6737 }
6738 else {
6739 DivideBCBorderSize(aVerWidth, largeHalf, smallHalf);
6740 }
6741 if (aIsBevel) {
6742 offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
6743 }
6744 else {
6745 offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
6746 }
6747 }
6748 return nsPresContext::CSSPixelsToAppUnits(offset);
6749 }
6751 BCVerticalSeg::BCVerticalSeg()
6752 {
6753 mCol = nullptr;
6754 mFirstCell = mLastCell = mAjaCell = nullptr;
6755 mOffsetX = mOffsetY = mLength = mWidth = mTopBevelOffset = 0;
6756 mTopBevelSide = NS_SIDE_TOP;
6757 mOwner = eCellOwner;
6758 }
6760 /**
6761 * Start a new vertical segment
6762 * @param aIter - iterator containing the structural information
6763 * @param aBorderOwner - determines the border style
6764 * @param aVerSegWidth - the width of segment in pixel
6765 * @param aHorSegHeight - the width of the horizontal segment joining the corner
6766 * at the start
6767 */
6768 void
6769 BCVerticalSeg::Start(BCPaintBorderIterator& aIter,
6770 BCBorderOwner aBorderOwner,
6771 BCPixelSize aVerSegWidth,
6772 BCPixelSize aHorSegHeight)
6773 {
6774 mozilla::css::Side ownerSide = NS_SIDE_TOP;
6775 bool bevel = false;
6778 nscoord cornerSubWidth = (aIter.mBCData) ?
6779 aIter.mBCData->GetCorner(ownerSide, bevel) : 0;
6781 bool topBevel = (aVerSegWidth > 0) ? bevel : false;
6782 BCPixelSize maxHorSegHeight = std::max(aIter.mPrevHorSegHeight, aHorSegHeight);
6783 nscoord offset = CalcVerCornerOffset(ownerSide, cornerSubWidth,
6784 maxHorSegHeight, true,
6785 topBevel);
6787 mTopBevelOffset = topBevel ?
6788 nsPresContext::CSSPixelsToAppUnits(maxHorSegHeight): 0;
6789 // XXX this assumes that only corners where 2 segments join can be beveled
6790 mTopBevelSide = (aHorSegHeight > 0) ? NS_SIDE_RIGHT : NS_SIDE_LEFT;
6791 mOffsetY += offset;
6792 mLength = -offset;
6793 mWidth = aVerSegWidth;
6794 mOwner = aBorderOwner;
6795 mFirstCell = aIter.mCell;
6796 mFirstRowGroup = aIter.mRg;
6797 mFirstRow = aIter.mRow;
6798 if (aIter.GetRelativeColIndex() > 0) {
6799 mAjaCell = aIter.mVerInfo[aIter.GetRelativeColIndex() - 1].mLastCell;
6800 }
6801 }
6803 /**
6804 * Initialize the vertical segments with information that will persist for any
6805 * vertical segment in this column
6806 * @param aIter - iterator containing the structural information
6807 */
6808 void
6809 BCVerticalSeg::Initialize(BCPaintBorderIterator& aIter)
6810 {
6811 int32_t relColIndex = aIter.GetRelativeColIndex();
6812 mCol = aIter.IsTableRightMost() ? aIter.mVerInfo[relColIndex - 1].mCol :
6813 aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex);
6814 if (!mCol) ABORT0();
6815 if (0 == relColIndex) {
6816 mOffsetX = aIter.mInitialOffsetX;
6817 }
6818 // set colX for the next column
6819 if (!aIter.IsDamageAreaRightMost()) {
6820 aIter.mVerInfo[relColIndex + 1].mOffsetX = mOffsetX +
6821 aIter.mColInc * mCol->GetSize().width;
6822 }
6823 mOffsetY = aIter.mInitialOffsetY;
6824 mLastCell = aIter.mCell;
6825 }
6827 /**
6828 * Compute the offsets for the bottom corner of a vertical segment
6829 * @param aIter - iterator containing the structural information
6830 * @param aHorSegHeight - the width of the horizontal segment joining the corner
6831 * at the start
6832 */
6833 void
6834 BCVerticalSeg::GetBottomCorner(BCPaintBorderIterator& aIter,
6835 BCPixelSize aHorSegHeight)
6836 {
6837 mozilla::css::Side ownerSide = NS_SIDE_TOP;
6838 nscoord cornerSubWidth = 0;
6839 bool bevel = false;
6840 if (aIter.mBCData) {
6841 cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel);
6842 }
6843 mIsBottomBevel = (mWidth > 0) ? bevel : false;
6844 mBottomHorSegHeight = std::max(aIter.mPrevHorSegHeight, aHorSegHeight);
6845 mBottomOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth,
6846 mBottomHorSegHeight,
6847 false, mIsBottomBevel);
6848 mLength += mBottomOffset;
6849 }
6851 /**
6852 * Paint the vertical segment
6853 * @param aIter - iterator containing the structural information
6854 * @param aRenderingContext - the rendering context
6855 * @param aHorSegHeight - the width of the horizontal segment joining the corner
6856 * at the start
6857 */
6858 void
6859 BCVerticalSeg::Paint(BCPaintBorderIterator& aIter,
6860 nsRenderingContext& aRenderingContext,
6861 BCPixelSize aHorSegHeight)
6862 {
6863 // get the border style, color and paint the segment
6864 mozilla::css::Side side = (aIter.IsDamageAreaRightMost()) ? NS_SIDE_RIGHT :
6865 NS_SIDE_LEFT;
6866 int32_t relColIndex = aIter.GetRelativeColIndex();
6867 nsTableColFrame* col = mCol; if (!col) ABORT0();
6868 nsTableCellFrame* cell = mFirstCell; // ???
6869 nsIFrame* owner = nullptr;
6870 uint8_t style = NS_STYLE_BORDER_STYLE_SOLID;
6871 nscolor color = 0xFFFFFFFF;
6873 switch (mOwner) {
6874 case eTableOwner:
6875 owner = aIter.mTable;
6876 break;
6877 case eAjaColGroupOwner:
6878 side = NS_SIDE_RIGHT;
6879 if (!aIter.IsTableRightMost() && (relColIndex > 0)) {
6880 col = aIter.mVerInfo[relColIndex - 1].mCol;
6881 } // and fall through
6882 case eColGroupOwner:
6883 if (col) {
6884 owner = col->GetParent();
6885 }
6886 break;
6887 case eAjaColOwner:
6888 side = NS_SIDE_RIGHT;
6889 if (!aIter.IsTableRightMost() && (relColIndex > 0)) {
6890 col = aIter.mVerInfo[relColIndex - 1].mCol;
6891 } // and fall through
6892 case eColOwner:
6893 owner = col;
6894 break;
6895 case eAjaRowGroupOwner:
6896 NS_ERROR("a neighboring rowgroup can never own a vertical border");
6897 // and fall through
6898 case eRowGroupOwner:
6899 NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(),
6900 "row group can own border only at table edge");
6901 owner = mFirstRowGroup;
6902 break;
6903 case eAjaRowOwner:
6904 NS_ASSERTION(false, "program error"); // and fall through
6905 case eRowOwner:
6906 NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(),
6907 "row can own border only at table edge");
6908 owner = mFirstRow;
6909 break;
6910 case eAjaCellOwner:
6911 side = NS_SIDE_RIGHT;
6912 cell = mAjaCell; // and fall through
6913 case eCellOwner:
6914 owner = cell;
6915 break;
6916 }
6917 if (owner) {
6918 ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR);
6919 }
6920 BCPixelSize smallHalf, largeHalf;
6921 DivideBCBorderSize(mWidth, smallHalf, largeHalf);
6922 nsRect segRect(mOffsetX - nsPresContext::CSSPixelsToAppUnits(largeHalf),
6923 mOffsetY,
6924 nsPresContext::CSSPixelsToAppUnits(mWidth), mLength);
6925 nscoord bottomBevelOffset = (mIsBottomBevel) ?
6926 nsPresContext::CSSPixelsToAppUnits(mBottomHorSegHeight) : 0;
6927 mozilla::css::Side bottomBevelSide = ((aHorSegHeight > 0) ^ !aIter.mTableIsLTR) ?
6928 NS_SIDE_RIGHT : NS_SIDE_LEFT;
6929 mozilla::css::Side topBevelSide = ((mTopBevelSide == NS_SIDE_RIGHT) ^ !aIter.mTableIsLTR)?
6930 NS_SIDE_RIGHT : NS_SIDE_LEFT;
6931 nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color,
6932 aIter.mTableBgColor, segRect,
6933 nsPresContext::AppUnitsPerCSSPixel(),
6934 topBevelSide, mTopBevelOffset,
6935 bottomBevelSide, bottomBevelOffset);
6936 }
6938 /**
6939 * Advance the start point of a segment
6940 */
6941 void
6942 BCVerticalSeg::AdvanceOffsetY()
6943 {
6944 mOffsetY += mLength - mBottomOffset;
6945 }
6947 /**
6948 * Accumulate the current segment
6949 */
6950 void
6951 BCVerticalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter)
6952 {
6953 mLastCell = aIter.mCell;
6954 mLength += aIter.mRow->GetRect().height;
6955 }
6957 BCHorizontalSeg::BCHorizontalSeg()
6958 {
6959 mOffsetX = mOffsetY = mLength = mWidth = mLeftBevelOffset = 0;
6960 mLeftBevelSide = NS_SIDE_TOP;
6961 mFirstCell = mAjaCell = nullptr;
6962 }
6964 /** Initialize a horizontal border segment for painting
6965 * @param aIter - iterator storing the current and adjacent frames
6966 * @param aBorderOwner - which frame owns the border
6967 * @param aBottomVerSegWidth - vertical segment width coming from up
6968 * @param aHorSegHeight - the height of the segment
6969 + */
6970 void
6971 BCHorizontalSeg::Start(BCPaintBorderIterator& aIter,
6972 BCBorderOwner aBorderOwner,
6973 BCPixelSize aBottomVerSegWidth,
6974 BCPixelSize aHorSegHeight)
6975 {
6976 mozilla::css::Side cornerOwnerSide = NS_SIDE_TOP;
6977 bool bevel = false;
6979 mOwner = aBorderOwner;
6980 nscoord cornerSubWidth = (aIter.mBCData) ?
6981 aIter.mBCData->GetCorner(cornerOwnerSide,
6982 bevel) : 0;
6984 bool leftBevel = (aHorSegHeight > 0) ? bevel : false;
6985 int32_t relColIndex = aIter.GetRelativeColIndex();
6986 nscoord maxVerSegWidth = std::max(aIter.mVerInfo[relColIndex].mWidth,
6987 aBottomVerSegWidth);
6988 nscoord offset = CalcHorCornerOffset(cornerOwnerSide, cornerSubWidth,
6989 maxVerSegWidth, true, leftBevel,
6990 aIter.mTableIsLTR);
6991 mLeftBevelOffset = (leftBevel && (aHorSegHeight > 0)) ? maxVerSegWidth : 0;
6992 // XXX this assumes that only corners where 2 segments join can be beveled
6993 mLeftBevelSide = (aBottomVerSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP;
6994 if (aIter.mTableIsLTR) {
6995 mOffsetX += offset;
6996 }
6997 else {
6998 mOffsetX -= offset;
6999 }
7000 mLength = -offset;
7001 mWidth = aHorSegHeight;
7002 mFirstCell = aIter.mCell;
7003 mAjaCell = (aIter.IsDamageAreaTopMost()) ? nullptr :
7004 aIter.mVerInfo[relColIndex].mLastCell;
7005 }
7007 /**
7008 * Compute the offsets for the right corner of a horizontal segment
7009 * @param aIter - iterator containing the structural information
7010 * @param aLeftSegWidth - the width of the vertical segment joining the corner
7011 * at the start
7012 */
7013 void
7014 BCHorizontalSeg::GetRightCorner(BCPaintBorderIterator& aIter,
7015 BCPixelSize aLeftSegWidth)
7016 {
7017 mozilla::css::Side ownerSide = NS_SIDE_TOP;
7018 nscoord cornerSubWidth = 0;
7019 bool bevel = false;
7020 if (aIter.mBCData) {
7021 cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel);
7022 }
7024 mIsRightBevel = (mWidth > 0) ? bevel : 0;
7025 int32_t relColIndex = aIter.GetRelativeColIndex();
7026 nscoord verWidth = std::max(aIter.mVerInfo[relColIndex].mWidth, aLeftSegWidth);
7027 mEndOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth,
7028 false, mIsRightBevel, aIter.mTableIsLTR);
7029 mLength += mEndOffset;
7030 mRightBevelOffset = (mIsRightBevel) ?
7031 nsPresContext::CSSPixelsToAppUnits(verWidth) : 0;
7032 mRightBevelSide = (aLeftSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP;
7033 }
7035 /**
7036 * Paint the horizontal segment
7037 * @param aIter - iterator containing the structural information
7038 * @param aRenderingContext - the rendering context
7039 */
7040 void
7041 BCHorizontalSeg::Paint(BCPaintBorderIterator& aIter,
7042 nsRenderingContext& aRenderingContext)
7043 {
7044 // get the border style, color and paint the segment
7045 mozilla::css::Side side = (aIter.IsDamageAreaBottomMost()) ? NS_SIDE_BOTTOM :
7046 NS_SIDE_TOP;
7047 nsIFrame* rg = aIter.mRg; if (!rg) ABORT0();
7048 nsIFrame* row = aIter.mRow; if (!row) ABORT0();
7049 nsIFrame* cell = mFirstCell;
7050 nsIFrame* col;
7051 nsIFrame* owner = nullptr;
7053 uint8_t style = NS_STYLE_BORDER_STYLE_SOLID;
7054 nscolor color = 0xFFFFFFFF;
7057 switch (mOwner) {
7058 case eTableOwner:
7059 owner = aIter.mTable;
7060 break;
7061 case eAjaColGroupOwner:
7062 NS_ERROR("neighboring colgroups can never own a horizontal border");
7063 // and fall through
7064 case eColGroupOwner:
7065 NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(),
7066 "col group can own border only at the table edge");
7067 col = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1);
7068 if (!col) ABORT0();
7069 owner = col->GetParent();
7070 break;
7071 case eAjaColOwner:
7072 NS_ERROR("neighboring column can never own a horizontal border");
7073 // and fall through
7074 case eColOwner:
7075 NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(),
7076 "col can own border only at the table edge");
7077 owner = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1);
7078 break;
7079 case eAjaRowGroupOwner:
7080 side = NS_SIDE_BOTTOM;
7081 rg = (aIter.IsTableBottomMost()) ? aIter.mRg : aIter.mPrevRg;
7082 // and fall through
7083 case eRowGroupOwner:
7084 owner = rg;
7085 break;
7086 case eAjaRowOwner:
7087 side = NS_SIDE_BOTTOM;
7088 row = (aIter.IsTableBottomMost()) ? aIter.mRow : aIter.mPrevRow;
7089 // and fall through
7090 case eRowOwner:
7091 owner = row;
7092 break;
7093 case eAjaCellOwner:
7094 side = NS_SIDE_BOTTOM;
7095 // if this is null due to the damage area origin-y > 0, then the border
7096 // won't show up anyway
7097 cell = mAjaCell;
7098 // and fall through
7099 case eCellOwner:
7100 owner = cell;
7101 break;
7102 }
7103 if (owner) {
7104 ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR);
7105 }
7106 BCPixelSize smallHalf, largeHalf;
7107 DivideBCBorderSize(mWidth, smallHalf, largeHalf);
7108 nsRect segRect(mOffsetX,
7109 mOffsetY - nsPresContext::CSSPixelsToAppUnits(largeHalf),
7110 mLength,
7111 nsPresContext::CSSPixelsToAppUnits(mWidth));
7112 if (aIter.mTableIsLTR) {
7113 nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color,
7114 aIter.mTableBgColor, segRect,
7115 nsPresContext::AppUnitsPerCSSPixel(),
7116 mLeftBevelSide,
7117 nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset),
7118 mRightBevelSide, mRightBevelOffset);
7119 }
7120 else {
7121 segRect.x -= segRect.width;
7122 nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color,
7123 aIter.mTableBgColor, segRect,
7124 nsPresContext::AppUnitsPerCSSPixel(),
7125 mRightBevelSide, mRightBevelOffset,
7126 mLeftBevelSide,
7127 nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset));
7128 }
7129 }
7131 /**
7132 * Advance the start point of a segment
7133 */
7134 void
7135 BCHorizontalSeg::AdvanceOffsetX(int32_t aIncrement)
7136 {
7137 mOffsetX += aIncrement * (mLength - mEndOffset);
7138 }
7140 /**
7141 * Accumulate the current segment
7142 */
7143 void
7144 BCHorizontalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter)
7145 {
7146 mLength += aIter.mVerInfo[aIter.GetRelativeColIndex()].mColWidth;
7147 }
7149 /**
7150 * store the column width information while painting horizontal segment
7151 */
7152 void
7153 BCPaintBorderIterator::StoreColumnWidth(int32_t aIndex)
7154 {
7155 if (IsTableRightMost()) {
7156 mVerInfo[aIndex].mColWidth = mVerInfo[aIndex - 1].mColWidth;
7157 }
7158 else {
7159 nsTableColFrame* col = mTableFirstInFlow->GetColFrame(mColIndex);
7160 if (!col) ABORT0();
7161 mVerInfo[aIndex].mColWidth = col->GetSize().width;
7162 }
7163 }
7164 /**
7165 * Determine if a vertical segment owns the corder
7166 */
7167 bool
7168 BCPaintBorderIterator::VerticalSegmentOwnsCorner()
7169 {
7170 mozilla::css::Side cornerOwnerSide = NS_SIDE_TOP;
7171 bool bevel = false;
7172 if (mBCData) {
7173 mBCData->GetCorner(cornerOwnerSide, bevel);
7174 }
7175 // unitialized ownerside, bevel
7176 return (NS_SIDE_TOP == cornerOwnerSide) ||
7177 (NS_SIDE_BOTTOM == cornerOwnerSide);
7178 }
7180 /**
7181 * Paint if necessary a horizontal segment, otherwise accumulate it
7182 * @param aRenderingContext - the rendering context
7183 */
7184 void
7185 BCPaintBorderIterator::AccumulateOrPaintHorizontalSegment(nsRenderingContext& aRenderingContext)
7186 {
7188 int32_t relColIndex = GetRelativeColIndex();
7189 // store the current col width if it hasn't been already
7190 if (mVerInfo[relColIndex].mColWidth < 0) {
7191 StoreColumnWidth(relColIndex);
7192 }
7194 BCBorderOwner borderOwner = eCellOwner;
7195 BCBorderOwner ignoreBorderOwner;
7196 bool isSegStart = true;
7197 bool ignoreSegStart;
7199 nscoord leftSegWidth =
7200 mBCData ? mBCData->GetLeftEdge(ignoreBorderOwner, ignoreSegStart) : 0;
7201 nscoord topSegHeight =
7202 mBCData ? mBCData->GetTopEdge(borderOwner, isSegStart) : 0;
7204 if (mIsNewRow || (IsDamageAreaLeftMost() && IsDamageAreaBottomMost())) {
7205 // reset for every new row and on the bottom of the last row
7206 mHorSeg.mOffsetY = mNextOffsetY;
7207 mNextOffsetY = mNextOffsetY + mRow->GetSize().height;
7208 mHorSeg.mOffsetX = mInitialOffsetX;
7209 mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight);
7210 }
7212 if (!IsDamageAreaLeftMost() && (isSegStart || IsDamageAreaRightMost() ||
7213 VerticalSegmentOwnsCorner())) {
7214 // paint the previous seg or the current one if IsDamageAreaRightMost()
7215 if (mHorSeg.mLength > 0) {
7216 mHorSeg.GetRightCorner(*this, leftSegWidth);
7217 if (mHorSeg.mWidth > 0) {
7218 mHorSeg.Paint(*this, aRenderingContext);
7219 }
7220 mHorSeg.AdvanceOffsetX(mColInc);
7221 }
7222 mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight);
7223 }
7224 mHorSeg.IncludeCurrentBorder(*this);
7225 mVerInfo[relColIndex].mWidth = leftSegWidth;
7226 mVerInfo[relColIndex].mLastCell = mCell;
7227 }
7228 /**
7229 * Paint if necessary a vertical segment, otherwise it
7230 * @param aRenderingContext - the rendering context
7231 */
7232 void
7233 BCPaintBorderIterator::AccumulateOrPaintVerticalSegment(nsRenderingContext& aRenderingContext)
7234 {
7235 BCBorderOwner borderOwner = eCellOwner;
7236 BCBorderOwner ignoreBorderOwner;
7237 bool isSegStart = true;
7238 bool ignoreSegStart;
7240 nscoord verSegWidth =
7241 mBCData ? mBCData->GetLeftEdge(borderOwner, isSegStart) : 0;
7242 nscoord horSegHeight =
7243 mBCData ? mBCData->GetTopEdge(ignoreBorderOwner, ignoreSegStart) : 0;
7245 int32_t relColIndex = GetRelativeColIndex();
7246 BCVerticalSeg& verSeg = mVerInfo[relColIndex];
7247 if (!verSeg.mCol) { // on the first damaged row and the first segment in the
7248 // col
7249 verSeg.Initialize(*this);
7250 verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight);
7251 }
7253 if (!IsDamageAreaTopMost() && (isSegStart || IsDamageAreaBottomMost() ||
7254 IsAfterRepeatedHeader() ||
7255 StartRepeatedFooter())) {
7256 // paint the previous seg or the current one if IsDamageAreaBottomMost()
7257 if (verSeg.mLength > 0) {
7258 verSeg.GetBottomCorner(*this, horSegHeight);
7259 if (verSeg.mWidth > 0) {
7260 verSeg.Paint(*this, aRenderingContext, horSegHeight);
7261 }
7262 verSeg.AdvanceOffsetY();
7263 }
7264 verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight);
7265 }
7266 verSeg.IncludeCurrentBorder(*this);
7267 mPrevHorSegHeight = horSegHeight;
7268 }
7270 /**
7271 * Reset the vertical information cache
7272 */
7273 void
7274 BCPaintBorderIterator::ResetVerInfo()
7275 {
7276 if (mVerInfo) {
7277 memset(mVerInfo, 0, mDamageArea.width * sizeof(BCVerticalSeg));
7278 // XXX reinitialize properly
7279 for (int32_t xIndex = 0; xIndex < mDamageArea.width; xIndex++) {
7280 mVerInfo[xIndex].mColWidth = -1;
7281 }
7282 }
7283 }
7285 /**
7286 * Method to paint BCBorders, this does not use currently display lists although
7287 * it will do this in future
7288 * @param aRenderingContext - the rendering context
7289 * @param aDirtyRect - inside this rectangle the BC Borders will redrawn
7290 */
7291 void
7292 nsTableFrame::PaintBCBorders(nsRenderingContext& aRenderingContext,
7293 const nsRect& aDirtyRect)
7294 {
7295 // We first transfer the aDirtyRect into cellmap coordinates to compute which
7296 // cell borders need to be painted
7297 BCPaintBorderIterator iter(this);
7298 if (!iter.SetDamageArea(aDirtyRect))
7299 return;
7301 // First, paint all of the vertical borders from top to bottom and left to
7302 // right as they become complete. They are painted first, since they are less
7303 // efficient to paint than horizontal segments. They were stored with as few
7304 // segments as possible (since horizontal borders are painted last and
7305 // possibly over them). For every cell in a row that fails in the damage are
7306 // we look up if the current border would start a new segment, if so we paint
7307 // the previously stored vertical segment and start a new segment. After
7308 // this we the now active segment with the current border. These
7309 // segments are stored in mVerInfo to be used on the next row
7310 for (iter.First(); !iter.mAtEnd; iter.Next()) {
7311 iter.AccumulateOrPaintVerticalSegment(aRenderingContext);
7312 }
7314 // Next, paint all of the horizontal border segments from top to bottom reuse
7315 // the mVerInfo array to keep track of col widths and vertical segments for
7316 // corner calculations
7317 iter.Reset();
7318 for (iter.First(); !iter.mAtEnd; iter.Next()) {
7319 iter.AccumulateOrPaintHorizontalSegment(aRenderingContext);
7320 }
7321 }
7323 bool nsTableFrame::RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols)
7324 {
7325 bool result = false;
7326 nsTableCellMap* cellMap = GetCellMap();
7327 NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
7328 if (cellMap) {
7329 result = cellMap->RowHasSpanningCells(aRowIndex, aNumEffCols);
7330 }
7331 return result;
7332 }
7334 bool nsTableFrame::RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols)
7335 {
7336 bool result = false;
7337 nsTableCellMap* cellMap = GetCellMap();
7338 NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
7339 if (cellMap) {
7340 result = cellMap->RowIsSpannedInto(aRowIndex, aNumEffCols);
7341 }
7342 return result;
7343 }
7345 /* static */
7346 void
7347 nsTableFrame::InvalidateTableFrame(nsIFrame* aFrame,
7348 const nsRect& aOrigRect,
7349 const nsRect& aOrigVisualOverflow,
7350 bool aIsFirstReflow)
7351 {
7352 nsIFrame* parent = aFrame->GetParent();
7353 NS_ASSERTION(parent, "What happened here?");
7355 if (parent->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
7356 // Don't bother; we'll invalidate the parent's overflow rect when
7357 // we finish reflowing it.
7358 return;
7359 }
7361 // The part that looks at both the rect and the overflow rect is a
7362 // bit of a hack. See nsBlockFrame::ReflowLine for an eloquent
7363 // description of its hackishness.
7364 //
7365 // This doesn't really make sense now that we have DLBI.
7366 // This code can probably be simplified a fair bit.
7367 nsRect visualOverflow = aFrame->GetVisualOverflowRect();
7368 if (aIsFirstReflow ||
7369 aOrigRect.TopLeft() != aFrame->GetPosition() ||
7370 aOrigVisualOverflow.TopLeft() != visualOverflow.TopLeft()) {
7371 // Invalidate the old and new overflow rects. Note that if the
7372 // frame moved, we can't just use aOrigVisualOverflow, since it's in
7373 // coordinates relative to the old position. So invalidate via
7374 // aFrame's parent, and reposition that overflow rect to the right
7375 // place.
7376 // XXXbz this doesn't handle outlines, does it?
7377 aFrame->InvalidateFrame();
7378 parent->InvalidateFrameWithRect(aOrigVisualOverflow + aOrigRect.TopLeft());
7379 } else if (aOrigRect.Size() != aFrame->GetSize() ||
7380 aOrigVisualOverflow.Size() != visualOverflow.Size()){
7381 aFrame->InvalidateFrameWithRect(aOrigVisualOverflow);
7382 aFrame->InvalidateFrame();
7383 parent->InvalidateFrameWithRect(aOrigRect);;
7384 parent->InvalidateFrame();
7385 }
7386 }