1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/tables/nsTableRowGroupFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1931 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 +#include "nsCOMPtr.h" 1.9 +#include "nsTableRowGroupFrame.h" 1.10 +#include "nsTableRowFrame.h" 1.11 +#include "nsTableFrame.h" 1.12 +#include "nsTableCellFrame.h" 1.13 +#include "nsPresContext.h" 1.14 +#include "nsStyleContext.h" 1.15 +#include "nsStyleConsts.h" 1.16 +#include "nsIContent.h" 1.17 +#include "nsGkAtoms.h" 1.18 +#include "nsIPresShell.h" 1.19 +#include "nsCSSRendering.h" 1.20 +#include "nsHTMLParts.h" 1.21 +#include "nsCSSFrameConstructor.h" 1.22 +#include "nsDisplayList.h" 1.23 + 1.24 +#include "nsCellMap.h"//table cell navigation 1.25 +#include <algorithm> 1.26 + 1.27 +using namespace mozilla; 1.28 +using namespace mozilla::layout; 1.29 + 1.30 +nsTableRowGroupFrame::nsTableRowGroupFrame(nsStyleContext* aContext): 1.31 + nsContainerFrame(aContext) 1.32 +{ 1.33 + SetRepeatable(false); 1.34 +} 1.35 + 1.36 +nsTableRowGroupFrame::~nsTableRowGroupFrame() 1.37 +{ 1.38 +} 1.39 + 1.40 +void 1.41 +nsTableRowGroupFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.42 +{ 1.43 + if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) { 1.44 + nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot); 1.45 + } 1.46 + 1.47 + nsContainerFrame::DestroyFrom(aDestructRoot); 1.48 +} 1.49 + 1.50 +NS_QUERYFRAME_HEAD(nsTableRowGroupFrame) 1.51 + NS_QUERYFRAME_ENTRY(nsTableRowGroupFrame) 1.52 +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) 1.53 + 1.54 +int32_t 1.55 +nsTableRowGroupFrame::GetRowCount() 1.56 +{ 1.57 +#ifdef DEBUG 1.58 + for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { 1.59 + NS_ASSERTION(e.get()->StyleDisplay()->mDisplay == 1.60 + NS_STYLE_DISPLAY_TABLE_ROW, 1.61 + "Unexpected display"); 1.62 + NS_ASSERTION(e.get()->GetType() == nsGkAtoms::tableRowFrame, 1.63 + "Unexpected frame type"); 1.64 + } 1.65 +#endif 1.66 + 1.67 + return mFrames.GetLength(); 1.68 +} 1.69 + 1.70 +int32_t nsTableRowGroupFrame::GetStartRowIndex() 1.71 +{ 1.72 + int32_t result = -1; 1.73 + if (mFrames.NotEmpty()) { 1.74 + NS_ASSERTION(mFrames.FirstChild()->GetType() == nsGkAtoms::tableRowFrame, 1.75 + "Unexpected frame type"); 1.76 + result = static_cast<nsTableRowFrame*>(mFrames.FirstChild())->GetRowIndex(); 1.77 + } 1.78 + // if the row group doesn't have any children, get it the hard way 1.79 + if (-1 == result) { 1.80 + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); 1.81 + return tableFrame->GetStartRowIndex(this); 1.82 + } 1.83 + 1.84 + return result; 1.85 +} 1.86 + 1.87 +void nsTableRowGroupFrame::AdjustRowIndices(int32_t aRowIndex, 1.88 + int32_t anAdjustment) 1.89 +{ 1.90 + nsIFrame* rowFrame = GetFirstPrincipalChild(); 1.91 + for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) { 1.92 + if (NS_STYLE_DISPLAY_TABLE_ROW==rowFrame->StyleDisplay()->mDisplay) { 1.93 + int32_t index = ((nsTableRowFrame*)rowFrame)->GetRowIndex(); 1.94 + if (index >= aRowIndex) 1.95 + ((nsTableRowFrame *)rowFrame)->SetRowIndex(index+anAdjustment); 1.96 + } 1.97 + } 1.98 +} 1.99 +nsresult 1.100 +nsTableRowGroupFrame::InitRepeatedFrame(nsPresContext* aPresContext, 1.101 + nsTableRowGroupFrame* aHeaderFooterFrame) 1.102 +{ 1.103 + nsTableRowFrame* copyRowFrame = GetFirstRow(); 1.104 + nsTableRowFrame* originalRowFrame = aHeaderFooterFrame->GetFirstRow(); 1.105 + AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP); 1.106 + while (copyRowFrame && originalRowFrame) { 1.107 + copyRowFrame->AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP); 1.108 + int rowIndex = originalRowFrame->GetRowIndex(); 1.109 + copyRowFrame->SetRowIndex(rowIndex); 1.110 + 1.111 + // For each table cell frame set its column index 1.112 + nsTableCellFrame* originalCellFrame = originalRowFrame->GetFirstCell(); 1.113 + nsTableCellFrame* copyCellFrame = copyRowFrame->GetFirstCell(); 1.114 + while (copyCellFrame && originalCellFrame) { 1.115 + NS_ASSERTION(originalCellFrame->GetContent() == copyCellFrame->GetContent(), 1.116 + "cell frames have different content"); 1.117 + int32_t colIndex; 1.118 + originalCellFrame->GetColIndex(colIndex); 1.119 + copyCellFrame->SetColIndex(colIndex); 1.120 + 1.121 + // Move to the next cell frame 1.122 + copyCellFrame = copyCellFrame->GetNextCell(); 1.123 + originalCellFrame = originalCellFrame->GetNextCell(); 1.124 + } 1.125 + 1.126 + // Move to the next row frame 1.127 + originalRowFrame = originalRowFrame->GetNextRow(); 1.128 + copyRowFrame = copyRowFrame->GetNextRow(); 1.129 + } 1.130 + 1.131 + return NS_OK; 1.132 +} 1.133 + 1.134 +/** 1.135 + * We need a custom display item for table row backgrounds. This is only used 1.136 + * when the table row is the root of a stacking context (e.g., has 'opacity'). 1.137 + * Table row backgrounds can extend beyond the row frame bounds, when 1.138 + * the row contains row-spanning cells. 1.139 + */ 1.140 +class nsDisplayTableRowGroupBackground : public nsDisplayTableItem { 1.141 +public: 1.142 + nsDisplayTableRowGroupBackground(nsDisplayListBuilder* aBuilder, 1.143 + nsTableRowGroupFrame* aFrame) : 1.144 + nsDisplayTableItem(aBuilder, aFrame) { 1.145 + MOZ_COUNT_CTOR(nsDisplayTableRowGroupBackground); 1.146 + } 1.147 +#ifdef NS_BUILD_REFCNT_LOGGING 1.148 + virtual ~nsDisplayTableRowGroupBackground() { 1.149 + MOZ_COUNT_DTOR(nsDisplayTableRowGroupBackground); 1.150 + } 1.151 +#endif 1.152 + 1.153 + virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, 1.154 + const nsDisplayItemGeometry* aGeometry, 1.155 + nsRegion *aInvalidRegion) MOZ_OVERRIDE; 1.156 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.157 + nsRenderingContext* aCtx) MOZ_OVERRIDE; 1.158 + 1.159 + NS_DISPLAY_DECL_NAME("TableRowGroupBackground", TYPE_TABLE_ROW_GROUP_BACKGROUND) 1.160 +}; 1.161 + 1.162 +void 1.163 +nsDisplayTableRowGroupBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, 1.164 + const nsDisplayItemGeometry* aGeometry, 1.165 + nsRegion *aInvalidRegion) 1.166 +{ 1.167 + if (aBuilder->ShouldSyncDecodeImages()) { 1.168 + if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling())) { 1.169 + bool snap; 1.170 + aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); 1.171 + } 1.172 + } 1.173 + 1.174 + nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); 1.175 +} 1.176 + 1.177 +void 1.178 +nsDisplayTableRowGroupBackground::Paint(nsDisplayListBuilder* aBuilder, 1.179 + nsRenderingContext* aCtx) 1.180 +{ 1.181 + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(mFrame); 1.182 + TableBackgroundPainter painter(tableFrame, 1.183 + TableBackgroundPainter::eOrigin_TableRowGroup, 1.184 + mFrame->PresContext(), *aCtx, 1.185 + mVisibleRect, ToReferenceFrame(), 1.186 + aBuilder->GetBackgroundPaintFlags()); 1.187 + painter.PaintRowGroup(static_cast<nsTableRowGroupFrame*>(mFrame)); 1.188 +} 1.189 + 1.190 +// Handle the child-traversal part of DisplayGenericTablePart 1.191 +static void 1.192 +DisplayRows(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, 1.193 + const nsRect& aDirtyRect, const nsDisplayListSet& aLists) 1.194 +{ 1.195 + nscoord overflowAbove; 1.196 + nsTableRowGroupFrame* f = static_cast<nsTableRowGroupFrame*>(aFrame); 1.197 + // Don't try to use the row cursor if we have to descend into placeholders; 1.198 + // we might have rows containing placeholders, where the row's overflow 1.199 + // area doesn't intersect the dirty rect but we need to descend into the row 1.200 + // to see out of flows. 1.201 + // Note that we really want to check ShouldDescendIntoFrame for all 1.202 + // the rows in |f|, but that's exactly what we're trying to avoid, so we 1.203 + // approximate it by checking it for |f|: if it's true for any row 1.204 + // in |f| then it's true for |f| itself. 1.205 + nsIFrame* kid = aBuilder->ShouldDescendIntoFrame(f) ? 1.206 + nullptr : f->GetFirstRowContaining(aDirtyRect.y, &overflowAbove); 1.207 + 1.208 + if (kid) { 1.209 + // have a cursor, use it 1.210 + while (kid) { 1.211 + if (kid->GetRect().y - overflowAbove >= aDirtyRect.YMost()) 1.212 + break; 1.213 + f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); 1.214 + kid = kid->GetNextSibling(); 1.215 + } 1.216 + return; 1.217 + } 1.218 + 1.219 + // No cursor. Traverse children the hard way and build a cursor while we're at it 1.220 + nsTableRowGroupFrame::FrameCursorData* cursor = f->SetupRowCursor(); 1.221 + kid = f->GetFirstPrincipalChild(); 1.222 + while (kid) { 1.223 + f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); 1.224 + 1.225 + if (cursor) { 1.226 + if (!cursor->AppendFrame(kid)) { 1.227 + f->ClearRowCursor(); 1.228 + return; 1.229 + } 1.230 + } 1.231 + 1.232 + kid = kid->GetNextSibling(); 1.233 + } 1.234 + if (cursor) { 1.235 + cursor->FinishBuildingCursor(); 1.236 + } 1.237 +} 1.238 + 1.239 +void 1.240 +nsTableRowGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.241 + const nsRect& aDirtyRect, 1.242 + const nsDisplayListSet& aLists) 1.243 +{ 1.244 + nsDisplayTableItem* item = nullptr; 1.245 + if (IsVisibleInSelection(aBuilder)) { 1.246 + bool isRoot = aBuilder->IsAtRootOfPseudoStackingContext(); 1.247 + if (isRoot) { 1.248 + // This background is created regardless of whether this frame is 1.249 + // visible or not. Visibility decisions are delegated to the 1.250 + // table background painter. 1.251 + item = new (aBuilder) nsDisplayTableRowGroupBackground(aBuilder, this); 1.252 + aLists.BorderBackground()->AppendNewToTop(item); 1.253 + } 1.254 + } 1.255 + nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, 1.256 + aLists, item, DisplayRows); 1.257 +} 1.258 + 1.259 +int 1.260 +nsTableRowGroupFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const 1.261 +{ 1.262 + int skip = 0; 1.263 + if (nullptr != GetPrevInFlow()) { 1.264 + skip |= LOGICAL_SIDE_B_START; 1.265 + } 1.266 + if (nullptr != GetNextInFlow()) { 1.267 + skip |= LOGICAL_SIDE_B_END; 1.268 + } 1.269 + return skip; 1.270 +} 1.271 + 1.272 +// Position and size aKidFrame and update our reflow state. The origin of 1.273 +// aKidRect is relative to the upper-left origin of our frame 1.274 +void 1.275 +nsTableRowGroupFrame::PlaceChild(nsPresContext* aPresContext, 1.276 + nsRowGroupReflowState& aReflowState, 1.277 + nsIFrame* aKidFrame, 1.278 + nsHTMLReflowMetrics& aDesiredSize, 1.279 + const nsRect& aOriginalKidRect, 1.280 + const nsRect& aOriginalKidVisualOverflow) 1.281 +{ 1.282 + bool isFirstReflow = 1.283 + (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; 1.284 + 1.285 + // Place and size the child 1.286 + FinishReflowChild(aKidFrame, aPresContext, aDesiredSize, nullptr, 0, 1.287 + aReflowState.y, 0); 1.288 + 1.289 + nsTableFrame::InvalidateTableFrame(aKidFrame, aOriginalKidRect, 1.290 + aOriginalKidVisualOverflow, isFirstReflow); 1.291 + 1.292 + // Adjust the running y-offset 1.293 + aReflowState.y += aDesiredSize.Height(); 1.294 + 1.295 + // If our height is constrained then update the available height 1.296 + if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { 1.297 + aReflowState.availSize.height -= aDesiredSize.Height(); 1.298 + } 1.299 +} 1.300 + 1.301 +void 1.302 +nsTableRowGroupFrame::InitChildReflowState(nsPresContext& aPresContext, 1.303 + bool aBorderCollapse, 1.304 + nsHTMLReflowState& aReflowState) 1.305 +{ 1.306 + nsMargin collapseBorder; 1.307 + nsMargin padding(0,0,0,0); 1.308 + nsMargin* pCollapseBorder = nullptr; 1.309 + if (aBorderCollapse) { 1.310 + nsTableRowFrame *rowFrame = do_QueryFrame(aReflowState.frame); 1.311 + if (rowFrame) { 1.312 + pCollapseBorder = rowFrame->GetBCBorderWidth(collapseBorder); 1.313 + } 1.314 + } 1.315 + aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, &padding); 1.316 +} 1.317 + 1.318 +static void 1.319 +CacheRowHeightsForPrinting(nsPresContext* aPresContext, 1.320 + nsTableRowFrame* aFirstRow) 1.321 +{ 1.322 + for (nsTableRowFrame* row = aFirstRow; row; row = row->GetNextRow()) { 1.323 + if (!row->GetPrevInFlow()) { 1.324 + row->SetHasUnpaginatedHeight(true); 1.325 + row->SetUnpaginatedHeight(aPresContext, row->GetSize().height); 1.326 + } 1.327 + } 1.328 +} 1.329 + 1.330 +nsresult 1.331 +nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext, 1.332 + nsHTMLReflowMetrics& aDesiredSize, 1.333 + nsRowGroupReflowState& aReflowState, 1.334 + nsReflowStatus& aStatus, 1.335 + bool* aPageBreakBeforeEnd) 1.336 +{ 1.337 + if (aPageBreakBeforeEnd) 1.338 + *aPageBreakBeforeEnd = false; 1.339 + 1.340 + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); 1.341 + nsresult rv = NS_OK; 1.342 + const bool borderCollapse = tableFrame->IsBorderCollapse(); 1.343 + nscoord cellSpacingY = tableFrame->GetCellSpacingY(); 1.344 + 1.345 + // XXXldb Should we really be checking this rather than available height? 1.346 + // (Think about multi-column layout!) 1.347 + bool isPaginated = aPresContext->IsPaginated() && 1.348 + NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height; 1.349 + 1.350 + bool haveRow = false; 1.351 + bool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() || 1.352 + tableFrame->IsGeometryDirty(); 1.353 + bool needToCalcRowHeights = reflowAllKids; 1.354 + 1.355 + nsIFrame *prevKidFrame = nullptr; 1.356 + for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; 1.357 + prevKidFrame = kidFrame, kidFrame = kidFrame->GetNextSibling()) { 1.358 + nsTableRowFrame *rowFrame = do_QueryFrame(kidFrame); 1.359 + if (!rowFrame) { 1.360 + // XXXldb nsCSSFrameConstructor needs to enforce this! 1.361 + NS_NOTREACHED("yikes, a non-row child"); 1.362 + continue; 1.363 + } 1.364 + 1.365 + haveRow = true; 1.366 + 1.367 + // Reflow the row frame 1.368 + if (reflowAllKids || 1.369 + NS_SUBTREE_DIRTY(kidFrame) || 1.370 + (aReflowState.reflowState.mFlags.mSpecialHeightReflow && 1.371 + (isPaginated || (kidFrame->GetStateBits() & 1.372 + NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) { 1.373 + nsRect oldKidRect = kidFrame->GetRect(); 1.374 + nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect(); 1.375 + 1.376 + // XXXldb We used to only pass aDesiredSize.mFlags through for the 1.377 + // incremental reflow codepath. 1.378 + nsHTMLReflowMetrics desiredSize(aReflowState.reflowState, 1.379 + aDesiredSize.mFlags); 1.380 + desiredSize.Width() = desiredSize.Height() = 0; 1.381 + 1.382 + // Reflow the child into the available space, giving it as much height as 1.383 + // it wants. We'll deal with splitting later after we've computed the row 1.384 + // heights, taking into account cells with row spans... 1.385 + nsSize kidAvailSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE); 1.386 + nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, 1.387 + kidFrame, kidAvailSize, 1.388 + -1, -1, 1.389 + nsHTMLReflowState::CALLER_WILL_INIT); 1.390 + InitChildReflowState(*aPresContext, borderCollapse, kidReflowState); 1.391 + 1.392 + // This can indicate that columns were resized. 1.393 + if (aReflowState.reflowState.mFlags.mHResize) 1.394 + kidReflowState.mFlags.mHResize = true; 1.395 + 1.396 + NS_ASSERTION(kidFrame == mFrames.FirstChild() || prevKidFrame, 1.397 + "If we're not on the first frame, we should have a " 1.398 + "previous sibling..."); 1.399 + // If prev row has nonzero YMost, then we can't be at the top of the page 1.400 + if (prevKidFrame && prevKidFrame->GetRect().YMost() > 0) { 1.401 + kidReflowState.mFlags.mIsTopOfPage = false; 1.402 + } 1.403 + 1.404 + rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, 1.405 + 0, aReflowState.y, 0, aStatus); 1.406 + 1.407 + // Place the child 1.408 + PlaceChild(aPresContext, aReflowState, kidFrame, desiredSize, 1.409 + oldKidRect, oldKidVisualOverflow); 1.410 + aReflowState.y += cellSpacingY; 1.411 + 1.412 + if (!reflowAllKids) { 1.413 + if (IsSimpleRowFrame(aReflowState.tableFrame, kidFrame)) { 1.414 + // Inform the row of its new height. 1.415 + rowFrame->DidResize(); 1.416 + // the overflow area may have changed inflate the overflow area 1.417 + const nsStylePosition *stylePos = StylePosition(); 1.418 + nsStyleUnit unit = stylePos->mHeight.GetUnit(); 1.419 + if (aReflowState.tableFrame->IsAutoHeight() && 1.420 + unit != eStyleUnit_Coord) { 1.421 + // Because other cells in the row may need to be aligned 1.422 + // differently, repaint the entire row 1.423 + nsRect kidRect(0, aReflowState.y, 1.424 + desiredSize.Width(), desiredSize.Height()); 1.425 + InvalidateFrame(); 1.426 + } 1.427 + else if (oldKidRect.height != desiredSize.Height()) 1.428 + needToCalcRowHeights = true; 1.429 + } else { 1.430 + needToCalcRowHeights = true; 1.431 + } 1.432 + } 1.433 + 1.434 + if (isPaginated && aPageBreakBeforeEnd && !*aPageBreakBeforeEnd) { 1.435 + nsTableRowFrame* nextRow = rowFrame->GetNextRow(); 1.436 + if (nextRow) { 1.437 + *aPageBreakBeforeEnd = nsTableFrame::PageBreakAfter(kidFrame, nextRow); 1.438 + } 1.439 + } 1.440 + } else { 1.441 + SlideChild(aReflowState, kidFrame); 1.442 + 1.443 + // Adjust the running y-offset so we know where the next row should be placed 1.444 + nscoord height = kidFrame->GetSize().height + cellSpacingY; 1.445 + aReflowState.y += height; 1.446 + 1.447 + if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { 1.448 + aReflowState.availSize.height -= height; 1.449 + } 1.450 + } 1.451 + ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame); 1.452 + } 1.453 + 1.454 + if (haveRow) 1.455 + aReflowState.y -= cellSpacingY; 1.456 + 1.457 + // Return our desired rect 1.458 + aDesiredSize.Width() = aReflowState.reflowState.AvailableWidth(); 1.459 + aDesiredSize.Height() = aReflowState.y; 1.460 + 1.461 + if (aReflowState.reflowState.mFlags.mSpecialHeightReflow) { 1.462 + DidResizeRows(aDesiredSize); 1.463 + if (isPaginated) { 1.464 + CacheRowHeightsForPrinting(aPresContext, GetFirstRow()); 1.465 + } 1.466 + } 1.467 + else if (needToCalcRowHeights) { 1.468 + CalculateRowHeights(aPresContext, aDesiredSize, aReflowState.reflowState); 1.469 + if (!reflowAllKids) { 1.470 + InvalidateFrame(); 1.471 + } 1.472 + } 1.473 + 1.474 + return rv; 1.475 +} 1.476 + 1.477 +nsTableRowFrame* 1.478 +nsTableRowGroupFrame::GetFirstRow() 1.479 +{ 1.480 + for (nsIFrame* childFrame = mFrames.FirstChild(); childFrame; 1.481 + childFrame = childFrame->GetNextSibling()) { 1.482 + nsTableRowFrame *rowFrame = do_QueryFrame(childFrame); 1.483 + if (rowFrame) { 1.484 + return rowFrame; 1.485 + } 1.486 + } 1.487 + return nullptr; 1.488 +} 1.489 + 1.490 + 1.491 +struct RowInfo { 1.492 + RowInfo() { height = pctHeight = hasStyleHeight = hasPctHeight = isSpecial = 0; } 1.493 + unsigned height; // content height or fixed height, excluding pct height 1.494 + unsigned pctHeight:29; // pct height 1.495 + unsigned hasStyleHeight:1; 1.496 + unsigned hasPctHeight:1; 1.497 + unsigned isSpecial:1; // there is no cell originating in the row with rowspan=1 and there are at 1.498 + // least 2 cells spanning the row and there is no style height on the row 1.499 +}; 1.500 + 1.501 +static void 1.502 +UpdateHeights(RowInfo& aRowInfo, 1.503 + nscoord aAdditionalHeight, 1.504 + nscoord& aTotal, 1.505 + nscoord& aUnconstrainedTotal) 1.506 +{ 1.507 + aRowInfo.height += aAdditionalHeight; 1.508 + aTotal += aAdditionalHeight; 1.509 + if (!aRowInfo.hasStyleHeight) { 1.510 + aUnconstrainedTotal += aAdditionalHeight; 1.511 + } 1.512 +} 1.513 + 1.514 +void 1.515 +nsTableRowGroupFrame::DidResizeRows(nsHTMLReflowMetrics& aDesiredSize) 1.516 +{ 1.517 + // update the cells spanning rows with their new heights 1.518 + // this is the place where all of the cells in the row get set to the height of the row 1.519 + // Reset the overflow area 1.520 + aDesiredSize.mOverflowAreas.Clear(); 1.521 + for (nsTableRowFrame* rowFrame = GetFirstRow(); 1.522 + rowFrame; rowFrame = rowFrame->GetNextRow()) { 1.523 + rowFrame->DidResize(); 1.524 + ConsiderChildOverflow(aDesiredSize.mOverflowAreas, rowFrame); 1.525 + } 1.526 +} 1.527 + 1.528 +// This calculates the height of all the rows and takes into account 1.529 +// style height on the row group, style heights on rows and cells, style heights on rowspans. 1.530 +// Actual row heights will be adjusted later if the table has a style height. 1.531 +// Even if rows don't change height, this method must be called to set the heights of each 1.532 +// cell in the row to the height of its row. 1.533 +void 1.534 +nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext, 1.535 + nsHTMLReflowMetrics& aDesiredSize, 1.536 + const nsHTMLReflowState& aReflowState) 1.537 +{ 1.538 + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); 1.539 + const bool isPaginated = aPresContext->IsPaginated(); 1.540 + 1.541 + // all table cells have the same top and bottom margins, namely cellSpacingY 1.542 + nscoord cellSpacingY = tableFrame->GetCellSpacingY(); 1.543 + 1.544 + int32_t numEffCols = tableFrame->GetEffectiveColCount(); 1.545 + 1.546 + int32_t startRowIndex = GetStartRowIndex(); 1.547 + // find the row corresponding to the row index we just found 1.548 + nsTableRowFrame* startRowFrame = GetFirstRow(); 1.549 + 1.550 + if (!startRowFrame) return; 1.551 + 1.552 + // the current row group height is the y origin of the 1st row we are about to calculated a height for 1.553 + nscoord startRowGroupHeight = startRowFrame->GetPosition().y; 1.554 + 1.555 + int32_t numRows = GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex()); 1.556 + // collect the current height of each row. nscoord* rowHeights = nullptr; 1.557 + if (numRows <= 0) 1.558 + return; 1.559 + 1.560 + nsTArray<RowInfo> rowInfo; 1.561 + if (!rowInfo.AppendElements(numRows)) { 1.562 + return; 1.563 + } 1.564 + 1.565 + bool hasRowSpanningCell = false; 1.566 + nscoord heightOfRows = 0; 1.567 + nscoord heightOfUnStyledRows = 0; 1.568 + // Get the height of each row without considering rowspans. This will be the max of 1.569 + // the largest desired height of each cell, the largest style height of each cell, 1.570 + // the style height of the row. 1.571 + nscoord pctHeightBasis = GetHeightBasis(aReflowState); 1.572 + int32_t rowIndex; // the index in rowInfo, not among the rows in the row group 1.573 + nsTableRowFrame* rowFrame; 1.574 + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { 1.575 + nscoord nonPctHeight = rowFrame->GetContentHeight(); 1.576 + if (isPaginated) { 1.577 + nonPctHeight = std::max(nonPctHeight, rowFrame->GetSize().height); 1.578 + } 1.579 + if (!rowFrame->GetPrevInFlow()) { 1.580 + if (rowFrame->HasPctHeight()) { 1.581 + rowInfo[rowIndex].hasPctHeight = true; 1.582 + rowInfo[rowIndex].pctHeight = rowFrame->GetHeight(pctHeightBasis); 1.583 + } 1.584 + rowInfo[rowIndex].hasStyleHeight = rowFrame->HasStyleHeight(); 1.585 + nonPctHeight = std::max(nonPctHeight, rowFrame->GetFixedHeight()); 1.586 + } 1.587 + UpdateHeights(rowInfo[rowIndex], nonPctHeight, heightOfRows, heightOfUnStyledRows); 1.588 + 1.589 + if (!rowInfo[rowIndex].hasStyleHeight) { 1.590 + if (isPaginated || tableFrame->HasMoreThanOneCell(rowIndex + startRowIndex)) { 1.591 + rowInfo[rowIndex].isSpecial = true; 1.592 + // iteratate the row's cell frames to see if any do not have rowspan > 1 1.593 + nsTableCellFrame* cellFrame = rowFrame->GetFirstCell(); 1.594 + while (cellFrame) { 1.595 + int32_t rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame); 1.596 + if (1 == rowSpan) { 1.597 + rowInfo[rowIndex].isSpecial = false; 1.598 + break; 1.599 + } 1.600 + cellFrame = cellFrame->GetNextCell(); 1.601 + } 1.602 + } 1.603 + } 1.604 + // See if a cell spans into the row. If so we'll have to do the next step 1.605 + if (!hasRowSpanningCell) { 1.606 + if (tableFrame->RowIsSpannedInto(rowIndex + startRowIndex, numEffCols)) { 1.607 + hasRowSpanningCell = true; 1.608 + } 1.609 + } 1.610 + } 1.611 + 1.612 + if (hasRowSpanningCell) { 1.613 + // Get the height of cells with rowspans and allocate any extra space to the rows they span 1.614 + // iteratate the child frames and process the row frames among them 1.615 + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { 1.616 + // See if the row has an originating cell with rowspan > 1. We cannot determine this for a row in a 1.617 + // continued row group by calling RowHasSpanningCells, because the row's fif may not have any originating 1.618 + // cells yet the row may have a continued cell which originates in it. 1.619 + if (GetPrevInFlow() || tableFrame->RowHasSpanningCells(startRowIndex + rowIndex, numEffCols)) { 1.620 + nsTableCellFrame* cellFrame = rowFrame->GetFirstCell(); 1.621 + // iteratate the row's cell frames 1.622 + while (cellFrame) { 1.623 + int32_t rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame); 1.624 + if ((rowIndex + rowSpan) > numRows) { 1.625 + // there might be rows pushed already to the nextInFlow 1.626 + rowSpan = numRows - rowIndex; 1.627 + } 1.628 + if (rowSpan > 1) { // a cell with rowspan > 1, determine the height of the rows it spans 1.629 + nscoord heightOfRowsSpanned = 0; 1.630 + nscoord heightOfUnStyledRowsSpanned = 0; 1.631 + nscoord numSpecialRowsSpanned = 0; 1.632 + nscoord cellSpacingTotal = 0; 1.633 + int32_t spanX; 1.634 + for (spanX = 0; spanX < rowSpan; spanX++) { 1.635 + heightOfRowsSpanned += rowInfo[rowIndex + spanX].height; 1.636 + if (!rowInfo[rowIndex + spanX].hasStyleHeight) { 1.637 + heightOfUnStyledRowsSpanned += rowInfo[rowIndex + spanX].height; 1.638 + } 1.639 + if (0 != spanX) { 1.640 + cellSpacingTotal += cellSpacingY; 1.641 + } 1.642 + if (rowInfo[rowIndex + spanX].isSpecial) { 1.643 + numSpecialRowsSpanned++; 1.644 + } 1.645 + } 1.646 + nscoord heightOfAreaSpanned = heightOfRowsSpanned + cellSpacingTotal; 1.647 + // get the height of the cell 1.648 + nsSize cellFrameSize = cellFrame->GetSize(); 1.649 + nsSize cellDesSize = cellFrame->GetDesiredSize(); 1.650 + rowFrame->CalculateCellActualHeight(cellFrame, cellDesSize.height); 1.651 + cellFrameSize.height = cellDesSize.height; 1.652 + if (cellFrame->HasVerticalAlignBaseline()) { 1.653 + // to ensure that a spanning cell with a long descender doesn't 1.654 + // collide with the next row, we need to take into account the shift 1.655 + // that will be done to align the cell on the baseline of the row. 1.656 + cellFrameSize.height += rowFrame->GetMaxCellAscent() - 1.657 + cellFrame->GetCellBaseline(); 1.658 + } 1.659 + 1.660 + if (heightOfAreaSpanned < cellFrameSize.height) { 1.661 + // the cell's height is larger than the available space of the rows it 1.662 + // spans so distribute the excess height to the rows affected 1.663 + nscoord extra = cellFrameSize.height - heightOfAreaSpanned; 1.664 + nscoord extraUsed = 0; 1.665 + if (0 == numSpecialRowsSpanned) { 1.666 + //NS_ASSERTION(heightOfRowsSpanned > 0, "invalid row span situation"); 1.667 + bool haveUnStyledRowsSpanned = (heightOfUnStyledRowsSpanned > 0); 1.668 + nscoord divisor = (haveUnStyledRowsSpanned) 1.669 + ? heightOfUnStyledRowsSpanned : heightOfRowsSpanned; 1.670 + if (divisor > 0) { 1.671 + for (spanX = rowSpan - 1; spanX >= 0; spanX--) { 1.672 + if (!haveUnStyledRowsSpanned || !rowInfo[rowIndex + spanX].hasStyleHeight) { 1.673 + // The amount of additional space each row gets is proportional to its height 1.674 + float percent = ((float)rowInfo[rowIndex + spanX].height) / ((float)divisor); 1.675 + 1.676 + // give rows their percentage, except for the first row which gets the remainder 1.677 + nscoord extraForRow = (0 == spanX) ? extra - extraUsed 1.678 + : NSToCoordRound(((float)(extra)) * percent); 1.679 + extraForRow = std::min(extraForRow, extra - extraUsed); 1.680 + // update the row height 1.681 + UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows); 1.682 + extraUsed += extraForRow; 1.683 + if (extraUsed >= extra) { 1.684 + NS_ASSERTION((extraUsed == extra), "invalid row height calculation"); 1.685 + break; 1.686 + } 1.687 + } 1.688 + } 1.689 + } 1.690 + else { 1.691 + // put everything in the last row 1.692 + UpdateHeights(rowInfo[rowIndex + rowSpan - 1], extra, heightOfRows, heightOfUnStyledRows); 1.693 + } 1.694 + } 1.695 + else { 1.696 + // give the extra to the special rows 1.697 + nscoord numSpecialRowsAllocated = 0; 1.698 + for (spanX = rowSpan - 1; spanX >= 0; spanX--) { 1.699 + if (rowInfo[rowIndex + spanX].isSpecial) { 1.700 + // The amount of additional space each degenerate row gets is proportional to the number of them 1.701 + float percent = 1.0f / ((float)numSpecialRowsSpanned); 1.702 + 1.703 + // give rows their percentage, except for the first row which gets the remainder 1.704 + nscoord extraForRow = (numSpecialRowsSpanned - 1 == numSpecialRowsAllocated) 1.705 + ? extra - extraUsed 1.706 + : NSToCoordRound(((float)(extra)) * percent); 1.707 + extraForRow = std::min(extraForRow, extra - extraUsed); 1.708 + // update the row height 1.709 + UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows); 1.710 + extraUsed += extraForRow; 1.711 + if (extraUsed >= extra) { 1.712 + NS_ASSERTION((extraUsed == extra), "invalid row height calculation"); 1.713 + break; 1.714 + } 1.715 + } 1.716 + } 1.717 + } 1.718 + } 1.719 + } // if (rowSpan > 1) 1.720 + cellFrame = cellFrame->GetNextCell(); 1.721 + } // while (cellFrame) 1.722 + } // if (tableFrame->RowHasSpanningCells(startRowIndex + rowIndex) { 1.723 + } // while (rowFrame) 1.724 + } 1.725 + 1.726 + // pct height rows have already got their content heights. Give them their pct heights up to pctHeightBasis 1.727 + nscoord extra = pctHeightBasis - heightOfRows; 1.728 + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame && (extra > 0); rowFrame = rowFrame->GetNextRow(), rowIndex++) { 1.729 + RowInfo& rInfo = rowInfo[rowIndex]; 1.730 + if (rInfo.hasPctHeight) { 1.731 + nscoord rowExtra = (rInfo.pctHeight > rInfo.height) 1.732 + ? rInfo.pctHeight - rInfo.height: 0; 1.733 + rowExtra = std::min(rowExtra, extra); 1.734 + UpdateHeights(rInfo, rowExtra, heightOfRows, heightOfUnStyledRows); 1.735 + extra -= rowExtra; 1.736 + } 1.737 + } 1.738 + 1.739 + bool styleHeightAllocation = false; 1.740 + nscoord rowGroupHeight = startRowGroupHeight + heightOfRows + ((numRows - 1) * cellSpacingY); 1.741 + // if we have a style height, allocate the extra height to unconstrained rows 1.742 + if ((aReflowState.ComputedHeight() > rowGroupHeight) && 1.743 + (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight())) { 1.744 + nscoord extraComputedHeight = aReflowState.ComputedHeight() - rowGroupHeight; 1.745 + nscoord extraUsed = 0; 1.746 + bool haveUnStyledRows = (heightOfUnStyledRows > 0); 1.747 + nscoord divisor = (haveUnStyledRows) 1.748 + ? heightOfUnStyledRows : heightOfRows; 1.749 + if (divisor > 0) { 1.750 + styleHeightAllocation = true; 1.751 + for (rowIndex = 0; rowIndex < numRows; rowIndex++) { 1.752 + if (!haveUnStyledRows || !rowInfo[rowIndex].hasStyleHeight) { 1.753 + // The amount of additional space each row gets is based on the 1.754 + // percentage of space it occupies 1.755 + float percent = ((float)rowInfo[rowIndex].height) / ((float)divisor); 1.756 + // give rows their percentage, except for the last row which gets the remainder 1.757 + nscoord extraForRow = (numRows - 1 == rowIndex) 1.758 + ? extraComputedHeight - extraUsed 1.759 + : NSToCoordRound(((float)extraComputedHeight) * percent); 1.760 + extraForRow = std::min(extraForRow, extraComputedHeight - extraUsed); 1.761 + // update the row height 1.762 + UpdateHeights(rowInfo[rowIndex], extraForRow, heightOfRows, heightOfUnStyledRows); 1.763 + extraUsed += extraForRow; 1.764 + if (extraUsed >= extraComputedHeight) { 1.765 + NS_ASSERTION((extraUsed == extraComputedHeight), "invalid row height calculation"); 1.766 + break; 1.767 + } 1.768 + } 1.769 + } 1.770 + } 1.771 + rowGroupHeight = aReflowState.ComputedHeight(); 1.772 + } 1.773 + 1.774 + nscoord yOrigin = startRowGroupHeight; 1.775 + // update the rows with their (potentially) new heights 1.776 + for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { 1.777 + nsRect rowBounds = rowFrame->GetRect(); 1.778 + nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect(); 1.779 + 1.780 + bool movedFrame = (rowBounds.y != yOrigin); 1.781 + nscoord rowHeight = (rowInfo[rowIndex].height > 0) ? rowInfo[rowIndex].height : 0; 1.782 + 1.783 + if (movedFrame || (rowHeight != rowBounds.height)) { 1.784 + // Resize/move the row to its final size and position 1.785 + if (movedFrame) { 1.786 + rowFrame->InvalidateFrameSubtree(); 1.787 + } 1.788 + 1.789 + rowFrame->SetRect(nsRect(rowBounds.x, yOrigin, rowBounds.width, 1.790 + rowHeight)); 1.791 + 1.792 + nsTableFrame::InvalidateTableFrame(rowFrame, rowBounds, rowVisualOverflow, 1.793 + false); 1.794 + } 1.795 + if (movedFrame) { 1.796 + nsTableFrame::RePositionViews(rowFrame); 1.797 + // XXXbz we don't need to update our overflow area? 1.798 + } 1.799 + yOrigin += rowHeight + cellSpacingY; 1.800 + } 1.801 + 1.802 + if (isPaginated && styleHeightAllocation) { 1.803 + // since the row group has a style height, cache the row heights, so next in flows can honor them 1.804 + CacheRowHeightsForPrinting(aPresContext, GetFirstRow()); 1.805 + } 1.806 + 1.807 + DidResizeRows(aDesiredSize); 1.808 + 1.809 + aDesiredSize.Height() = rowGroupHeight; // Adjust our desired size 1.810 +} 1.811 + 1.812 +nscoord 1.813 +nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aYTotalOffset, 1.814 + nscoord aWidth) 1.815 +{ 1.816 + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); 1.817 + const nsStyleVisibility* groupVis = StyleVisibility(); 1.818 + bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible); 1.819 + if (collapseGroup) { 1.820 + tableFrame->SetNeedToCollapse(true); 1.821 + } 1.822 + 1.823 + nsOverflowAreas overflow; 1.824 + 1.825 + nsTableRowFrame* rowFrame= GetFirstRow(); 1.826 + bool didCollapse = false; 1.827 + nscoord yGroupOffset = 0; 1.828 + while (rowFrame) { 1.829 + yGroupOffset += rowFrame->CollapseRowIfNecessary(yGroupOffset, 1.830 + aWidth, collapseGroup, 1.831 + didCollapse); 1.832 + ConsiderChildOverflow(overflow, rowFrame); 1.833 + rowFrame = rowFrame->GetNextRow(); 1.834 + } 1.835 + 1.836 + nsRect groupRect = GetRect(); 1.837 + nsRect oldGroupRect = groupRect; 1.838 + nsRect oldGroupVisualOverflow = GetVisualOverflowRect(); 1.839 + 1.840 + groupRect.height -= yGroupOffset; 1.841 + if (didCollapse) { 1.842 + // add back the cellspacing between rowgroups 1.843 + groupRect.height += tableFrame->GetCellSpacingY(); 1.844 + } 1.845 + 1.846 + groupRect.y -= aYTotalOffset; 1.847 + groupRect.width = aWidth; 1.848 + 1.849 + if (aYTotalOffset != 0) { 1.850 + InvalidateFrameSubtree(); 1.851 + } 1.852 + 1.853 + SetRect(groupRect); 1.854 + overflow.UnionAllWith(nsRect(0, 0, groupRect.width, groupRect.height)); 1.855 + FinishAndStoreOverflow(overflow, groupRect.Size()); 1.856 + nsTableFrame::RePositionViews(this); 1.857 + nsTableFrame::InvalidateTableFrame(this, oldGroupRect, oldGroupVisualOverflow, 1.858 + false); 1.859 + 1.860 + return yGroupOffset; 1.861 +} 1.862 + 1.863 +// Move a child that was skipped during a reflow. 1.864 +void 1.865 +nsTableRowGroupFrame::SlideChild(nsRowGroupReflowState& aReflowState, 1.866 + nsIFrame* aKidFrame) 1.867 +{ 1.868 + // Move the frame if we need to 1.869 + nsPoint oldPosition = aKidFrame->GetPosition(); 1.870 + nsPoint newPosition = oldPosition; 1.871 + newPosition.y = aReflowState.y; 1.872 + if (oldPosition.y != newPosition.y) { 1.873 + aKidFrame->InvalidateFrameSubtree(); 1.874 + aKidFrame->SetPosition(newPosition); 1.875 + nsTableFrame::RePositionViews(aKidFrame); 1.876 + aKidFrame->InvalidateFrameSubtree(); 1.877 + } 1.878 +} 1.879 + 1.880 +// Create a continuing frame, add it to the child list, and then push it 1.881 +// and the frames that follow 1.882 +void 1.883 +nsTableRowGroupFrame::CreateContinuingRowFrame(nsPresContext& aPresContext, 1.884 + nsIFrame& aRowFrame, 1.885 + nsIFrame** aContRowFrame) 1.886 +{ 1.887 + // XXX what is the row index? 1.888 + if (!aContRowFrame) {NS_ASSERTION(false, "bad call"); return;} 1.889 + // create the continuing frame which will create continuing cell frames 1.890 + *aContRowFrame = aPresContext.PresShell()->FrameConstructor()-> 1.891 + CreateContinuingFrame(&aPresContext, &aRowFrame, this); 1.892 + 1.893 + // Add the continuing row frame to the child list 1.894 + mFrames.InsertFrame(nullptr, &aRowFrame, *aContRowFrame); 1.895 + 1.896 + // Push the continuing row frame and the frames that follow 1.897 + PushChildren(*aContRowFrame, &aRowFrame); 1.898 +} 1.899 + 1.900 +// Reflow the cells with rowspan > 1 which originate between aFirstRow 1.901 +// and end on or after aLastRow. aFirstTruncatedRow is the highest row on the 1.902 +// page that contains a cell which cannot split on this page 1.903 +void 1.904 +nsTableRowGroupFrame::SplitSpanningCells(nsPresContext& aPresContext, 1.905 + const nsHTMLReflowState& aReflowState, 1.906 + nsTableFrame& aTable, 1.907 + nsTableRowFrame& aFirstRow, 1.908 + nsTableRowFrame& aLastRow, 1.909 + bool aFirstRowIsTopOfPage, 1.910 + nscoord aSpanningRowBottom, 1.911 + nsTableRowFrame*& aContRow, 1.912 + nsTableRowFrame*& aFirstTruncatedRow, 1.913 + nscoord& aDesiredHeight) 1.914 +{ 1.915 + NS_ASSERTION(aSpanningRowBottom >= 0, "Can't split negative heights"); 1.916 + aFirstTruncatedRow = nullptr; 1.917 + aDesiredHeight = 0; 1.918 + 1.919 + const bool borderCollapse = aTable.IsBorderCollapse(); 1.920 + int32_t lastRowIndex = aLastRow.GetRowIndex(); 1.921 + bool wasLast = false; 1.922 + bool haveRowSpan = false; 1.923 + // Iterate the rows between aFirstRow and aLastRow 1.924 + for (nsTableRowFrame* row = &aFirstRow; !wasLast; row = row->GetNextRow()) { 1.925 + wasLast = (row == &aLastRow); 1.926 + int32_t rowIndex = row->GetRowIndex(); 1.927 + nsPoint rowPos = row->GetPosition(); 1.928 + // Iterate the cells looking for those that have rowspan > 1 1.929 + for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) { 1.930 + int32_t rowSpan = aTable.GetEffectiveRowSpan(rowIndex, *cell); 1.931 + // Only reflow rowspan > 1 cells which span aLastRow. Those which don't span aLastRow 1.932 + // were reflowed correctly during the unconstrained height reflow. 1.933 + if ((rowSpan > 1) && (rowIndex + rowSpan > lastRowIndex)) { 1.934 + haveRowSpan = true; 1.935 + nsReflowStatus status; 1.936 + // Ask the row to reflow the cell to the height of all the rows it spans up through aLastRow 1.937 + // aAvailHeight is the space between the row group start and the end of the page 1.938 + nscoord cellAvailHeight = aSpanningRowBottom - rowPos.y; 1.939 + NS_ASSERTION(cellAvailHeight >= 0, "No space for cell?"); 1.940 + bool isTopOfPage = (row == &aFirstRow) && aFirstRowIsTopOfPage; 1.941 + 1.942 + nsRect rowRect = row->GetRect(); 1.943 + nsSize rowAvailSize(aReflowState.AvailableWidth(), 1.944 + std::max(aReflowState.AvailableHeight() - rowRect.y, 1.945 + 0)); 1.946 + // don't let the available height exceed what 1.947 + // CalculateRowHeights set for it 1.948 + rowAvailSize.height = std::min(rowAvailSize.height, rowRect.height); 1.949 + nsHTMLReflowState rowReflowState(&aPresContext, aReflowState, 1.950 + row, rowAvailSize, 1.951 + -1, -1, 1.952 + nsHTMLReflowState::CALLER_WILL_INIT); 1.953 + InitChildReflowState(aPresContext, borderCollapse, rowReflowState); 1.954 + rowReflowState.mFlags.mIsTopOfPage = isTopOfPage; // set top of page 1.955 + 1.956 + nscoord cellHeight = row->ReflowCellFrame(&aPresContext, rowReflowState, 1.957 + isTopOfPage, cell, 1.958 + cellAvailHeight, status); 1.959 + aDesiredHeight = std::max(aDesiredHeight, rowPos.y + cellHeight); 1.960 + if (NS_FRAME_IS_COMPLETE(status)) { 1.961 + if (cellHeight > cellAvailHeight) { 1.962 + aFirstTruncatedRow = row; 1.963 + if ((row != &aFirstRow) || !aFirstRowIsTopOfPage) { 1.964 + // return now, since we will be getting another reflow after either (1) row is 1.965 + // moved to the next page or (2) the row group is moved to the next page 1.966 + return; 1.967 + } 1.968 + } 1.969 + } 1.970 + else { 1.971 + if (!aContRow) { 1.972 + CreateContinuingRowFrame(aPresContext, aLastRow, (nsIFrame**)&aContRow); 1.973 + } 1.974 + if (aContRow) { 1.975 + if (row != &aLastRow) { 1.976 + // aContRow needs a continuation for cell, since cell spanned into aLastRow 1.977 + // but does not originate there 1.978 + nsTableCellFrame* contCell = static_cast<nsTableCellFrame*>( 1.979 + aPresContext.PresShell()->FrameConstructor()-> 1.980 + CreateContinuingFrame(&aPresContext, cell, &aLastRow)); 1.981 + int32_t colIndex; 1.982 + cell->GetColIndex(colIndex); 1.983 + aContRow->InsertCellFrame(contCell, colIndex); 1.984 + } 1.985 + } 1.986 + } 1.987 + } 1.988 + } 1.989 + } 1.990 + if (!haveRowSpan) { 1.991 + aDesiredHeight = aLastRow.GetRect().YMost(); 1.992 + } 1.993 +} 1.994 + 1.995 +// Remove the next-in-flow of the row, its cells and their cell blocks. This 1.996 +// is necessary in case the row doesn't need a continuation later on or needs 1.997 +// a continuation which doesn't have the same number of cells that now exist. 1.998 +void 1.999 +nsTableRowGroupFrame::UndoContinuedRow(nsPresContext* aPresContext, 1.1000 + nsTableRowFrame* aRow) 1.1001 +{ 1.1002 + if (!aRow) return; // allow null aRow to avoid callers doing null checks 1.1003 + 1.1004 + // rowBefore was the prev-sibling of aRow's next-sibling before aRow was created 1.1005 + nsTableRowFrame* rowBefore = (nsTableRowFrame*)aRow->GetPrevInFlow(); 1.1006 + NS_PRECONDITION(mFrames.ContainsFrame(rowBefore), 1.1007 + "rowBefore not in our frame list?"); 1.1008 + 1.1009 + AutoFrameListPtr overflows(aPresContext, StealOverflowFrames()); 1.1010 + if (!rowBefore || !overflows || overflows->IsEmpty() || 1.1011 + overflows->FirstChild() != aRow) { 1.1012 + NS_ERROR("invalid continued row"); 1.1013 + return; 1.1014 + } 1.1015 + 1.1016 + // Destroy aRow, its cells, and their cell blocks. Cell blocks that have split 1.1017 + // will not have reflowed yet to pick up content from any overflow lines. 1.1018 + overflows->DestroyFrame(aRow); 1.1019 + 1.1020 + // Put the overflow rows into our child list 1.1021 + if (!overflows->IsEmpty()) { 1.1022 + mFrames.InsertFrames(nullptr, rowBefore, *overflows); 1.1023 + } 1.1024 +} 1.1025 + 1.1026 +static nsTableRowFrame* 1.1027 +GetRowBefore(nsTableRowFrame& aStartRow, 1.1028 + nsTableRowFrame& aRow) 1.1029 +{ 1.1030 + nsTableRowFrame* rowBefore = nullptr; 1.1031 + for (nsTableRowFrame* sib = &aStartRow; sib && (sib != &aRow); sib = sib->GetNextRow()) { 1.1032 + rowBefore = sib; 1.1033 + } 1.1034 + return rowBefore; 1.1035 +} 1.1036 + 1.1037 +nsresult 1.1038 +nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext, 1.1039 + nsHTMLReflowMetrics& aDesiredSize, 1.1040 + const nsHTMLReflowState& aReflowState, 1.1041 + nsTableFrame* aTableFrame, 1.1042 + nsReflowStatus& aStatus, 1.1043 + bool aRowForcedPageBreak) 1.1044 +{ 1.1045 + NS_PRECONDITION(aPresContext->IsPaginated(), "SplitRowGroup currently supports only paged media"); 1.1046 + 1.1047 + nsresult rv = NS_OK; 1.1048 + nsTableRowFrame* prevRowFrame = nullptr; 1.1049 + aDesiredSize.Height() = 0; 1.1050 + 1.1051 + nscoord availWidth = aReflowState.AvailableWidth(); 1.1052 + nscoord availHeight = aReflowState.AvailableHeight(); 1.1053 + 1.1054 + const bool borderCollapse = aTableFrame->IsBorderCollapse(); 1.1055 + nscoord cellSpacingY = aTableFrame->GetCellSpacingY(); 1.1056 + 1.1057 + // get the page height 1.1058 + nscoord pageHeight = aPresContext->GetPageSize().height; 1.1059 + NS_ASSERTION(pageHeight != NS_UNCONSTRAINEDSIZE, 1.1060 + "The table shouldn't be split when there should be space"); 1.1061 + 1.1062 + bool isTopOfPage = aReflowState.mFlags.mIsTopOfPage; 1.1063 + nsTableRowFrame* firstRowThisPage = GetFirstRow(); 1.1064 + 1.1065 + // Need to dirty the table's geometry, or else the row might skip 1.1066 + // reflowing its cell as an optimization. 1.1067 + aTableFrame->SetGeometryDirty(); 1.1068 + 1.1069 + // Walk each of the row frames looking for the first row frame that doesn't fit 1.1070 + // in the available space 1.1071 + for (nsTableRowFrame* rowFrame = firstRowThisPage; rowFrame; rowFrame = rowFrame->GetNextRow()) { 1.1072 + bool rowIsOnPage = true; 1.1073 + nsRect rowRect = rowFrame->GetRect(); 1.1074 + // See if the row fits on this page 1.1075 + if (rowRect.YMost() > availHeight) { 1.1076 + nsTableRowFrame* contRow = nullptr; 1.1077 + // Reflow the row in the availabe space and have it split if it is the 1st 1.1078 + // row (on the page) or there is at least 5% of the current page available 1.1079 + // XXX this 5% should be made a preference 1.1080 + if (!prevRowFrame || (availHeight - aDesiredSize.Height() > pageHeight / 20)) { 1.1081 + nsSize availSize(availWidth, std::max(availHeight - rowRect.y, 0)); 1.1082 + // don't let the available height exceed what CalculateRowHeights set for it 1.1083 + availSize.height = std::min(availSize.height, rowRect.height); 1.1084 + 1.1085 + nsHTMLReflowState rowReflowState(aPresContext, aReflowState, 1.1086 + rowFrame, availSize, 1.1087 + -1, -1, 1.1088 + nsHTMLReflowState::CALLER_WILL_INIT); 1.1089 + 1.1090 + InitChildReflowState(*aPresContext, borderCollapse, rowReflowState); 1.1091 + rowReflowState.mFlags.mIsTopOfPage = isTopOfPage; // set top of page 1.1092 + nsHTMLReflowMetrics rowMetrics(aReflowState); 1.1093 + 1.1094 + // Get the old size before we reflow. 1.1095 + nsRect oldRowRect = rowFrame->GetRect(); 1.1096 + nsRect oldRowVisualOverflow = rowFrame->GetVisualOverflowRect(); 1.1097 + 1.1098 + // Reflow the cell with the constrained height. A cell with rowspan >1 will get this 1.1099 + // reflow later during SplitSpanningCells. 1.1100 + rv = ReflowChild(rowFrame, aPresContext, rowMetrics, rowReflowState, 1.1101 + 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus); 1.1102 + if (NS_FAILED(rv)) return rv; 1.1103 + rowFrame->SetSize(nsSize(rowMetrics.Width(), rowMetrics.Height())); 1.1104 + rowFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED); 1.1105 + rowFrame->DidResize(); 1.1106 + 1.1107 + if (!aRowForcedPageBreak && !NS_FRAME_IS_FULLY_COMPLETE(aStatus) && 1.1108 + ShouldAvoidBreakInside(aReflowState)) { 1.1109 + aStatus = NS_INLINE_LINE_BREAK_BEFORE(); 1.1110 + break; 1.1111 + } 1.1112 + 1.1113 + nsTableFrame::InvalidateTableFrame(rowFrame, oldRowRect, 1.1114 + oldRowVisualOverflow, 1.1115 + false); 1.1116 + 1.1117 + if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { 1.1118 + // The row frame is incomplete and all of the rowspan 1 cells' block frames split 1.1119 + if ((rowMetrics.Height() <= rowReflowState.AvailableHeight()) || isTopOfPage) { 1.1120 + // The row stays on this page because either it split ok or we're on the top of page. 1.1121 + // If top of page and the height exceeded the avail height, then there will be data loss 1.1122 + NS_ASSERTION(rowMetrics.Height() <= rowReflowState.AvailableHeight(), 1.1123 + "data loss - incomplete row needed more height than available, on top of page"); 1.1124 + CreateContinuingRowFrame(*aPresContext, *rowFrame, (nsIFrame**)&contRow); 1.1125 + if (contRow) { 1.1126 + aDesiredSize.Height() += rowMetrics.Height(); 1.1127 + if (prevRowFrame) 1.1128 + aDesiredSize.Height() += cellSpacingY; 1.1129 + } 1.1130 + else return NS_ERROR_NULL_POINTER; 1.1131 + } 1.1132 + else { 1.1133 + // Put the row on the next page to give it more height 1.1134 + rowIsOnPage = false; 1.1135 + } 1.1136 + } 1.1137 + else { 1.1138 + // The row frame is complete because either (1) its minimum height is greater than the 1.1139 + // available height we gave it, or (2) it may have been given a larger height through 1.1140 + // style than its content, or (3) it contains a rowspan >1 cell which hasn't been 1.1141 + // reflowed with a constrained height yet (we will find out when SplitSpanningCells is 1.1142 + // called below) 1.1143 + if (rowMetrics.Height() > availSize.height || 1.1144 + (NS_INLINE_IS_BREAK_BEFORE(aStatus) && !aRowForcedPageBreak)) { 1.1145 + // cases (1) and (2) 1.1146 + if (isTopOfPage) { 1.1147 + // We're on top of the page, so keep the row on this page. There will be data loss. 1.1148 + // Push the row frame that follows 1.1149 + nsTableRowFrame* nextRowFrame = rowFrame->GetNextRow(); 1.1150 + if (nextRowFrame) { 1.1151 + aStatus = NS_FRAME_NOT_COMPLETE; 1.1152 + } 1.1153 + aDesiredSize.Height() += rowMetrics.Height(); 1.1154 + if (prevRowFrame) 1.1155 + aDesiredSize.Height() += cellSpacingY; 1.1156 + NS_WARNING("data loss - complete row needed more height than available, on top of page"); 1.1157 + } 1.1158 + else { 1.1159 + // We're not on top of the page, so put the row on the next page to give it more height 1.1160 + rowIsOnPage = false; 1.1161 + } 1.1162 + } 1.1163 + } 1.1164 + } //if (!prevRowFrame || (availHeight - aDesiredSize.Height() > pageHeight / 20)) 1.1165 + else { 1.1166 + // put the row on the next page to give it more height 1.1167 + rowIsOnPage = false; 1.1168 + } 1.1169 + 1.1170 + nsTableRowFrame* lastRowThisPage = rowFrame; 1.1171 + nscoord spanningRowBottom = availHeight; 1.1172 + if (!rowIsOnPage) { 1.1173 + NS_ASSERTION(!contRow, "We should not have created a continuation if none of this row fits"); 1.1174 + if (!aRowForcedPageBreak && ShouldAvoidBreakInside(aReflowState)) { 1.1175 + aStatus = NS_INLINE_LINE_BREAK_BEFORE(); 1.1176 + break; 1.1177 + } 1.1178 + if (prevRowFrame) { 1.1179 + spanningRowBottom = prevRowFrame->GetRect().YMost(); 1.1180 + lastRowThisPage = prevRowFrame; 1.1181 + isTopOfPage = (lastRowThisPage == firstRowThisPage) && aReflowState.mFlags.mIsTopOfPage; 1.1182 + aStatus = NS_FRAME_NOT_COMPLETE; 1.1183 + } 1.1184 + else { 1.1185 + // We can't push children, so let our parent reflow us again with more space 1.1186 + aDesiredSize.Height() = rowRect.YMost(); 1.1187 + aStatus = NS_FRAME_COMPLETE; 1.1188 + break; 1.1189 + } 1.1190 + } 1.1191 + // reflow the cells with rowspan >1 that occur on the page 1.1192 + 1.1193 + nsTableRowFrame* firstTruncatedRow; 1.1194 + nscoord yMost; 1.1195 + SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, *firstRowThisPage, 1.1196 + *lastRowThisPage, aReflowState.mFlags.mIsTopOfPage, spanningRowBottom, contRow, 1.1197 + firstTruncatedRow, yMost); 1.1198 + if (firstTruncatedRow) { 1.1199 + // A rowspan >1 cell did not fit (and could not split) in the space we gave it 1.1200 + if (firstTruncatedRow == firstRowThisPage) { 1.1201 + if (aReflowState.mFlags.mIsTopOfPage) { 1.1202 + NS_WARNING("data loss in a row spanned cell"); 1.1203 + } 1.1204 + else { 1.1205 + // We can't push children, so let our parent reflow us again with more space 1.1206 + aDesiredSize.Height() = rowRect.YMost(); 1.1207 + aStatus = NS_FRAME_COMPLETE; 1.1208 + UndoContinuedRow(aPresContext, contRow); 1.1209 + contRow = nullptr; 1.1210 + } 1.1211 + } 1.1212 + else { // (firstTruncatedRow != firstRowThisPage) 1.1213 + // Try to put firstTruncateRow on the next page 1.1214 + nsTableRowFrame* rowBefore = ::GetRowBefore(*firstRowThisPage, *firstTruncatedRow); 1.1215 + nscoord oldSpanningRowBottom = spanningRowBottom; 1.1216 + spanningRowBottom = rowBefore->GetRect().YMost(); 1.1217 + 1.1218 + UndoContinuedRow(aPresContext, contRow); 1.1219 + contRow = nullptr; 1.1220 + nsTableRowFrame* oldLastRowThisPage = lastRowThisPage; 1.1221 + lastRowThisPage = rowBefore; 1.1222 + aStatus = NS_FRAME_NOT_COMPLETE; 1.1223 + 1.1224 + // Call SplitSpanningCells again with rowBefore as the last row on the page 1.1225 + SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, 1.1226 + *firstRowThisPage, *rowBefore, aReflowState.mFlags.mIsTopOfPage, 1.1227 + spanningRowBottom, contRow, firstTruncatedRow, aDesiredSize.Height()); 1.1228 + if (firstTruncatedRow) { 1.1229 + if (aReflowState.mFlags.mIsTopOfPage) { 1.1230 + // We were better off with the 1st call to SplitSpanningCells, do it again 1.1231 + UndoContinuedRow(aPresContext, contRow); 1.1232 + contRow = nullptr; 1.1233 + lastRowThisPage = oldLastRowThisPage; 1.1234 + spanningRowBottom = oldSpanningRowBottom; 1.1235 + SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, *firstRowThisPage, 1.1236 + *lastRowThisPage, aReflowState.mFlags.mIsTopOfPage, spanningRowBottom, contRow, 1.1237 + firstTruncatedRow, aDesiredSize.Height()); 1.1238 + NS_WARNING("data loss in a row spanned cell"); 1.1239 + } 1.1240 + else { 1.1241 + // Let our parent reflow us again with more space 1.1242 + aDesiredSize.Height() = rowRect.YMost(); 1.1243 + aStatus = NS_FRAME_COMPLETE; 1.1244 + UndoContinuedRow(aPresContext, contRow); 1.1245 + contRow = nullptr; 1.1246 + } 1.1247 + } 1.1248 + } // if (firstTruncatedRow == firstRowThisPage) 1.1249 + } // if (firstTruncatedRow) 1.1250 + else { 1.1251 + aDesiredSize.Height() = std::max(aDesiredSize.Height(), yMost); 1.1252 + if (contRow) { 1.1253 + aStatus = NS_FRAME_NOT_COMPLETE; 1.1254 + } 1.1255 + } 1.1256 + if (NS_FRAME_IS_NOT_COMPLETE(aStatus) && !contRow) { 1.1257 + nsTableRowFrame* nextRow = lastRowThisPage->GetNextRow(); 1.1258 + if (nextRow) { 1.1259 + PushChildren(nextRow, lastRowThisPage); 1.1260 + } 1.1261 + } 1.1262 + break; 1.1263 + } // if (rowRect.YMost() > availHeight) 1.1264 + else { 1.1265 + aDesiredSize.Height() = rowRect.YMost(); 1.1266 + prevRowFrame = rowFrame; 1.1267 + // see if there is a page break after the row 1.1268 + nsTableRowFrame* nextRow = rowFrame->GetNextRow(); 1.1269 + if (nextRow && nsTableFrame::PageBreakAfter(rowFrame, nextRow)) { 1.1270 + PushChildren(nextRow, rowFrame); 1.1271 + aStatus = NS_FRAME_NOT_COMPLETE; 1.1272 + break; 1.1273 + } 1.1274 + } 1.1275 + // after the 1st row that has a height, we can't be on top 1.1276 + // of the page anymore. 1.1277 + isTopOfPage = isTopOfPage && rowRect.YMost() == 0; 1.1278 + } 1.1279 + return NS_OK; 1.1280 +} 1.1281 + 1.1282 +/** Layout the entire row group. 1.1283 + * This method stacks rows vertically according to HTML 4.0 rules. 1.1284 + * Rows are responsible for layout of their children. 1.1285 + */ 1.1286 +nsresult 1.1287 +nsTableRowGroupFrame::Reflow(nsPresContext* aPresContext, 1.1288 + nsHTMLReflowMetrics& aDesiredSize, 1.1289 + const nsHTMLReflowState& aReflowState, 1.1290 + nsReflowStatus& aStatus) 1.1291 +{ 1.1292 + DO_GLOBAL_REFLOW_COUNT("nsTableRowGroupFrame"); 1.1293 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); 1.1294 + 1.1295 + nsresult rv = NS_OK; 1.1296 + aStatus = NS_FRAME_COMPLETE; 1.1297 + 1.1298 + // Row geometry may be going to change so we need to invalidate any row cursor. 1.1299 + ClearRowCursor(); 1.1300 + 1.1301 + // see if a special height reflow needs to occur due to having a pct height 1.1302 + nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState); 1.1303 + 1.1304 + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); 1.1305 + nsRowGroupReflowState state(aReflowState, tableFrame); 1.1306 + const nsStyleVisibility* groupVis = StyleVisibility(); 1.1307 + bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible); 1.1308 + if (collapseGroup) { 1.1309 + tableFrame->SetNeedToCollapse(true); 1.1310 + } 1.1311 + 1.1312 + // Check for an overflow list 1.1313 + MoveOverflowToChildList(); 1.1314 + 1.1315 + // Reflow the existing frames. 1.1316 + bool splitDueToPageBreak = false; 1.1317 + rv = ReflowChildren(aPresContext, aDesiredSize, state, aStatus, 1.1318 + &splitDueToPageBreak); 1.1319 + 1.1320 + // See if all the frames fit. Do not try to split anything if we're 1.1321 + // not paginated ... we can't split across columns yet. 1.1322 + if (aReflowState.mFlags.mTableIsSplittable && 1.1323 + NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() && 1.1324 + (NS_FRAME_NOT_COMPLETE == aStatus || splitDueToPageBreak || 1.1325 + aDesiredSize.Height() > aReflowState.AvailableHeight())) { 1.1326 + // Nope, find a place to split the row group 1.1327 + bool specialReflow = (bool)aReflowState.mFlags.mSpecialHeightReflow; 1.1328 + ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = false; 1.1329 + 1.1330 + SplitRowGroup(aPresContext, aDesiredSize, aReflowState, tableFrame, aStatus, 1.1331 + splitDueToPageBreak); 1.1332 + 1.1333 + ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = specialReflow; 1.1334 + } 1.1335 + 1.1336 + // XXXmats The following is just bogus. We leave it here for now because 1.1337 + // ReflowChildren should pull up rows from our next-in-flow before returning 1.1338 + // a Complete status, but doesn't (bug 804888). 1.1339 + if (GetNextInFlow() && GetNextInFlow()->GetFirstPrincipalChild()) { 1.1340 + NS_FRAME_SET_INCOMPLETE(aStatus); 1.1341 + } 1.1342 + 1.1343 + SetHasStyleHeight((NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()) && 1.1344 + (aReflowState.ComputedHeight() > 0)); 1.1345 + 1.1346 + // just set our width to what was available. The table will calculate the width and not use our value. 1.1347 + aDesiredSize.Width() = aReflowState.AvailableWidth(); 1.1348 + 1.1349 + aDesiredSize.UnionOverflowAreasWithDesiredBounds(); 1.1350 + 1.1351 + // If our parent is in initial reflow, it'll handle invalidating our 1.1352 + // entire overflow rect. 1.1353 + if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) && 1.1354 + nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) { 1.1355 + InvalidateFrame(); 1.1356 + } 1.1357 + 1.1358 + FinishAndStoreOverflow(&aDesiredSize); 1.1359 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 1.1360 + return rv; 1.1361 +} 1.1362 + 1.1363 +bool 1.1364 +nsTableRowGroupFrame::UpdateOverflow() 1.1365 +{ 1.1366 + // Row cursor invariants depend on the visual overflow area of the rows, 1.1367 + // which may have changed, so we need to clear the cursor now. 1.1368 + ClearRowCursor(); 1.1369 + return nsContainerFrame::UpdateOverflow(); 1.1370 +} 1.1371 + 1.1372 +/* virtual */ void 1.1373 +nsTableRowGroupFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) 1.1374 +{ 1.1375 + nsContainerFrame::DidSetStyleContext(aOldStyleContext); 1.1376 + 1.1377 + if (!aOldStyleContext) //avoid this on init 1.1378 + return; 1.1379 + 1.1380 + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); 1.1381 + if (tableFrame->IsBorderCollapse() && 1.1382 + tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) { 1.1383 + nsIntRect damageArea(0, GetStartRowIndex(), tableFrame->GetColCount(), 1.1384 + GetRowCount()); 1.1385 + tableFrame->AddBCDamageArea(damageArea); 1.1386 + } 1.1387 +} 1.1388 + 1.1389 +nsresult 1.1390 +nsTableRowGroupFrame::AppendFrames(ChildListID aListID, 1.1391 + nsFrameList& aFrameList) 1.1392 +{ 1.1393 + NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); 1.1394 + 1.1395 + ClearRowCursor(); 1.1396 + 1.1397 + // collect the new row frames in an array 1.1398 + // XXXbz why are we doing the QI stuff? There shouldn't be any non-rows here. 1.1399 + nsAutoTArray<nsTableRowFrame*, 8> rows; 1.1400 + for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { 1.1401 + nsTableRowFrame *rowFrame = do_QueryFrame(e.get()); 1.1402 + NS_ASSERTION(rowFrame, "Unexpected frame; frame constructor screwed up"); 1.1403 + if (rowFrame) { 1.1404 + NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW == 1.1405 + e.get()->StyleDisplay()->mDisplay, 1.1406 + "wrong display type on rowframe"); 1.1407 + rows.AppendElement(rowFrame); 1.1408 + } 1.1409 + } 1.1410 + 1.1411 + int32_t rowIndex = GetRowCount(); 1.1412 + // Append the frames to the sibling chain 1.1413 + mFrames.AppendFrames(nullptr, aFrameList); 1.1414 + 1.1415 + if (rows.Length() > 0) { 1.1416 + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); 1.1417 + tableFrame->AppendRows(this, rowIndex, rows); 1.1418 + PresContext()->PresShell()-> 1.1419 + FrameNeedsReflow(this, nsIPresShell::eTreeChange, 1.1420 + NS_FRAME_HAS_DIRTY_CHILDREN); 1.1421 + tableFrame->SetGeometryDirty(); 1.1422 + } 1.1423 + 1.1424 + return NS_OK; 1.1425 +} 1.1426 + 1.1427 +nsresult 1.1428 +nsTableRowGroupFrame::InsertFrames(ChildListID aListID, 1.1429 + nsIFrame* aPrevFrame, 1.1430 + nsFrameList& aFrameList) 1.1431 +{ 1.1432 + NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); 1.1433 + NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, 1.1434 + "inserting after sibling frame with different parent"); 1.1435 + 1.1436 + ClearRowCursor(); 1.1437 + 1.1438 + // collect the new row frames in an array 1.1439 + // XXXbz why are we doing the QI stuff? There shouldn't be any non-rows here. 1.1440 + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); 1.1441 + nsTArray<nsTableRowFrame*> rows; 1.1442 + bool gotFirstRow = false; 1.1443 + for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { 1.1444 + nsTableRowFrame *rowFrame = do_QueryFrame(e.get()); 1.1445 + NS_ASSERTION(rowFrame, "Unexpected frame; frame constructor screwed up"); 1.1446 + if (rowFrame) { 1.1447 + NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW == 1.1448 + e.get()->StyleDisplay()->mDisplay, 1.1449 + "wrong display type on rowframe"); 1.1450 + rows.AppendElement(rowFrame); 1.1451 + if (!gotFirstRow) { 1.1452 + rowFrame->SetFirstInserted(true); 1.1453 + gotFirstRow = true; 1.1454 + tableFrame->SetRowInserted(true); 1.1455 + } 1.1456 + } 1.1457 + } 1.1458 + 1.1459 + int32_t startRowIndex = GetStartRowIndex(); 1.1460 + // Insert the frames in the sibling chain 1.1461 + mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList); 1.1462 + 1.1463 + int32_t numRows = rows.Length(); 1.1464 + if (numRows > 0) { 1.1465 + nsTableRowFrame* prevRow = (nsTableRowFrame *)nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame, nsGkAtoms::tableRowFrame); 1.1466 + int32_t rowIndex = (prevRow) ? prevRow->GetRowIndex() + 1 : startRowIndex; 1.1467 + tableFrame->InsertRows(this, rows, rowIndex, true); 1.1468 + 1.1469 + PresContext()->PresShell()-> 1.1470 + FrameNeedsReflow(this, nsIPresShell::eTreeChange, 1.1471 + NS_FRAME_HAS_DIRTY_CHILDREN); 1.1472 + tableFrame->SetGeometryDirty(); 1.1473 + } 1.1474 + return NS_OK; 1.1475 +} 1.1476 + 1.1477 +nsresult 1.1478 +nsTableRowGroupFrame::RemoveFrame(ChildListID aListID, 1.1479 + nsIFrame* aOldFrame) 1.1480 +{ 1.1481 + NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); 1.1482 + 1.1483 + ClearRowCursor(); 1.1484 + 1.1485 + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); 1.1486 + // XXX why are we doing the QI stuff? There shouldn't be any non-rows here. 1.1487 + nsTableRowFrame* rowFrame = do_QueryFrame(aOldFrame); 1.1488 + if (rowFrame) { 1.1489 + // remove the rows from the table (and flag a rebalance) 1.1490 + tableFrame->RemoveRows(*rowFrame, 1, true); 1.1491 + 1.1492 + PresContext()->PresShell()-> 1.1493 + FrameNeedsReflow(this, nsIPresShell::eTreeChange, 1.1494 + NS_FRAME_HAS_DIRTY_CHILDREN); 1.1495 + tableFrame->SetGeometryDirty(); 1.1496 + } 1.1497 + mFrames.DestroyFrame(aOldFrame); 1.1498 + 1.1499 + return NS_OK; 1.1500 +} 1.1501 + 1.1502 +/* virtual */ nsMargin 1.1503 +nsTableRowGroupFrame::GetUsedMargin() const 1.1504 +{ 1.1505 + return nsMargin(0,0,0,0); 1.1506 +} 1.1507 + 1.1508 +/* virtual */ nsMargin 1.1509 +nsTableRowGroupFrame::GetUsedBorder() const 1.1510 +{ 1.1511 + return nsMargin(0,0,0,0); 1.1512 +} 1.1513 + 1.1514 +/* virtual */ nsMargin 1.1515 +nsTableRowGroupFrame::GetUsedPadding() const 1.1516 +{ 1.1517 + return nsMargin(0,0,0,0); 1.1518 +} 1.1519 + 1.1520 +nscoord 1.1521 +nsTableRowGroupFrame::GetHeightBasis(const nsHTMLReflowState& aReflowState) 1.1522 +{ 1.1523 + nscoord result = 0; 1.1524 + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); 1.1525 + if ((aReflowState.ComputedHeight() > 0) && (aReflowState.ComputedHeight() < NS_UNCONSTRAINEDSIZE)) { 1.1526 + nscoord cellSpacing = std::max(0, GetRowCount() - 1) * tableFrame->GetCellSpacingY(); 1.1527 + result = aReflowState.ComputedHeight() - cellSpacing; 1.1528 + } 1.1529 + else { 1.1530 + const nsHTMLReflowState* parentRS = aReflowState.parentReflowState; 1.1531 + if (parentRS && (tableFrame != parentRS->frame)) { 1.1532 + parentRS = parentRS->parentReflowState; 1.1533 + } 1.1534 + if (parentRS && (tableFrame == parentRS->frame) && 1.1535 + (parentRS->ComputedHeight() > 0) && (parentRS->ComputedHeight() < NS_UNCONSTRAINEDSIZE)) { 1.1536 + nscoord cellSpacing = std::max(0, tableFrame->GetRowCount() + 1) * tableFrame->GetCellSpacingY(); 1.1537 + result = parentRS->ComputedHeight() - cellSpacing; 1.1538 + } 1.1539 + } 1.1540 + 1.1541 + return result; 1.1542 +} 1.1543 + 1.1544 +bool 1.1545 +nsTableRowGroupFrame::IsSimpleRowFrame(nsTableFrame* aTableFrame, 1.1546 + nsIFrame* aFrame) 1.1547 +{ 1.1548 + // Make sure it's a row frame and not a row group frame 1.1549 + nsTableRowFrame *rowFrame = do_QueryFrame(aFrame); 1.1550 + if (rowFrame) { 1.1551 + int32_t rowIndex = rowFrame->GetRowIndex(); 1.1552 + 1.1553 + // It's a simple row frame if there are no cells that span into or 1.1554 + // across the row 1.1555 + int32_t numEffCols = aTableFrame->GetEffectiveColCount(); 1.1556 + if (!aTableFrame->RowIsSpannedInto(rowIndex, numEffCols) && 1.1557 + !aTableFrame->RowHasSpanningCells(rowIndex, numEffCols)) { 1.1558 + return true; 1.1559 + } 1.1560 + } 1.1561 + 1.1562 + return false; 1.1563 +} 1.1564 + 1.1565 +nsIAtom* 1.1566 +nsTableRowGroupFrame::GetType() const 1.1567 +{ 1.1568 + return nsGkAtoms::tableRowGroupFrame; 1.1569 +} 1.1570 + 1.1571 +/** find page break before the first row **/ 1.1572 +bool 1.1573 +nsTableRowGroupFrame::HasInternalBreakBefore() const 1.1574 +{ 1.1575 + nsIFrame* firstChild = mFrames.FirstChild(); 1.1576 + if (!firstChild) 1.1577 + return false; 1.1578 + return firstChild->StyleDisplay()->mBreakBefore; 1.1579 +} 1.1580 + 1.1581 +/** find page break after the last row **/ 1.1582 +bool 1.1583 +nsTableRowGroupFrame::HasInternalBreakAfter() const 1.1584 +{ 1.1585 + nsIFrame* lastChild = mFrames.LastChild(); 1.1586 + if (!lastChild) 1.1587 + return false; 1.1588 + return lastChild->StyleDisplay()->mBreakAfter; 1.1589 +} 1.1590 +/* ----- global methods ----- */ 1.1591 + 1.1592 +nsIFrame* 1.1593 +NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.1594 +{ 1.1595 + return new (aPresShell) nsTableRowGroupFrame(aContext); 1.1596 +} 1.1597 + 1.1598 +NS_IMPL_FRAMEARENA_HELPERS(nsTableRowGroupFrame) 1.1599 + 1.1600 +#ifdef DEBUG_FRAME_DUMP 1.1601 +nsresult 1.1602 +nsTableRowGroupFrame::GetFrameName(nsAString& aResult) const 1.1603 +{ 1.1604 + return MakeFrameName(NS_LITERAL_STRING("TableRowGroup"), aResult); 1.1605 +} 1.1606 +#endif 1.1607 + 1.1608 +nsMargin* 1.1609 +nsTableRowGroupFrame::GetBCBorderWidth(nsMargin& aBorder) 1.1610 +{ 1.1611 + aBorder.left = aBorder.right = aBorder.top = aBorder.bottom = 0; 1.1612 + 1.1613 + nsTableRowFrame* firstRowFrame = nullptr; 1.1614 + nsTableRowFrame* lastRowFrame = nullptr; 1.1615 + for (nsTableRowFrame* rowFrame = GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) { 1.1616 + if (!firstRowFrame) { 1.1617 + firstRowFrame = rowFrame; 1.1618 + } 1.1619 + lastRowFrame = rowFrame; 1.1620 + } 1.1621 + if (firstRowFrame) { 1.1622 + aBorder.top = nsPresContext::CSSPixelsToAppUnits(firstRowFrame->GetTopBCBorderWidth()); 1.1623 + aBorder.bottom = nsPresContext::CSSPixelsToAppUnits(lastRowFrame->GetBottomBCBorderWidth()); 1.1624 + } 1.1625 + 1.1626 + return &aBorder; 1.1627 +} 1.1628 + 1.1629 +void nsTableRowGroupFrame::SetContinuousBCBorderWidth(uint8_t aForSide, 1.1630 + BCPixelSize aPixelValue) 1.1631 +{ 1.1632 + switch (aForSide) { 1.1633 + case NS_SIDE_RIGHT: 1.1634 + mRightContBorderWidth = aPixelValue; 1.1635 + return; 1.1636 + case NS_SIDE_BOTTOM: 1.1637 + mBottomContBorderWidth = aPixelValue; 1.1638 + return; 1.1639 + case NS_SIDE_LEFT: 1.1640 + mLeftContBorderWidth = aPixelValue; 1.1641 + return; 1.1642 + default: 1.1643 + NS_ERROR("invalid NS_SIDE argument"); 1.1644 + } 1.1645 +} 1.1646 + 1.1647 +//nsILineIterator methods 1.1648 +int32_t 1.1649 +nsTableRowGroupFrame::GetNumLines() 1.1650 +{ 1.1651 + return GetRowCount(); 1.1652 +} 1.1653 + 1.1654 +bool 1.1655 +nsTableRowGroupFrame::GetDirection() 1.1656 +{ 1.1657 + nsTableFrame* table = nsTableFrame::GetTableFrame(this); 1.1658 + return (NS_STYLE_DIRECTION_RTL == 1.1659 + table->StyleVisibility()->mDirection); 1.1660 +} 1.1661 + 1.1662 +NS_IMETHODIMP 1.1663 +nsTableRowGroupFrame::GetLine(int32_t aLineNumber, 1.1664 + nsIFrame** aFirstFrameOnLine, 1.1665 + int32_t* aNumFramesOnLine, 1.1666 + nsRect& aLineBounds, 1.1667 + uint32_t* aLineFlags) 1.1668 +{ 1.1669 + NS_ENSURE_ARG_POINTER(aFirstFrameOnLine); 1.1670 + NS_ENSURE_ARG_POINTER(aNumFramesOnLine); 1.1671 + NS_ENSURE_ARG_POINTER(aLineFlags); 1.1672 + 1.1673 + nsTableFrame* table = nsTableFrame::GetTableFrame(this); 1.1674 + nsTableCellMap* cellMap = table->GetCellMap(); 1.1675 + 1.1676 + *aLineFlags = 0; 1.1677 + *aFirstFrameOnLine = nullptr; 1.1678 + *aNumFramesOnLine = 0; 1.1679 + aLineBounds.SetRect(0, 0, 0, 0); 1.1680 + 1.1681 + if ((aLineNumber < 0) || (aLineNumber >= GetRowCount())) { 1.1682 + return NS_OK; 1.1683 + } 1.1684 + aLineNumber += GetStartRowIndex(); 1.1685 + 1.1686 + *aNumFramesOnLine = cellMap->GetNumCellsOriginatingInRow(aLineNumber); 1.1687 + if (*aNumFramesOnLine == 0) { 1.1688 + return NS_OK; 1.1689 + } 1.1690 + int32_t colCount = table->GetColCount(); 1.1691 + for (int32_t i = 0; i < colCount; i++) { 1.1692 + CellData* data = cellMap->GetDataAt(aLineNumber, i); 1.1693 + if (data && data->IsOrig()) { 1.1694 + *aFirstFrameOnLine = (nsIFrame*)data->GetCellFrame(); 1.1695 + nsIFrame* parent = (*aFirstFrameOnLine)->GetParent(); 1.1696 + aLineBounds = parent->GetRect(); 1.1697 + return NS_OK; 1.1698 + } 1.1699 + } 1.1700 + NS_ERROR("cellmap is lying"); 1.1701 + return NS_ERROR_FAILURE; 1.1702 +} 1.1703 + 1.1704 +int32_t 1.1705 +nsTableRowGroupFrame::FindLineContaining(nsIFrame* aFrame, int32_t aStartLine) 1.1706 +{ 1.1707 + NS_ENSURE_TRUE(aFrame, -1); 1.1708 + 1.1709 + nsTableRowFrame *rowFrame = do_QueryFrame(aFrame); 1.1710 + NS_ASSERTION(rowFrame, "RowGroup contains a frame that is not a row"); 1.1711 + 1.1712 + int32_t rowIndexInGroup = rowFrame->GetRowIndex() - GetStartRowIndex(); 1.1713 + 1.1714 + return rowIndexInGroup >= aStartLine ? rowIndexInGroup : -1; 1.1715 +} 1.1716 + 1.1717 +NS_IMETHODIMP 1.1718 +nsTableRowGroupFrame::CheckLineOrder(int32_t aLine, 1.1719 + bool *aIsReordered, 1.1720 + nsIFrame **aFirstVisual, 1.1721 + nsIFrame **aLastVisual) 1.1722 +{ 1.1723 + *aIsReordered = false; 1.1724 + *aFirstVisual = nullptr; 1.1725 + *aLastVisual = nullptr; 1.1726 + return NS_OK; 1.1727 +} 1.1728 + 1.1729 +NS_IMETHODIMP 1.1730 +nsTableRowGroupFrame::FindFrameAt(int32_t aLineNumber, 1.1731 + nscoord aX, 1.1732 + nsIFrame** aFrameFound, 1.1733 + bool* aXIsBeforeFirstFrame, 1.1734 + bool* aXIsAfterLastFrame) 1.1735 +{ 1.1736 + nsTableFrame* table = nsTableFrame::GetTableFrame(this); 1.1737 + nsTableCellMap* cellMap = table->GetCellMap(); 1.1738 + 1.1739 + *aFrameFound = nullptr; 1.1740 + *aXIsBeforeFirstFrame = true; 1.1741 + *aXIsAfterLastFrame = false; 1.1742 + 1.1743 + aLineNumber += GetStartRowIndex(); 1.1744 + int32_t numCells = cellMap->GetNumCellsOriginatingInRow(aLineNumber); 1.1745 + if (numCells == 0) { 1.1746 + return NS_OK; 1.1747 + } 1.1748 + 1.1749 + nsIFrame* frame = nullptr; 1.1750 + int32_t colCount = table->GetColCount(); 1.1751 + for (int32_t i = 0; i < colCount; i++) { 1.1752 + CellData* data = cellMap->GetDataAt(aLineNumber, i); 1.1753 + if (data && data->IsOrig()) { 1.1754 + frame = (nsIFrame*)data->GetCellFrame(); 1.1755 + break; 1.1756 + } 1.1757 + } 1.1758 + NS_ASSERTION(frame, "cellmap is lying"); 1.1759 + bool isRTL = (NS_STYLE_DIRECTION_RTL == 1.1760 + table->StyleVisibility()->mDirection); 1.1761 + 1.1762 + nsIFrame* closestFromLeft = nullptr; 1.1763 + nsIFrame* closestFromRight = nullptr; 1.1764 + int32_t n = numCells; 1.1765 + nsIFrame* firstFrame = frame; 1.1766 + while (n--) { 1.1767 + nsRect rect = frame->GetRect(); 1.1768 + if (rect.width > 0) { 1.1769 + // If aX is inside this frame - this is it 1.1770 + if (rect.x <= aX && rect.XMost() > aX) { 1.1771 + closestFromLeft = closestFromRight = frame; 1.1772 + break; 1.1773 + } 1.1774 + if (rect.x < aX) { 1.1775 + if (!closestFromLeft || 1.1776 + rect.XMost() > closestFromLeft->GetRect().XMost()) 1.1777 + closestFromLeft = frame; 1.1778 + } 1.1779 + else { 1.1780 + if (!closestFromRight || 1.1781 + rect.x < closestFromRight->GetRect().x) 1.1782 + closestFromRight = frame; 1.1783 + } 1.1784 + } 1.1785 + frame = frame->GetNextSibling(); 1.1786 + } 1.1787 + if (!closestFromLeft && !closestFromRight) { 1.1788 + // All frames were zero-width. Just take the first one. 1.1789 + closestFromLeft = closestFromRight = firstFrame; 1.1790 + } 1.1791 + *aXIsBeforeFirstFrame = isRTL ? !closestFromRight : !closestFromLeft; 1.1792 + *aXIsAfterLastFrame = isRTL ? !closestFromLeft : !closestFromRight; 1.1793 + if (closestFromLeft == closestFromRight) { 1.1794 + *aFrameFound = closestFromLeft; 1.1795 + } 1.1796 + else if (!closestFromLeft) { 1.1797 + *aFrameFound = closestFromRight; 1.1798 + } 1.1799 + else if (!closestFromRight) { 1.1800 + *aFrameFound = closestFromLeft; 1.1801 + } 1.1802 + else { // we're between two frames 1.1803 + nscoord delta = closestFromRight->GetRect().x - 1.1804 + closestFromLeft->GetRect().XMost(); 1.1805 + if (aX < closestFromLeft->GetRect().XMost() + delta/2) 1.1806 + *aFrameFound = closestFromLeft; 1.1807 + else 1.1808 + *aFrameFound = closestFromRight; 1.1809 + } 1.1810 + return NS_OK; 1.1811 +} 1.1812 + 1.1813 +NS_IMETHODIMP 1.1814 +nsTableRowGroupFrame::GetNextSiblingOnLine(nsIFrame*& aFrame, 1.1815 + int32_t aLineNumber) 1.1816 +{ 1.1817 + NS_ENSURE_ARG_POINTER(aFrame); 1.1818 + aFrame = aFrame->GetNextSibling(); 1.1819 + return NS_OK; 1.1820 +} 1.1821 + 1.1822 +//end nsLineIterator methods 1.1823 + 1.1824 +static void 1.1825 +DestroyFrameCursorData(void* aPropertyValue) 1.1826 +{ 1.1827 + delete static_cast<nsTableRowGroupFrame::FrameCursorData*>(aPropertyValue); 1.1828 +} 1.1829 + 1.1830 +NS_DECLARE_FRAME_PROPERTY(RowCursorProperty, DestroyFrameCursorData) 1.1831 + 1.1832 +void 1.1833 +nsTableRowGroupFrame::ClearRowCursor() 1.1834 +{ 1.1835 + if (!(GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR)) 1.1836 + return; 1.1837 + 1.1838 + RemoveStateBits(NS_ROWGROUP_HAS_ROW_CURSOR); 1.1839 + Properties().Delete(RowCursorProperty()); 1.1840 +} 1.1841 + 1.1842 +nsTableRowGroupFrame::FrameCursorData* 1.1843 +nsTableRowGroupFrame::SetupRowCursor() 1.1844 +{ 1.1845 + if (GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR) { 1.1846 + // We already have a valid row cursor. Don't waste time rebuilding it. 1.1847 + return nullptr; 1.1848 + } 1.1849 + 1.1850 + nsIFrame* f = mFrames.FirstChild(); 1.1851 + int32_t count; 1.1852 + for (count = 0; f && count < MIN_ROWS_NEEDING_CURSOR; ++count) { 1.1853 + f = f->GetNextSibling(); 1.1854 + } 1.1855 + if (!f) { 1.1856 + // Less than MIN_ROWS_NEEDING_CURSOR rows, so just don't bother 1.1857 + return nullptr; 1.1858 + } 1.1859 + 1.1860 + FrameCursorData* data = new FrameCursorData(); 1.1861 + if (!data) 1.1862 + return nullptr; 1.1863 + Properties().Set(RowCursorProperty(), data); 1.1864 + AddStateBits(NS_ROWGROUP_HAS_ROW_CURSOR); 1.1865 + return data; 1.1866 +} 1.1867 + 1.1868 +nsIFrame* 1.1869 +nsTableRowGroupFrame::GetFirstRowContaining(nscoord aY, nscoord* aOverflowAbove) 1.1870 +{ 1.1871 + if (!(GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR)) 1.1872 + return nullptr; 1.1873 + 1.1874 + FrameCursorData* property = static_cast<FrameCursorData*> 1.1875 + (Properties().Get(RowCursorProperty())); 1.1876 + uint32_t cursorIndex = property->mCursorIndex; 1.1877 + uint32_t frameCount = property->mFrames.Length(); 1.1878 + if (cursorIndex >= frameCount) 1.1879 + return nullptr; 1.1880 + nsIFrame* cursorFrame = property->mFrames[cursorIndex]; 1.1881 + 1.1882 + // The cursor's frame list excludes frames with empty overflow-area, so 1.1883 + // we don't need to check that here. 1.1884 + 1.1885 + // We use property->mOverflowBelow here instead of computing the frame's 1.1886 + // true overflowArea.YMost(), because it is essential for the thresholds 1.1887 + // to form a monotonically increasing sequence. Otherwise we would break 1.1888 + // encountering a row whose overflowArea.YMost() is <= aY but which has 1.1889 + // a row above it containing cell(s) that span to include aY. 1.1890 + while (cursorIndex > 0 && 1.1891 + cursorFrame->GetRect().YMost() + property->mOverflowBelow > aY) { 1.1892 + --cursorIndex; 1.1893 + cursorFrame = property->mFrames[cursorIndex]; 1.1894 + } 1.1895 + while (cursorIndex + 1 < frameCount && 1.1896 + cursorFrame->GetRect().YMost() + property->mOverflowBelow <= aY) { 1.1897 + ++cursorIndex; 1.1898 + cursorFrame = property->mFrames[cursorIndex]; 1.1899 + } 1.1900 + 1.1901 + property->mCursorIndex = cursorIndex; 1.1902 + *aOverflowAbove = property->mOverflowAbove; 1.1903 + return cursorFrame; 1.1904 +} 1.1905 + 1.1906 +bool 1.1907 +nsTableRowGroupFrame::FrameCursorData::AppendFrame(nsIFrame* aFrame) 1.1908 +{ 1.1909 + nsRect overflowRect = aFrame->GetVisualOverflowRect(); 1.1910 + if (overflowRect.IsEmpty()) 1.1911 + return true; 1.1912 + nscoord overflowAbove = -overflowRect.y; 1.1913 + nscoord overflowBelow = overflowRect.YMost() - aFrame->GetSize().height; 1.1914 + mOverflowAbove = std::max(mOverflowAbove, overflowAbove); 1.1915 + mOverflowBelow = std::max(mOverflowBelow, overflowBelow); 1.1916 + return mFrames.AppendElement(aFrame) != nullptr; 1.1917 +} 1.1918 + 1.1919 +void 1.1920 +nsTableRowGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey) 1.1921 +{ 1.1922 + nsIFrame::InvalidateFrame(aDisplayItemKey); 1.1923 + GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey); 1.1924 +} 1.1925 + 1.1926 +void 1.1927 +nsTableRowGroupFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey) 1.1928 +{ 1.1929 + nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey); 1.1930 + // If we have filters applied that would affects our bounds, then 1.1931 + // we get an inactive layer created and this is computed 1.1932 + // within FrameLayerBuilder 1.1933 + GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey); 1.1934 +}