Fri, 16 Jan 2015 04:50:19 +0100
Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | #include "nsCOMPtr.h" |
michael@0 | 6 | #include "nsTableRowGroupFrame.h" |
michael@0 | 7 | #include "nsTableRowFrame.h" |
michael@0 | 8 | #include "nsTableFrame.h" |
michael@0 | 9 | #include "nsTableCellFrame.h" |
michael@0 | 10 | #include "nsPresContext.h" |
michael@0 | 11 | #include "nsStyleContext.h" |
michael@0 | 12 | #include "nsStyleConsts.h" |
michael@0 | 13 | #include "nsIContent.h" |
michael@0 | 14 | #include "nsGkAtoms.h" |
michael@0 | 15 | #include "nsIPresShell.h" |
michael@0 | 16 | #include "nsCSSRendering.h" |
michael@0 | 17 | #include "nsHTMLParts.h" |
michael@0 | 18 | #include "nsCSSFrameConstructor.h" |
michael@0 | 19 | #include "nsDisplayList.h" |
michael@0 | 20 | |
michael@0 | 21 | #include "nsCellMap.h"//table cell navigation |
michael@0 | 22 | #include <algorithm> |
michael@0 | 23 | |
michael@0 | 24 | using namespace mozilla; |
michael@0 | 25 | using namespace mozilla::layout; |
michael@0 | 26 | |
michael@0 | 27 | nsTableRowGroupFrame::nsTableRowGroupFrame(nsStyleContext* aContext): |
michael@0 | 28 | nsContainerFrame(aContext) |
michael@0 | 29 | { |
michael@0 | 30 | SetRepeatable(false); |
michael@0 | 31 | } |
michael@0 | 32 | |
michael@0 | 33 | nsTableRowGroupFrame::~nsTableRowGroupFrame() |
michael@0 | 34 | { |
michael@0 | 35 | } |
michael@0 | 36 | |
michael@0 | 37 | void |
michael@0 | 38 | nsTableRowGroupFrame::DestroyFrom(nsIFrame* aDestructRoot) |
michael@0 | 39 | { |
michael@0 | 40 | if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) { |
michael@0 | 41 | nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot); |
michael@0 | 42 | } |
michael@0 | 43 | |
michael@0 | 44 | nsContainerFrame::DestroyFrom(aDestructRoot); |
michael@0 | 45 | } |
michael@0 | 46 | |
michael@0 | 47 | NS_QUERYFRAME_HEAD(nsTableRowGroupFrame) |
michael@0 | 48 | NS_QUERYFRAME_ENTRY(nsTableRowGroupFrame) |
michael@0 | 49 | NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) |
michael@0 | 50 | |
michael@0 | 51 | int32_t |
michael@0 | 52 | nsTableRowGroupFrame::GetRowCount() |
michael@0 | 53 | { |
michael@0 | 54 | #ifdef DEBUG |
michael@0 | 55 | for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { |
michael@0 | 56 | NS_ASSERTION(e.get()->StyleDisplay()->mDisplay == |
michael@0 | 57 | NS_STYLE_DISPLAY_TABLE_ROW, |
michael@0 | 58 | "Unexpected display"); |
michael@0 | 59 | NS_ASSERTION(e.get()->GetType() == nsGkAtoms::tableRowFrame, |
michael@0 | 60 | "Unexpected frame type"); |
michael@0 | 61 | } |
michael@0 | 62 | #endif |
michael@0 | 63 | |
michael@0 | 64 | return mFrames.GetLength(); |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | int32_t nsTableRowGroupFrame::GetStartRowIndex() |
michael@0 | 68 | { |
michael@0 | 69 | int32_t result = -1; |
michael@0 | 70 | if (mFrames.NotEmpty()) { |
michael@0 | 71 | NS_ASSERTION(mFrames.FirstChild()->GetType() == nsGkAtoms::tableRowFrame, |
michael@0 | 72 | "Unexpected frame type"); |
michael@0 | 73 | result = static_cast<nsTableRowFrame*>(mFrames.FirstChild())->GetRowIndex(); |
michael@0 | 74 | } |
michael@0 | 75 | // if the row group doesn't have any children, get it the hard way |
michael@0 | 76 | if (-1 == result) { |
michael@0 | 77 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 78 | return tableFrame->GetStartRowIndex(this); |
michael@0 | 79 | } |
michael@0 | 80 | |
michael@0 | 81 | return result; |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | void nsTableRowGroupFrame::AdjustRowIndices(int32_t aRowIndex, |
michael@0 | 85 | int32_t anAdjustment) |
michael@0 | 86 | { |
michael@0 | 87 | nsIFrame* rowFrame = GetFirstPrincipalChild(); |
michael@0 | 88 | for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) { |
michael@0 | 89 | if (NS_STYLE_DISPLAY_TABLE_ROW==rowFrame->StyleDisplay()->mDisplay) { |
michael@0 | 90 | int32_t index = ((nsTableRowFrame*)rowFrame)->GetRowIndex(); |
michael@0 | 91 | if (index >= aRowIndex) |
michael@0 | 92 | ((nsTableRowFrame *)rowFrame)->SetRowIndex(index+anAdjustment); |
michael@0 | 93 | } |
michael@0 | 94 | } |
michael@0 | 95 | } |
michael@0 | 96 | nsresult |
michael@0 | 97 | nsTableRowGroupFrame::InitRepeatedFrame(nsPresContext* aPresContext, |
michael@0 | 98 | nsTableRowGroupFrame* aHeaderFooterFrame) |
michael@0 | 99 | { |
michael@0 | 100 | nsTableRowFrame* copyRowFrame = GetFirstRow(); |
michael@0 | 101 | nsTableRowFrame* originalRowFrame = aHeaderFooterFrame->GetFirstRow(); |
michael@0 | 102 | AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP); |
michael@0 | 103 | while (copyRowFrame && originalRowFrame) { |
michael@0 | 104 | copyRowFrame->AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP); |
michael@0 | 105 | int rowIndex = originalRowFrame->GetRowIndex(); |
michael@0 | 106 | copyRowFrame->SetRowIndex(rowIndex); |
michael@0 | 107 | |
michael@0 | 108 | // For each table cell frame set its column index |
michael@0 | 109 | nsTableCellFrame* originalCellFrame = originalRowFrame->GetFirstCell(); |
michael@0 | 110 | nsTableCellFrame* copyCellFrame = copyRowFrame->GetFirstCell(); |
michael@0 | 111 | while (copyCellFrame && originalCellFrame) { |
michael@0 | 112 | NS_ASSERTION(originalCellFrame->GetContent() == copyCellFrame->GetContent(), |
michael@0 | 113 | "cell frames have different content"); |
michael@0 | 114 | int32_t colIndex; |
michael@0 | 115 | originalCellFrame->GetColIndex(colIndex); |
michael@0 | 116 | copyCellFrame->SetColIndex(colIndex); |
michael@0 | 117 | |
michael@0 | 118 | // Move to the next cell frame |
michael@0 | 119 | copyCellFrame = copyCellFrame->GetNextCell(); |
michael@0 | 120 | originalCellFrame = originalCellFrame->GetNextCell(); |
michael@0 | 121 | } |
michael@0 | 122 | |
michael@0 | 123 | // Move to the next row frame |
michael@0 | 124 | originalRowFrame = originalRowFrame->GetNextRow(); |
michael@0 | 125 | copyRowFrame = copyRowFrame->GetNextRow(); |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | return NS_OK; |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | /** |
michael@0 | 132 | * We need a custom display item for table row backgrounds. This is only used |
michael@0 | 133 | * when the table row is the root of a stacking context (e.g., has 'opacity'). |
michael@0 | 134 | * Table row backgrounds can extend beyond the row frame bounds, when |
michael@0 | 135 | * the row contains row-spanning cells. |
michael@0 | 136 | */ |
michael@0 | 137 | class nsDisplayTableRowGroupBackground : public nsDisplayTableItem { |
michael@0 | 138 | public: |
michael@0 | 139 | nsDisplayTableRowGroupBackground(nsDisplayListBuilder* aBuilder, |
michael@0 | 140 | nsTableRowGroupFrame* aFrame) : |
michael@0 | 141 | nsDisplayTableItem(aBuilder, aFrame) { |
michael@0 | 142 | MOZ_COUNT_CTOR(nsDisplayTableRowGroupBackground); |
michael@0 | 143 | } |
michael@0 | 144 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 145 | virtual ~nsDisplayTableRowGroupBackground() { |
michael@0 | 146 | MOZ_COUNT_DTOR(nsDisplayTableRowGroupBackground); |
michael@0 | 147 | } |
michael@0 | 148 | #endif |
michael@0 | 149 | |
michael@0 | 150 | virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 151 | const nsDisplayItemGeometry* aGeometry, |
michael@0 | 152 | nsRegion *aInvalidRegion) MOZ_OVERRIDE; |
michael@0 | 153 | virtual void Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 154 | nsRenderingContext* aCtx) MOZ_OVERRIDE; |
michael@0 | 155 | |
michael@0 | 156 | NS_DISPLAY_DECL_NAME("TableRowGroupBackground", TYPE_TABLE_ROW_GROUP_BACKGROUND) |
michael@0 | 157 | }; |
michael@0 | 158 | |
michael@0 | 159 | void |
michael@0 | 160 | nsDisplayTableRowGroupBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 161 | const nsDisplayItemGeometry* aGeometry, |
michael@0 | 162 | nsRegion *aInvalidRegion) |
michael@0 | 163 | { |
michael@0 | 164 | if (aBuilder->ShouldSyncDecodeImages()) { |
michael@0 | 165 | if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling())) { |
michael@0 | 166 | bool snap; |
michael@0 | 167 | aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); |
michael@0 | 168 | } |
michael@0 | 169 | } |
michael@0 | 170 | |
michael@0 | 171 | nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); |
michael@0 | 172 | } |
michael@0 | 173 | |
michael@0 | 174 | void |
michael@0 | 175 | nsDisplayTableRowGroupBackground::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 176 | nsRenderingContext* aCtx) |
michael@0 | 177 | { |
michael@0 | 178 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(mFrame); |
michael@0 | 179 | TableBackgroundPainter painter(tableFrame, |
michael@0 | 180 | TableBackgroundPainter::eOrigin_TableRowGroup, |
michael@0 | 181 | mFrame->PresContext(), *aCtx, |
michael@0 | 182 | mVisibleRect, ToReferenceFrame(), |
michael@0 | 183 | aBuilder->GetBackgroundPaintFlags()); |
michael@0 | 184 | painter.PaintRowGroup(static_cast<nsTableRowGroupFrame*>(mFrame)); |
michael@0 | 185 | } |
michael@0 | 186 | |
michael@0 | 187 | // Handle the child-traversal part of DisplayGenericTablePart |
michael@0 | 188 | static void |
michael@0 | 189 | DisplayRows(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, |
michael@0 | 190 | const nsRect& aDirtyRect, const nsDisplayListSet& aLists) |
michael@0 | 191 | { |
michael@0 | 192 | nscoord overflowAbove; |
michael@0 | 193 | nsTableRowGroupFrame* f = static_cast<nsTableRowGroupFrame*>(aFrame); |
michael@0 | 194 | // Don't try to use the row cursor if we have to descend into placeholders; |
michael@0 | 195 | // we might have rows containing placeholders, where the row's overflow |
michael@0 | 196 | // area doesn't intersect the dirty rect but we need to descend into the row |
michael@0 | 197 | // to see out of flows. |
michael@0 | 198 | // Note that we really want to check ShouldDescendIntoFrame for all |
michael@0 | 199 | // the rows in |f|, but that's exactly what we're trying to avoid, so we |
michael@0 | 200 | // approximate it by checking it for |f|: if it's true for any row |
michael@0 | 201 | // in |f| then it's true for |f| itself. |
michael@0 | 202 | nsIFrame* kid = aBuilder->ShouldDescendIntoFrame(f) ? |
michael@0 | 203 | nullptr : f->GetFirstRowContaining(aDirtyRect.y, &overflowAbove); |
michael@0 | 204 | |
michael@0 | 205 | if (kid) { |
michael@0 | 206 | // have a cursor, use it |
michael@0 | 207 | while (kid) { |
michael@0 | 208 | if (kid->GetRect().y - overflowAbove >= aDirtyRect.YMost()) |
michael@0 | 209 | break; |
michael@0 | 210 | f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); |
michael@0 | 211 | kid = kid->GetNextSibling(); |
michael@0 | 212 | } |
michael@0 | 213 | return; |
michael@0 | 214 | } |
michael@0 | 215 | |
michael@0 | 216 | // No cursor. Traverse children the hard way and build a cursor while we're at it |
michael@0 | 217 | nsTableRowGroupFrame::FrameCursorData* cursor = f->SetupRowCursor(); |
michael@0 | 218 | kid = f->GetFirstPrincipalChild(); |
michael@0 | 219 | while (kid) { |
michael@0 | 220 | f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); |
michael@0 | 221 | |
michael@0 | 222 | if (cursor) { |
michael@0 | 223 | if (!cursor->AppendFrame(kid)) { |
michael@0 | 224 | f->ClearRowCursor(); |
michael@0 | 225 | return; |
michael@0 | 226 | } |
michael@0 | 227 | } |
michael@0 | 228 | |
michael@0 | 229 | kid = kid->GetNextSibling(); |
michael@0 | 230 | } |
michael@0 | 231 | if (cursor) { |
michael@0 | 232 | cursor->FinishBuildingCursor(); |
michael@0 | 233 | } |
michael@0 | 234 | } |
michael@0 | 235 | |
michael@0 | 236 | void |
michael@0 | 237 | nsTableRowGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
michael@0 | 238 | const nsRect& aDirtyRect, |
michael@0 | 239 | const nsDisplayListSet& aLists) |
michael@0 | 240 | { |
michael@0 | 241 | nsDisplayTableItem* item = nullptr; |
michael@0 | 242 | if (IsVisibleInSelection(aBuilder)) { |
michael@0 | 243 | bool isRoot = aBuilder->IsAtRootOfPseudoStackingContext(); |
michael@0 | 244 | if (isRoot) { |
michael@0 | 245 | // This background is created regardless of whether this frame is |
michael@0 | 246 | // visible or not. Visibility decisions are delegated to the |
michael@0 | 247 | // table background painter. |
michael@0 | 248 | item = new (aBuilder) nsDisplayTableRowGroupBackground(aBuilder, this); |
michael@0 | 249 | aLists.BorderBackground()->AppendNewToTop(item); |
michael@0 | 250 | } |
michael@0 | 251 | } |
michael@0 | 252 | nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, |
michael@0 | 253 | aLists, item, DisplayRows); |
michael@0 | 254 | } |
michael@0 | 255 | |
michael@0 | 256 | int |
michael@0 | 257 | nsTableRowGroupFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const |
michael@0 | 258 | { |
michael@0 | 259 | int skip = 0; |
michael@0 | 260 | if (nullptr != GetPrevInFlow()) { |
michael@0 | 261 | skip |= LOGICAL_SIDE_B_START; |
michael@0 | 262 | } |
michael@0 | 263 | if (nullptr != GetNextInFlow()) { |
michael@0 | 264 | skip |= LOGICAL_SIDE_B_END; |
michael@0 | 265 | } |
michael@0 | 266 | return skip; |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | // Position and size aKidFrame and update our reflow state. The origin of |
michael@0 | 270 | // aKidRect is relative to the upper-left origin of our frame |
michael@0 | 271 | void |
michael@0 | 272 | nsTableRowGroupFrame::PlaceChild(nsPresContext* aPresContext, |
michael@0 | 273 | nsRowGroupReflowState& aReflowState, |
michael@0 | 274 | nsIFrame* aKidFrame, |
michael@0 | 275 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 276 | const nsRect& aOriginalKidRect, |
michael@0 | 277 | const nsRect& aOriginalKidVisualOverflow) |
michael@0 | 278 | { |
michael@0 | 279 | bool isFirstReflow = |
michael@0 | 280 | (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; |
michael@0 | 281 | |
michael@0 | 282 | // Place and size the child |
michael@0 | 283 | FinishReflowChild(aKidFrame, aPresContext, aDesiredSize, nullptr, 0, |
michael@0 | 284 | aReflowState.y, 0); |
michael@0 | 285 | |
michael@0 | 286 | nsTableFrame::InvalidateTableFrame(aKidFrame, aOriginalKidRect, |
michael@0 | 287 | aOriginalKidVisualOverflow, isFirstReflow); |
michael@0 | 288 | |
michael@0 | 289 | // Adjust the running y-offset |
michael@0 | 290 | aReflowState.y += aDesiredSize.Height(); |
michael@0 | 291 | |
michael@0 | 292 | // If our height is constrained then update the available height |
michael@0 | 293 | if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { |
michael@0 | 294 | aReflowState.availSize.height -= aDesiredSize.Height(); |
michael@0 | 295 | } |
michael@0 | 296 | } |
michael@0 | 297 | |
michael@0 | 298 | void |
michael@0 | 299 | nsTableRowGroupFrame::InitChildReflowState(nsPresContext& aPresContext, |
michael@0 | 300 | bool aBorderCollapse, |
michael@0 | 301 | nsHTMLReflowState& aReflowState) |
michael@0 | 302 | { |
michael@0 | 303 | nsMargin collapseBorder; |
michael@0 | 304 | nsMargin padding(0,0,0,0); |
michael@0 | 305 | nsMargin* pCollapseBorder = nullptr; |
michael@0 | 306 | if (aBorderCollapse) { |
michael@0 | 307 | nsTableRowFrame *rowFrame = do_QueryFrame(aReflowState.frame); |
michael@0 | 308 | if (rowFrame) { |
michael@0 | 309 | pCollapseBorder = rowFrame->GetBCBorderWidth(collapseBorder); |
michael@0 | 310 | } |
michael@0 | 311 | } |
michael@0 | 312 | aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, &padding); |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | static void |
michael@0 | 316 | CacheRowHeightsForPrinting(nsPresContext* aPresContext, |
michael@0 | 317 | nsTableRowFrame* aFirstRow) |
michael@0 | 318 | { |
michael@0 | 319 | for (nsTableRowFrame* row = aFirstRow; row; row = row->GetNextRow()) { |
michael@0 | 320 | if (!row->GetPrevInFlow()) { |
michael@0 | 321 | row->SetHasUnpaginatedHeight(true); |
michael@0 | 322 | row->SetUnpaginatedHeight(aPresContext, row->GetSize().height); |
michael@0 | 323 | } |
michael@0 | 324 | } |
michael@0 | 325 | } |
michael@0 | 326 | |
michael@0 | 327 | nsresult |
michael@0 | 328 | nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext, |
michael@0 | 329 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 330 | nsRowGroupReflowState& aReflowState, |
michael@0 | 331 | nsReflowStatus& aStatus, |
michael@0 | 332 | bool* aPageBreakBeforeEnd) |
michael@0 | 333 | { |
michael@0 | 334 | if (aPageBreakBeforeEnd) |
michael@0 | 335 | *aPageBreakBeforeEnd = false; |
michael@0 | 336 | |
michael@0 | 337 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 338 | nsresult rv = NS_OK; |
michael@0 | 339 | const bool borderCollapse = tableFrame->IsBorderCollapse(); |
michael@0 | 340 | nscoord cellSpacingY = tableFrame->GetCellSpacingY(); |
michael@0 | 341 | |
michael@0 | 342 | // XXXldb Should we really be checking this rather than available height? |
michael@0 | 343 | // (Think about multi-column layout!) |
michael@0 | 344 | bool isPaginated = aPresContext->IsPaginated() && |
michael@0 | 345 | NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height; |
michael@0 | 346 | |
michael@0 | 347 | bool haveRow = false; |
michael@0 | 348 | bool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() || |
michael@0 | 349 | tableFrame->IsGeometryDirty(); |
michael@0 | 350 | bool needToCalcRowHeights = reflowAllKids; |
michael@0 | 351 | |
michael@0 | 352 | nsIFrame *prevKidFrame = nullptr; |
michael@0 | 353 | for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; |
michael@0 | 354 | prevKidFrame = kidFrame, kidFrame = kidFrame->GetNextSibling()) { |
michael@0 | 355 | nsTableRowFrame *rowFrame = do_QueryFrame(kidFrame); |
michael@0 | 356 | if (!rowFrame) { |
michael@0 | 357 | // XXXldb nsCSSFrameConstructor needs to enforce this! |
michael@0 | 358 | NS_NOTREACHED("yikes, a non-row child"); |
michael@0 | 359 | continue; |
michael@0 | 360 | } |
michael@0 | 361 | |
michael@0 | 362 | haveRow = true; |
michael@0 | 363 | |
michael@0 | 364 | // Reflow the row frame |
michael@0 | 365 | if (reflowAllKids || |
michael@0 | 366 | NS_SUBTREE_DIRTY(kidFrame) || |
michael@0 | 367 | (aReflowState.reflowState.mFlags.mSpecialHeightReflow && |
michael@0 | 368 | (isPaginated || (kidFrame->GetStateBits() & |
michael@0 | 369 | NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) { |
michael@0 | 370 | nsRect oldKidRect = kidFrame->GetRect(); |
michael@0 | 371 | nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect(); |
michael@0 | 372 | |
michael@0 | 373 | // XXXldb We used to only pass aDesiredSize.mFlags through for the |
michael@0 | 374 | // incremental reflow codepath. |
michael@0 | 375 | nsHTMLReflowMetrics desiredSize(aReflowState.reflowState, |
michael@0 | 376 | aDesiredSize.mFlags); |
michael@0 | 377 | desiredSize.Width() = desiredSize.Height() = 0; |
michael@0 | 378 | |
michael@0 | 379 | // Reflow the child into the available space, giving it as much height as |
michael@0 | 380 | // it wants. We'll deal with splitting later after we've computed the row |
michael@0 | 381 | // heights, taking into account cells with row spans... |
michael@0 | 382 | nsSize kidAvailSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE); |
michael@0 | 383 | nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, |
michael@0 | 384 | kidFrame, kidAvailSize, |
michael@0 | 385 | -1, -1, |
michael@0 | 386 | nsHTMLReflowState::CALLER_WILL_INIT); |
michael@0 | 387 | InitChildReflowState(*aPresContext, borderCollapse, kidReflowState); |
michael@0 | 388 | |
michael@0 | 389 | // This can indicate that columns were resized. |
michael@0 | 390 | if (aReflowState.reflowState.mFlags.mHResize) |
michael@0 | 391 | kidReflowState.mFlags.mHResize = true; |
michael@0 | 392 | |
michael@0 | 393 | NS_ASSERTION(kidFrame == mFrames.FirstChild() || prevKidFrame, |
michael@0 | 394 | "If we're not on the first frame, we should have a " |
michael@0 | 395 | "previous sibling..."); |
michael@0 | 396 | // If prev row has nonzero YMost, then we can't be at the top of the page |
michael@0 | 397 | if (prevKidFrame && prevKidFrame->GetRect().YMost() > 0) { |
michael@0 | 398 | kidReflowState.mFlags.mIsTopOfPage = false; |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, |
michael@0 | 402 | 0, aReflowState.y, 0, aStatus); |
michael@0 | 403 | |
michael@0 | 404 | // Place the child |
michael@0 | 405 | PlaceChild(aPresContext, aReflowState, kidFrame, desiredSize, |
michael@0 | 406 | oldKidRect, oldKidVisualOverflow); |
michael@0 | 407 | aReflowState.y += cellSpacingY; |
michael@0 | 408 | |
michael@0 | 409 | if (!reflowAllKids) { |
michael@0 | 410 | if (IsSimpleRowFrame(aReflowState.tableFrame, kidFrame)) { |
michael@0 | 411 | // Inform the row of its new height. |
michael@0 | 412 | rowFrame->DidResize(); |
michael@0 | 413 | // the overflow area may have changed inflate the overflow area |
michael@0 | 414 | const nsStylePosition *stylePos = StylePosition(); |
michael@0 | 415 | nsStyleUnit unit = stylePos->mHeight.GetUnit(); |
michael@0 | 416 | if (aReflowState.tableFrame->IsAutoHeight() && |
michael@0 | 417 | unit != eStyleUnit_Coord) { |
michael@0 | 418 | // Because other cells in the row may need to be aligned |
michael@0 | 419 | // differently, repaint the entire row |
michael@0 | 420 | nsRect kidRect(0, aReflowState.y, |
michael@0 | 421 | desiredSize.Width(), desiredSize.Height()); |
michael@0 | 422 | InvalidateFrame(); |
michael@0 | 423 | } |
michael@0 | 424 | else if (oldKidRect.height != desiredSize.Height()) |
michael@0 | 425 | needToCalcRowHeights = true; |
michael@0 | 426 | } else { |
michael@0 | 427 | needToCalcRowHeights = true; |
michael@0 | 428 | } |
michael@0 | 429 | } |
michael@0 | 430 | |
michael@0 | 431 | if (isPaginated && aPageBreakBeforeEnd && !*aPageBreakBeforeEnd) { |
michael@0 | 432 | nsTableRowFrame* nextRow = rowFrame->GetNextRow(); |
michael@0 | 433 | if (nextRow) { |
michael@0 | 434 | *aPageBreakBeforeEnd = nsTableFrame::PageBreakAfter(kidFrame, nextRow); |
michael@0 | 435 | } |
michael@0 | 436 | } |
michael@0 | 437 | } else { |
michael@0 | 438 | SlideChild(aReflowState, kidFrame); |
michael@0 | 439 | |
michael@0 | 440 | // Adjust the running y-offset so we know where the next row should be placed |
michael@0 | 441 | nscoord height = kidFrame->GetSize().height + cellSpacingY; |
michael@0 | 442 | aReflowState.y += height; |
michael@0 | 443 | |
michael@0 | 444 | if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { |
michael@0 | 445 | aReflowState.availSize.height -= height; |
michael@0 | 446 | } |
michael@0 | 447 | } |
michael@0 | 448 | ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame); |
michael@0 | 449 | } |
michael@0 | 450 | |
michael@0 | 451 | if (haveRow) |
michael@0 | 452 | aReflowState.y -= cellSpacingY; |
michael@0 | 453 | |
michael@0 | 454 | // Return our desired rect |
michael@0 | 455 | aDesiredSize.Width() = aReflowState.reflowState.AvailableWidth(); |
michael@0 | 456 | aDesiredSize.Height() = aReflowState.y; |
michael@0 | 457 | |
michael@0 | 458 | if (aReflowState.reflowState.mFlags.mSpecialHeightReflow) { |
michael@0 | 459 | DidResizeRows(aDesiredSize); |
michael@0 | 460 | if (isPaginated) { |
michael@0 | 461 | CacheRowHeightsForPrinting(aPresContext, GetFirstRow()); |
michael@0 | 462 | } |
michael@0 | 463 | } |
michael@0 | 464 | else if (needToCalcRowHeights) { |
michael@0 | 465 | CalculateRowHeights(aPresContext, aDesiredSize, aReflowState.reflowState); |
michael@0 | 466 | if (!reflowAllKids) { |
michael@0 | 467 | InvalidateFrame(); |
michael@0 | 468 | } |
michael@0 | 469 | } |
michael@0 | 470 | |
michael@0 | 471 | return rv; |
michael@0 | 472 | } |
michael@0 | 473 | |
michael@0 | 474 | nsTableRowFrame* |
michael@0 | 475 | nsTableRowGroupFrame::GetFirstRow() |
michael@0 | 476 | { |
michael@0 | 477 | for (nsIFrame* childFrame = mFrames.FirstChild(); childFrame; |
michael@0 | 478 | childFrame = childFrame->GetNextSibling()) { |
michael@0 | 479 | nsTableRowFrame *rowFrame = do_QueryFrame(childFrame); |
michael@0 | 480 | if (rowFrame) { |
michael@0 | 481 | return rowFrame; |
michael@0 | 482 | } |
michael@0 | 483 | } |
michael@0 | 484 | return nullptr; |
michael@0 | 485 | } |
michael@0 | 486 | |
michael@0 | 487 | |
michael@0 | 488 | struct RowInfo { |
michael@0 | 489 | RowInfo() { height = pctHeight = hasStyleHeight = hasPctHeight = isSpecial = 0; } |
michael@0 | 490 | unsigned height; // content height or fixed height, excluding pct height |
michael@0 | 491 | unsigned pctHeight:29; // pct height |
michael@0 | 492 | unsigned hasStyleHeight:1; |
michael@0 | 493 | unsigned hasPctHeight:1; |
michael@0 | 494 | unsigned isSpecial:1; // there is no cell originating in the row with rowspan=1 and there are at |
michael@0 | 495 | // least 2 cells spanning the row and there is no style height on the row |
michael@0 | 496 | }; |
michael@0 | 497 | |
michael@0 | 498 | static void |
michael@0 | 499 | UpdateHeights(RowInfo& aRowInfo, |
michael@0 | 500 | nscoord aAdditionalHeight, |
michael@0 | 501 | nscoord& aTotal, |
michael@0 | 502 | nscoord& aUnconstrainedTotal) |
michael@0 | 503 | { |
michael@0 | 504 | aRowInfo.height += aAdditionalHeight; |
michael@0 | 505 | aTotal += aAdditionalHeight; |
michael@0 | 506 | if (!aRowInfo.hasStyleHeight) { |
michael@0 | 507 | aUnconstrainedTotal += aAdditionalHeight; |
michael@0 | 508 | } |
michael@0 | 509 | } |
michael@0 | 510 | |
michael@0 | 511 | void |
michael@0 | 512 | nsTableRowGroupFrame::DidResizeRows(nsHTMLReflowMetrics& aDesiredSize) |
michael@0 | 513 | { |
michael@0 | 514 | // update the cells spanning rows with their new heights |
michael@0 | 515 | // this is the place where all of the cells in the row get set to the height of the row |
michael@0 | 516 | // Reset the overflow area |
michael@0 | 517 | aDesiredSize.mOverflowAreas.Clear(); |
michael@0 | 518 | for (nsTableRowFrame* rowFrame = GetFirstRow(); |
michael@0 | 519 | rowFrame; rowFrame = rowFrame->GetNextRow()) { |
michael@0 | 520 | rowFrame->DidResize(); |
michael@0 | 521 | ConsiderChildOverflow(aDesiredSize.mOverflowAreas, rowFrame); |
michael@0 | 522 | } |
michael@0 | 523 | } |
michael@0 | 524 | |
michael@0 | 525 | // This calculates the height of all the rows and takes into account |
michael@0 | 526 | // style height on the row group, style heights on rows and cells, style heights on rowspans. |
michael@0 | 527 | // Actual row heights will be adjusted later if the table has a style height. |
michael@0 | 528 | // Even if rows don't change height, this method must be called to set the heights of each |
michael@0 | 529 | // cell in the row to the height of its row. |
michael@0 | 530 | void |
michael@0 | 531 | nsTableRowGroupFrame::CalculateRowHeights(nsPresContext* aPresContext, |
michael@0 | 532 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 533 | const nsHTMLReflowState& aReflowState) |
michael@0 | 534 | { |
michael@0 | 535 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 536 | const bool isPaginated = aPresContext->IsPaginated(); |
michael@0 | 537 | |
michael@0 | 538 | // all table cells have the same top and bottom margins, namely cellSpacingY |
michael@0 | 539 | nscoord cellSpacingY = tableFrame->GetCellSpacingY(); |
michael@0 | 540 | |
michael@0 | 541 | int32_t numEffCols = tableFrame->GetEffectiveColCount(); |
michael@0 | 542 | |
michael@0 | 543 | int32_t startRowIndex = GetStartRowIndex(); |
michael@0 | 544 | // find the row corresponding to the row index we just found |
michael@0 | 545 | nsTableRowFrame* startRowFrame = GetFirstRow(); |
michael@0 | 546 | |
michael@0 | 547 | if (!startRowFrame) return; |
michael@0 | 548 | |
michael@0 | 549 | // the current row group height is the y origin of the 1st row we are about to calculated a height for |
michael@0 | 550 | nscoord startRowGroupHeight = startRowFrame->GetPosition().y; |
michael@0 | 551 | |
michael@0 | 552 | int32_t numRows = GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex()); |
michael@0 | 553 | // collect the current height of each row. nscoord* rowHeights = nullptr; |
michael@0 | 554 | if (numRows <= 0) |
michael@0 | 555 | return; |
michael@0 | 556 | |
michael@0 | 557 | nsTArray<RowInfo> rowInfo; |
michael@0 | 558 | if (!rowInfo.AppendElements(numRows)) { |
michael@0 | 559 | return; |
michael@0 | 560 | } |
michael@0 | 561 | |
michael@0 | 562 | bool hasRowSpanningCell = false; |
michael@0 | 563 | nscoord heightOfRows = 0; |
michael@0 | 564 | nscoord heightOfUnStyledRows = 0; |
michael@0 | 565 | // Get the height of each row without considering rowspans. This will be the max of |
michael@0 | 566 | // the largest desired height of each cell, the largest style height of each cell, |
michael@0 | 567 | // the style height of the row. |
michael@0 | 568 | nscoord pctHeightBasis = GetHeightBasis(aReflowState); |
michael@0 | 569 | int32_t rowIndex; // the index in rowInfo, not among the rows in the row group |
michael@0 | 570 | nsTableRowFrame* rowFrame; |
michael@0 | 571 | for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { |
michael@0 | 572 | nscoord nonPctHeight = rowFrame->GetContentHeight(); |
michael@0 | 573 | if (isPaginated) { |
michael@0 | 574 | nonPctHeight = std::max(nonPctHeight, rowFrame->GetSize().height); |
michael@0 | 575 | } |
michael@0 | 576 | if (!rowFrame->GetPrevInFlow()) { |
michael@0 | 577 | if (rowFrame->HasPctHeight()) { |
michael@0 | 578 | rowInfo[rowIndex].hasPctHeight = true; |
michael@0 | 579 | rowInfo[rowIndex].pctHeight = rowFrame->GetHeight(pctHeightBasis); |
michael@0 | 580 | } |
michael@0 | 581 | rowInfo[rowIndex].hasStyleHeight = rowFrame->HasStyleHeight(); |
michael@0 | 582 | nonPctHeight = std::max(nonPctHeight, rowFrame->GetFixedHeight()); |
michael@0 | 583 | } |
michael@0 | 584 | UpdateHeights(rowInfo[rowIndex], nonPctHeight, heightOfRows, heightOfUnStyledRows); |
michael@0 | 585 | |
michael@0 | 586 | if (!rowInfo[rowIndex].hasStyleHeight) { |
michael@0 | 587 | if (isPaginated || tableFrame->HasMoreThanOneCell(rowIndex + startRowIndex)) { |
michael@0 | 588 | rowInfo[rowIndex].isSpecial = true; |
michael@0 | 589 | // iteratate the row's cell frames to see if any do not have rowspan > 1 |
michael@0 | 590 | nsTableCellFrame* cellFrame = rowFrame->GetFirstCell(); |
michael@0 | 591 | while (cellFrame) { |
michael@0 | 592 | int32_t rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame); |
michael@0 | 593 | if (1 == rowSpan) { |
michael@0 | 594 | rowInfo[rowIndex].isSpecial = false; |
michael@0 | 595 | break; |
michael@0 | 596 | } |
michael@0 | 597 | cellFrame = cellFrame->GetNextCell(); |
michael@0 | 598 | } |
michael@0 | 599 | } |
michael@0 | 600 | } |
michael@0 | 601 | // See if a cell spans into the row. If so we'll have to do the next step |
michael@0 | 602 | if (!hasRowSpanningCell) { |
michael@0 | 603 | if (tableFrame->RowIsSpannedInto(rowIndex + startRowIndex, numEffCols)) { |
michael@0 | 604 | hasRowSpanningCell = true; |
michael@0 | 605 | } |
michael@0 | 606 | } |
michael@0 | 607 | } |
michael@0 | 608 | |
michael@0 | 609 | if (hasRowSpanningCell) { |
michael@0 | 610 | // Get the height of cells with rowspans and allocate any extra space to the rows they span |
michael@0 | 611 | // iteratate the child frames and process the row frames among them |
michael@0 | 612 | for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { |
michael@0 | 613 | // See if the row has an originating cell with rowspan > 1. We cannot determine this for a row in a |
michael@0 | 614 | // continued row group by calling RowHasSpanningCells, because the row's fif may not have any originating |
michael@0 | 615 | // cells yet the row may have a continued cell which originates in it. |
michael@0 | 616 | if (GetPrevInFlow() || tableFrame->RowHasSpanningCells(startRowIndex + rowIndex, numEffCols)) { |
michael@0 | 617 | nsTableCellFrame* cellFrame = rowFrame->GetFirstCell(); |
michael@0 | 618 | // iteratate the row's cell frames |
michael@0 | 619 | while (cellFrame) { |
michael@0 | 620 | int32_t rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame); |
michael@0 | 621 | if ((rowIndex + rowSpan) > numRows) { |
michael@0 | 622 | // there might be rows pushed already to the nextInFlow |
michael@0 | 623 | rowSpan = numRows - rowIndex; |
michael@0 | 624 | } |
michael@0 | 625 | if (rowSpan > 1) { // a cell with rowspan > 1, determine the height of the rows it spans |
michael@0 | 626 | nscoord heightOfRowsSpanned = 0; |
michael@0 | 627 | nscoord heightOfUnStyledRowsSpanned = 0; |
michael@0 | 628 | nscoord numSpecialRowsSpanned = 0; |
michael@0 | 629 | nscoord cellSpacingTotal = 0; |
michael@0 | 630 | int32_t spanX; |
michael@0 | 631 | for (spanX = 0; spanX < rowSpan; spanX++) { |
michael@0 | 632 | heightOfRowsSpanned += rowInfo[rowIndex + spanX].height; |
michael@0 | 633 | if (!rowInfo[rowIndex + spanX].hasStyleHeight) { |
michael@0 | 634 | heightOfUnStyledRowsSpanned += rowInfo[rowIndex + spanX].height; |
michael@0 | 635 | } |
michael@0 | 636 | if (0 != spanX) { |
michael@0 | 637 | cellSpacingTotal += cellSpacingY; |
michael@0 | 638 | } |
michael@0 | 639 | if (rowInfo[rowIndex + spanX].isSpecial) { |
michael@0 | 640 | numSpecialRowsSpanned++; |
michael@0 | 641 | } |
michael@0 | 642 | } |
michael@0 | 643 | nscoord heightOfAreaSpanned = heightOfRowsSpanned + cellSpacingTotal; |
michael@0 | 644 | // get the height of the cell |
michael@0 | 645 | nsSize cellFrameSize = cellFrame->GetSize(); |
michael@0 | 646 | nsSize cellDesSize = cellFrame->GetDesiredSize(); |
michael@0 | 647 | rowFrame->CalculateCellActualHeight(cellFrame, cellDesSize.height); |
michael@0 | 648 | cellFrameSize.height = cellDesSize.height; |
michael@0 | 649 | if (cellFrame->HasVerticalAlignBaseline()) { |
michael@0 | 650 | // to ensure that a spanning cell with a long descender doesn't |
michael@0 | 651 | // collide with the next row, we need to take into account the shift |
michael@0 | 652 | // that will be done to align the cell on the baseline of the row. |
michael@0 | 653 | cellFrameSize.height += rowFrame->GetMaxCellAscent() - |
michael@0 | 654 | cellFrame->GetCellBaseline(); |
michael@0 | 655 | } |
michael@0 | 656 | |
michael@0 | 657 | if (heightOfAreaSpanned < cellFrameSize.height) { |
michael@0 | 658 | // the cell's height is larger than the available space of the rows it |
michael@0 | 659 | // spans so distribute the excess height to the rows affected |
michael@0 | 660 | nscoord extra = cellFrameSize.height - heightOfAreaSpanned; |
michael@0 | 661 | nscoord extraUsed = 0; |
michael@0 | 662 | if (0 == numSpecialRowsSpanned) { |
michael@0 | 663 | //NS_ASSERTION(heightOfRowsSpanned > 0, "invalid row span situation"); |
michael@0 | 664 | bool haveUnStyledRowsSpanned = (heightOfUnStyledRowsSpanned > 0); |
michael@0 | 665 | nscoord divisor = (haveUnStyledRowsSpanned) |
michael@0 | 666 | ? heightOfUnStyledRowsSpanned : heightOfRowsSpanned; |
michael@0 | 667 | if (divisor > 0) { |
michael@0 | 668 | for (spanX = rowSpan - 1; spanX >= 0; spanX--) { |
michael@0 | 669 | if (!haveUnStyledRowsSpanned || !rowInfo[rowIndex + spanX].hasStyleHeight) { |
michael@0 | 670 | // The amount of additional space each row gets is proportional to its height |
michael@0 | 671 | float percent = ((float)rowInfo[rowIndex + spanX].height) / ((float)divisor); |
michael@0 | 672 | |
michael@0 | 673 | // give rows their percentage, except for the first row which gets the remainder |
michael@0 | 674 | nscoord extraForRow = (0 == spanX) ? extra - extraUsed |
michael@0 | 675 | : NSToCoordRound(((float)(extra)) * percent); |
michael@0 | 676 | extraForRow = std::min(extraForRow, extra - extraUsed); |
michael@0 | 677 | // update the row height |
michael@0 | 678 | UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows); |
michael@0 | 679 | extraUsed += extraForRow; |
michael@0 | 680 | if (extraUsed >= extra) { |
michael@0 | 681 | NS_ASSERTION((extraUsed == extra), "invalid row height calculation"); |
michael@0 | 682 | break; |
michael@0 | 683 | } |
michael@0 | 684 | } |
michael@0 | 685 | } |
michael@0 | 686 | } |
michael@0 | 687 | else { |
michael@0 | 688 | // put everything in the last row |
michael@0 | 689 | UpdateHeights(rowInfo[rowIndex + rowSpan - 1], extra, heightOfRows, heightOfUnStyledRows); |
michael@0 | 690 | } |
michael@0 | 691 | } |
michael@0 | 692 | else { |
michael@0 | 693 | // give the extra to the special rows |
michael@0 | 694 | nscoord numSpecialRowsAllocated = 0; |
michael@0 | 695 | for (spanX = rowSpan - 1; spanX >= 0; spanX--) { |
michael@0 | 696 | if (rowInfo[rowIndex + spanX].isSpecial) { |
michael@0 | 697 | // The amount of additional space each degenerate row gets is proportional to the number of them |
michael@0 | 698 | float percent = 1.0f / ((float)numSpecialRowsSpanned); |
michael@0 | 699 | |
michael@0 | 700 | // give rows their percentage, except for the first row which gets the remainder |
michael@0 | 701 | nscoord extraForRow = (numSpecialRowsSpanned - 1 == numSpecialRowsAllocated) |
michael@0 | 702 | ? extra - extraUsed |
michael@0 | 703 | : NSToCoordRound(((float)(extra)) * percent); |
michael@0 | 704 | extraForRow = std::min(extraForRow, extra - extraUsed); |
michael@0 | 705 | // update the row height |
michael@0 | 706 | UpdateHeights(rowInfo[rowIndex + spanX], extraForRow, heightOfRows, heightOfUnStyledRows); |
michael@0 | 707 | extraUsed += extraForRow; |
michael@0 | 708 | if (extraUsed >= extra) { |
michael@0 | 709 | NS_ASSERTION((extraUsed == extra), "invalid row height calculation"); |
michael@0 | 710 | break; |
michael@0 | 711 | } |
michael@0 | 712 | } |
michael@0 | 713 | } |
michael@0 | 714 | } |
michael@0 | 715 | } |
michael@0 | 716 | } // if (rowSpan > 1) |
michael@0 | 717 | cellFrame = cellFrame->GetNextCell(); |
michael@0 | 718 | } // while (cellFrame) |
michael@0 | 719 | } // if (tableFrame->RowHasSpanningCells(startRowIndex + rowIndex) { |
michael@0 | 720 | } // while (rowFrame) |
michael@0 | 721 | } |
michael@0 | 722 | |
michael@0 | 723 | // pct height rows have already got their content heights. Give them their pct heights up to pctHeightBasis |
michael@0 | 724 | nscoord extra = pctHeightBasis - heightOfRows; |
michael@0 | 725 | for (rowFrame = startRowFrame, rowIndex = 0; rowFrame && (extra > 0); rowFrame = rowFrame->GetNextRow(), rowIndex++) { |
michael@0 | 726 | RowInfo& rInfo = rowInfo[rowIndex]; |
michael@0 | 727 | if (rInfo.hasPctHeight) { |
michael@0 | 728 | nscoord rowExtra = (rInfo.pctHeight > rInfo.height) |
michael@0 | 729 | ? rInfo.pctHeight - rInfo.height: 0; |
michael@0 | 730 | rowExtra = std::min(rowExtra, extra); |
michael@0 | 731 | UpdateHeights(rInfo, rowExtra, heightOfRows, heightOfUnStyledRows); |
michael@0 | 732 | extra -= rowExtra; |
michael@0 | 733 | } |
michael@0 | 734 | } |
michael@0 | 735 | |
michael@0 | 736 | bool styleHeightAllocation = false; |
michael@0 | 737 | nscoord rowGroupHeight = startRowGroupHeight + heightOfRows + ((numRows - 1) * cellSpacingY); |
michael@0 | 738 | // if we have a style height, allocate the extra height to unconstrained rows |
michael@0 | 739 | if ((aReflowState.ComputedHeight() > rowGroupHeight) && |
michael@0 | 740 | (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight())) { |
michael@0 | 741 | nscoord extraComputedHeight = aReflowState.ComputedHeight() - rowGroupHeight; |
michael@0 | 742 | nscoord extraUsed = 0; |
michael@0 | 743 | bool haveUnStyledRows = (heightOfUnStyledRows > 0); |
michael@0 | 744 | nscoord divisor = (haveUnStyledRows) |
michael@0 | 745 | ? heightOfUnStyledRows : heightOfRows; |
michael@0 | 746 | if (divisor > 0) { |
michael@0 | 747 | styleHeightAllocation = true; |
michael@0 | 748 | for (rowIndex = 0; rowIndex < numRows; rowIndex++) { |
michael@0 | 749 | if (!haveUnStyledRows || !rowInfo[rowIndex].hasStyleHeight) { |
michael@0 | 750 | // The amount of additional space each row gets is based on the |
michael@0 | 751 | // percentage of space it occupies |
michael@0 | 752 | float percent = ((float)rowInfo[rowIndex].height) / ((float)divisor); |
michael@0 | 753 | // give rows their percentage, except for the last row which gets the remainder |
michael@0 | 754 | nscoord extraForRow = (numRows - 1 == rowIndex) |
michael@0 | 755 | ? extraComputedHeight - extraUsed |
michael@0 | 756 | : NSToCoordRound(((float)extraComputedHeight) * percent); |
michael@0 | 757 | extraForRow = std::min(extraForRow, extraComputedHeight - extraUsed); |
michael@0 | 758 | // update the row height |
michael@0 | 759 | UpdateHeights(rowInfo[rowIndex], extraForRow, heightOfRows, heightOfUnStyledRows); |
michael@0 | 760 | extraUsed += extraForRow; |
michael@0 | 761 | if (extraUsed >= extraComputedHeight) { |
michael@0 | 762 | NS_ASSERTION((extraUsed == extraComputedHeight), "invalid row height calculation"); |
michael@0 | 763 | break; |
michael@0 | 764 | } |
michael@0 | 765 | } |
michael@0 | 766 | } |
michael@0 | 767 | } |
michael@0 | 768 | rowGroupHeight = aReflowState.ComputedHeight(); |
michael@0 | 769 | } |
michael@0 | 770 | |
michael@0 | 771 | nscoord yOrigin = startRowGroupHeight; |
michael@0 | 772 | // update the rows with their (potentially) new heights |
michael@0 | 773 | for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) { |
michael@0 | 774 | nsRect rowBounds = rowFrame->GetRect(); |
michael@0 | 775 | nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect(); |
michael@0 | 776 | |
michael@0 | 777 | bool movedFrame = (rowBounds.y != yOrigin); |
michael@0 | 778 | nscoord rowHeight = (rowInfo[rowIndex].height > 0) ? rowInfo[rowIndex].height : 0; |
michael@0 | 779 | |
michael@0 | 780 | if (movedFrame || (rowHeight != rowBounds.height)) { |
michael@0 | 781 | // Resize/move the row to its final size and position |
michael@0 | 782 | if (movedFrame) { |
michael@0 | 783 | rowFrame->InvalidateFrameSubtree(); |
michael@0 | 784 | } |
michael@0 | 785 | |
michael@0 | 786 | rowFrame->SetRect(nsRect(rowBounds.x, yOrigin, rowBounds.width, |
michael@0 | 787 | rowHeight)); |
michael@0 | 788 | |
michael@0 | 789 | nsTableFrame::InvalidateTableFrame(rowFrame, rowBounds, rowVisualOverflow, |
michael@0 | 790 | false); |
michael@0 | 791 | } |
michael@0 | 792 | if (movedFrame) { |
michael@0 | 793 | nsTableFrame::RePositionViews(rowFrame); |
michael@0 | 794 | // XXXbz we don't need to update our overflow area? |
michael@0 | 795 | } |
michael@0 | 796 | yOrigin += rowHeight + cellSpacingY; |
michael@0 | 797 | } |
michael@0 | 798 | |
michael@0 | 799 | if (isPaginated && styleHeightAllocation) { |
michael@0 | 800 | // since the row group has a style height, cache the row heights, so next in flows can honor them |
michael@0 | 801 | CacheRowHeightsForPrinting(aPresContext, GetFirstRow()); |
michael@0 | 802 | } |
michael@0 | 803 | |
michael@0 | 804 | DidResizeRows(aDesiredSize); |
michael@0 | 805 | |
michael@0 | 806 | aDesiredSize.Height() = rowGroupHeight; // Adjust our desired size |
michael@0 | 807 | } |
michael@0 | 808 | |
michael@0 | 809 | nscoord |
michael@0 | 810 | nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aYTotalOffset, |
michael@0 | 811 | nscoord aWidth) |
michael@0 | 812 | { |
michael@0 | 813 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 814 | const nsStyleVisibility* groupVis = StyleVisibility(); |
michael@0 | 815 | bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible); |
michael@0 | 816 | if (collapseGroup) { |
michael@0 | 817 | tableFrame->SetNeedToCollapse(true); |
michael@0 | 818 | } |
michael@0 | 819 | |
michael@0 | 820 | nsOverflowAreas overflow; |
michael@0 | 821 | |
michael@0 | 822 | nsTableRowFrame* rowFrame= GetFirstRow(); |
michael@0 | 823 | bool didCollapse = false; |
michael@0 | 824 | nscoord yGroupOffset = 0; |
michael@0 | 825 | while (rowFrame) { |
michael@0 | 826 | yGroupOffset += rowFrame->CollapseRowIfNecessary(yGroupOffset, |
michael@0 | 827 | aWidth, collapseGroup, |
michael@0 | 828 | didCollapse); |
michael@0 | 829 | ConsiderChildOverflow(overflow, rowFrame); |
michael@0 | 830 | rowFrame = rowFrame->GetNextRow(); |
michael@0 | 831 | } |
michael@0 | 832 | |
michael@0 | 833 | nsRect groupRect = GetRect(); |
michael@0 | 834 | nsRect oldGroupRect = groupRect; |
michael@0 | 835 | nsRect oldGroupVisualOverflow = GetVisualOverflowRect(); |
michael@0 | 836 | |
michael@0 | 837 | groupRect.height -= yGroupOffset; |
michael@0 | 838 | if (didCollapse) { |
michael@0 | 839 | // add back the cellspacing between rowgroups |
michael@0 | 840 | groupRect.height += tableFrame->GetCellSpacingY(); |
michael@0 | 841 | } |
michael@0 | 842 | |
michael@0 | 843 | groupRect.y -= aYTotalOffset; |
michael@0 | 844 | groupRect.width = aWidth; |
michael@0 | 845 | |
michael@0 | 846 | if (aYTotalOffset != 0) { |
michael@0 | 847 | InvalidateFrameSubtree(); |
michael@0 | 848 | } |
michael@0 | 849 | |
michael@0 | 850 | SetRect(groupRect); |
michael@0 | 851 | overflow.UnionAllWith(nsRect(0, 0, groupRect.width, groupRect.height)); |
michael@0 | 852 | FinishAndStoreOverflow(overflow, groupRect.Size()); |
michael@0 | 853 | nsTableFrame::RePositionViews(this); |
michael@0 | 854 | nsTableFrame::InvalidateTableFrame(this, oldGroupRect, oldGroupVisualOverflow, |
michael@0 | 855 | false); |
michael@0 | 856 | |
michael@0 | 857 | return yGroupOffset; |
michael@0 | 858 | } |
michael@0 | 859 | |
michael@0 | 860 | // Move a child that was skipped during a reflow. |
michael@0 | 861 | void |
michael@0 | 862 | nsTableRowGroupFrame::SlideChild(nsRowGroupReflowState& aReflowState, |
michael@0 | 863 | nsIFrame* aKidFrame) |
michael@0 | 864 | { |
michael@0 | 865 | // Move the frame if we need to |
michael@0 | 866 | nsPoint oldPosition = aKidFrame->GetPosition(); |
michael@0 | 867 | nsPoint newPosition = oldPosition; |
michael@0 | 868 | newPosition.y = aReflowState.y; |
michael@0 | 869 | if (oldPosition.y != newPosition.y) { |
michael@0 | 870 | aKidFrame->InvalidateFrameSubtree(); |
michael@0 | 871 | aKidFrame->SetPosition(newPosition); |
michael@0 | 872 | nsTableFrame::RePositionViews(aKidFrame); |
michael@0 | 873 | aKidFrame->InvalidateFrameSubtree(); |
michael@0 | 874 | } |
michael@0 | 875 | } |
michael@0 | 876 | |
michael@0 | 877 | // Create a continuing frame, add it to the child list, and then push it |
michael@0 | 878 | // and the frames that follow |
michael@0 | 879 | void |
michael@0 | 880 | nsTableRowGroupFrame::CreateContinuingRowFrame(nsPresContext& aPresContext, |
michael@0 | 881 | nsIFrame& aRowFrame, |
michael@0 | 882 | nsIFrame** aContRowFrame) |
michael@0 | 883 | { |
michael@0 | 884 | // XXX what is the row index? |
michael@0 | 885 | if (!aContRowFrame) {NS_ASSERTION(false, "bad call"); return;} |
michael@0 | 886 | // create the continuing frame which will create continuing cell frames |
michael@0 | 887 | *aContRowFrame = aPresContext.PresShell()->FrameConstructor()-> |
michael@0 | 888 | CreateContinuingFrame(&aPresContext, &aRowFrame, this); |
michael@0 | 889 | |
michael@0 | 890 | // Add the continuing row frame to the child list |
michael@0 | 891 | mFrames.InsertFrame(nullptr, &aRowFrame, *aContRowFrame); |
michael@0 | 892 | |
michael@0 | 893 | // Push the continuing row frame and the frames that follow |
michael@0 | 894 | PushChildren(*aContRowFrame, &aRowFrame); |
michael@0 | 895 | } |
michael@0 | 896 | |
michael@0 | 897 | // Reflow the cells with rowspan > 1 which originate between aFirstRow |
michael@0 | 898 | // and end on or after aLastRow. aFirstTruncatedRow is the highest row on the |
michael@0 | 899 | // page that contains a cell which cannot split on this page |
michael@0 | 900 | void |
michael@0 | 901 | nsTableRowGroupFrame::SplitSpanningCells(nsPresContext& aPresContext, |
michael@0 | 902 | const nsHTMLReflowState& aReflowState, |
michael@0 | 903 | nsTableFrame& aTable, |
michael@0 | 904 | nsTableRowFrame& aFirstRow, |
michael@0 | 905 | nsTableRowFrame& aLastRow, |
michael@0 | 906 | bool aFirstRowIsTopOfPage, |
michael@0 | 907 | nscoord aSpanningRowBottom, |
michael@0 | 908 | nsTableRowFrame*& aContRow, |
michael@0 | 909 | nsTableRowFrame*& aFirstTruncatedRow, |
michael@0 | 910 | nscoord& aDesiredHeight) |
michael@0 | 911 | { |
michael@0 | 912 | NS_ASSERTION(aSpanningRowBottom >= 0, "Can't split negative heights"); |
michael@0 | 913 | aFirstTruncatedRow = nullptr; |
michael@0 | 914 | aDesiredHeight = 0; |
michael@0 | 915 | |
michael@0 | 916 | const bool borderCollapse = aTable.IsBorderCollapse(); |
michael@0 | 917 | int32_t lastRowIndex = aLastRow.GetRowIndex(); |
michael@0 | 918 | bool wasLast = false; |
michael@0 | 919 | bool haveRowSpan = false; |
michael@0 | 920 | // Iterate the rows between aFirstRow and aLastRow |
michael@0 | 921 | for (nsTableRowFrame* row = &aFirstRow; !wasLast; row = row->GetNextRow()) { |
michael@0 | 922 | wasLast = (row == &aLastRow); |
michael@0 | 923 | int32_t rowIndex = row->GetRowIndex(); |
michael@0 | 924 | nsPoint rowPos = row->GetPosition(); |
michael@0 | 925 | // Iterate the cells looking for those that have rowspan > 1 |
michael@0 | 926 | for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) { |
michael@0 | 927 | int32_t rowSpan = aTable.GetEffectiveRowSpan(rowIndex, *cell); |
michael@0 | 928 | // Only reflow rowspan > 1 cells which span aLastRow. Those which don't span aLastRow |
michael@0 | 929 | // were reflowed correctly during the unconstrained height reflow. |
michael@0 | 930 | if ((rowSpan > 1) && (rowIndex + rowSpan > lastRowIndex)) { |
michael@0 | 931 | haveRowSpan = true; |
michael@0 | 932 | nsReflowStatus status; |
michael@0 | 933 | // Ask the row to reflow the cell to the height of all the rows it spans up through aLastRow |
michael@0 | 934 | // aAvailHeight is the space between the row group start and the end of the page |
michael@0 | 935 | nscoord cellAvailHeight = aSpanningRowBottom - rowPos.y; |
michael@0 | 936 | NS_ASSERTION(cellAvailHeight >= 0, "No space for cell?"); |
michael@0 | 937 | bool isTopOfPage = (row == &aFirstRow) && aFirstRowIsTopOfPage; |
michael@0 | 938 | |
michael@0 | 939 | nsRect rowRect = row->GetRect(); |
michael@0 | 940 | nsSize rowAvailSize(aReflowState.AvailableWidth(), |
michael@0 | 941 | std::max(aReflowState.AvailableHeight() - rowRect.y, |
michael@0 | 942 | 0)); |
michael@0 | 943 | // don't let the available height exceed what |
michael@0 | 944 | // CalculateRowHeights set for it |
michael@0 | 945 | rowAvailSize.height = std::min(rowAvailSize.height, rowRect.height); |
michael@0 | 946 | nsHTMLReflowState rowReflowState(&aPresContext, aReflowState, |
michael@0 | 947 | row, rowAvailSize, |
michael@0 | 948 | -1, -1, |
michael@0 | 949 | nsHTMLReflowState::CALLER_WILL_INIT); |
michael@0 | 950 | InitChildReflowState(aPresContext, borderCollapse, rowReflowState); |
michael@0 | 951 | rowReflowState.mFlags.mIsTopOfPage = isTopOfPage; // set top of page |
michael@0 | 952 | |
michael@0 | 953 | nscoord cellHeight = row->ReflowCellFrame(&aPresContext, rowReflowState, |
michael@0 | 954 | isTopOfPage, cell, |
michael@0 | 955 | cellAvailHeight, status); |
michael@0 | 956 | aDesiredHeight = std::max(aDesiredHeight, rowPos.y + cellHeight); |
michael@0 | 957 | if (NS_FRAME_IS_COMPLETE(status)) { |
michael@0 | 958 | if (cellHeight > cellAvailHeight) { |
michael@0 | 959 | aFirstTruncatedRow = row; |
michael@0 | 960 | if ((row != &aFirstRow) || !aFirstRowIsTopOfPage) { |
michael@0 | 961 | // return now, since we will be getting another reflow after either (1) row is |
michael@0 | 962 | // moved to the next page or (2) the row group is moved to the next page |
michael@0 | 963 | return; |
michael@0 | 964 | } |
michael@0 | 965 | } |
michael@0 | 966 | } |
michael@0 | 967 | else { |
michael@0 | 968 | if (!aContRow) { |
michael@0 | 969 | CreateContinuingRowFrame(aPresContext, aLastRow, (nsIFrame**)&aContRow); |
michael@0 | 970 | } |
michael@0 | 971 | if (aContRow) { |
michael@0 | 972 | if (row != &aLastRow) { |
michael@0 | 973 | // aContRow needs a continuation for cell, since cell spanned into aLastRow |
michael@0 | 974 | // but does not originate there |
michael@0 | 975 | nsTableCellFrame* contCell = static_cast<nsTableCellFrame*>( |
michael@0 | 976 | aPresContext.PresShell()->FrameConstructor()-> |
michael@0 | 977 | CreateContinuingFrame(&aPresContext, cell, &aLastRow)); |
michael@0 | 978 | int32_t colIndex; |
michael@0 | 979 | cell->GetColIndex(colIndex); |
michael@0 | 980 | aContRow->InsertCellFrame(contCell, colIndex); |
michael@0 | 981 | } |
michael@0 | 982 | } |
michael@0 | 983 | } |
michael@0 | 984 | } |
michael@0 | 985 | } |
michael@0 | 986 | } |
michael@0 | 987 | if (!haveRowSpan) { |
michael@0 | 988 | aDesiredHeight = aLastRow.GetRect().YMost(); |
michael@0 | 989 | } |
michael@0 | 990 | } |
michael@0 | 991 | |
michael@0 | 992 | // Remove the next-in-flow of the row, its cells and their cell blocks. This |
michael@0 | 993 | // is necessary in case the row doesn't need a continuation later on or needs |
michael@0 | 994 | // a continuation which doesn't have the same number of cells that now exist. |
michael@0 | 995 | void |
michael@0 | 996 | nsTableRowGroupFrame::UndoContinuedRow(nsPresContext* aPresContext, |
michael@0 | 997 | nsTableRowFrame* aRow) |
michael@0 | 998 | { |
michael@0 | 999 | if (!aRow) return; // allow null aRow to avoid callers doing null checks |
michael@0 | 1000 | |
michael@0 | 1001 | // rowBefore was the prev-sibling of aRow's next-sibling before aRow was created |
michael@0 | 1002 | nsTableRowFrame* rowBefore = (nsTableRowFrame*)aRow->GetPrevInFlow(); |
michael@0 | 1003 | NS_PRECONDITION(mFrames.ContainsFrame(rowBefore), |
michael@0 | 1004 | "rowBefore not in our frame list?"); |
michael@0 | 1005 | |
michael@0 | 1006 | AutoFrameListPtr overflows(aPresContext, StealOverflowFrames()); |
michael@0 | 1007 | if (!rowBefore || !overflows || overflows->IsEmpty() || |
michael@0 | 1008 | overflows->FirstChild() != aRow) { |
michael@0 | 1009 | NS_ERROR("invalid continued row"); |
michael@0 | 1010 | return; |
michael@0 | 1011 | } |
michael@0 | 1012 | |
michael@0 | 1013 | // Destroy aRow, its cells, and their cell blocks. Cell blocks that have split |
michael@0 | 1014 | // will not have reflowed yet to pick up content from any overflow lines. |
michael@0 | 1015 | overflows->DestroyFrame(aRow); |
michael@0 | 1016 | |
michael@0 | 1017 | // Put the overflow rows into our child list |
michael@0 | 1018 | if (!overflows->IsEmpty()) { |
michael@0 | 1019 | mFrames.InsertFrames(nullptr, rowBefore, *overflows); |
michael@0 | 1020 | } |
michael@0 | 1021 | } |
michael@0 | 1022 | |
michael@0 | 1023 | static nsTableRowFrame* |
michael@0 | 1024 | GetRowBefore(nsTableRowFrame& aStartRow, |
michael@0 | 1025 | nsTableRowFrame& aRow) |
michael@0 | 1026 | { |
michael@0 | 1027 | nsTableRowFrame* rowBefore = nullptr; |
michael@0 | 1028 | for (nsTableRowFrame* sib = &aStartRow; sib && (sib != &aRow); sib = sib->GetNextRow()) { |
michael@0 | 1029 | rowBefore = sib; |
michael@0 | 1030 | } |
michael@0 | 1031 | return rowBefore; |
michael@0 | 1032 | } |
michael@0 | 1033 | |
michael@0 | 1034 | nsresult |
michael@0 | 1035 | nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext, |
michael@0 | 1036 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 1037 | const nsHTMLReflowState& aReflowState, |
michael@0 | 1038 | nsTableFrame* aTableFrame, |
michael@0 | 1039 | nsReflowStatus& aStatus, |
michael@0 | 1040 | bool aRowForcedPageBreak) |
michael@0 | 1041 | { |
michael@0 | 1042 | NS_PRECONDITION(aPresContext->IsPaginated(), "SplitRowGroup currently supports only paged media"); |
michael@0 | 1043 | |
michael@0 | 1044 | nsresult rv = NS_OK; |
michael@0 | 1045 | nsTableRowFrame* prevRowFrame = nullptr; |
michael@0 | 1046 | aDesiredSize.Height() = 0; |
michael@0 | 1047 | |
michael@0 | 1048 | nscoord availWidth = aReflowState.AvailableWidth(); |
michael@0 | 1049 | nscoord availHeight = aReflowState.AvailableHeight(); |
michael@0 | 1050 | |
michael@0 | 1051 | const bool borderCollapse = aTableFrame->IsBorderCollapse(); |
michael@0 | 1052 | nscoord cellSpacingY = aTableFrame->GetCellSpacingY(); |
michael@0 | 1053 | |
michael@0 | 1054 | // get the page height |
michael@0 | 1055 | nscoord pageHeight = aPresContext->GetPageSize().height; |
michael@0 | 1056 | NS_ASSERTION(pageHeight != NS_UNCONSTRAINEDSIZE, |
michael@0 | 1057 | "The table shouldn't be split when there should be space"); |
michael@0 | 1058 | |
michael@0 | 1059 | bool isTopOfPage = aReflowState.mFlags.mIsTopOfPage; |
michael@0 | 1060 | nsTableRowFrame* firstRowThisPage = GetFirstRow(); |
michael@0 | 1061 | |
michael@0 | 1062 | // Need to dirty the table's geometry, or else the row might skip |
michael@0 | 1063 | // reflowing its cell as an optimization. |
michael@0 | 1064 | aTableFrame->SetGeometryDirty(); |
michael@0 | 1065 | |
michael@0 | 1066 | // Walk each of the row frames looking for the first row frame that doesn't fit |
michael@0 | 1067 | // in the available space |
michael@0 | 1068 | for (nsTableRowFrame* rowFrame = firstRowThisPage; rowFrame; rowFrame = rowFrame->GetNextRow()) { |
michael@0 | 1069 | bool rowIsOnPage = true; |
michael@0 | 1070 | nsRect rowRect = rowFrame->GetRect(); |
michael@0 | 1071 | // See if the row fits on this page |
michael@0 | 1072 | if (rowRect.YMost() > availHeight) { |
michael@0 | 1073 | nsTableRowFrame* contRow = nullptr; |
michael@0 | 1074 | // Reflow the row in the availabe space and have it split if it is the 1st |
michael@0 | 1075 | // row (on the page) or there is at least 5% of the current page available |
michael@0 | 1076 | // XXX this 5% should be made a preference |
michael@0 | 1077 | if (!prevRowFrame || (availHeight - aDesiredSize.Height() > pageHeight / 20)) { |
michael@0 | 1078 | nsSize availSize(availWidth, std::max(availHeight - rowRect.y, 0)); |
michael@0 | 1079 | // don't let the available height exceed what CalculateRowHeights set for it |
michael@0 | 1080 | availSize.height = std::min(availSize.height, rowRect.height); |
michael@0 | 1081 | |
michael@0 | 1082 | nsHTMLReflowState rowReflowState(aPresContext, aReflowState, |
michael@0 | 1083 | rowFrame, availSize, |
michael@0 | 1084 | -1, -1, |
michael@0 | 1085 | nsHTMLReflowState::CALLER_WILL_INIT); |
michael@0 | 1086 | |
michael@0 | 1087 | InitChildReflowState(*aPresContext, borderCollapse, rowReflowState); |
michael@0 | 1088 | rowReflowState.mFlags.mIsTopOfPage = isTopOfPage; // set top of page |
michael@0 | 1089 | nsHTMLReflowMetrics rowMetrics(aReflowState); |
michael@0 | 1090 | |
michael@0 | 1091 | // Get the old size before we reflow. |
michael@0 | 1092 | nsRect oldRowRect = rowFrame->GetRect(); |
michael@0 | 1093 | nsRect oldRowVisualOverflow = rowFrame->GetVisualOverflowRect(); |
michael@0 | 1094 | |
michael@0 | 1095 | // Reflow the cell with the constrained height. A cell with rowspan >1 will get this |
michael@0 | 1096 | // reflow later during SplitSpanningCells. |
michael@0 | 1097 | rv = ReflowChild(rowFrame, aPresContext, rowMetrics, rowReflowState, |
michael@0 | 1098 | 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus); |
michael@0 | 1099 | if (NS_FAILED(rv)) return rv; |
michael@0 | 1100 | rowFrame->SetSize(nsSize(rowMetrics.Width(), rowMetrics.Height())); |
michael@0 | 1101 | rowFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED); |
michael@0 | 1102 | rowFrame->DidResize(); |
michael@0 | 1103 | |
michael@0 | 1104 | if (!aRowForcedPageBreak && !NS_FRAME_IS_FULLY_COMPLETE(aStatus) && |
michael@0 | 1105 | ShouldAvoidBreakInside(aReflowState)) { |
michael@0 | 1106 | aStatus = NS_INLINE_LINE_BREAK_BEFORE(); |
michael@0 | 1107 | break; |
michael@0 | 1108 | } |
michael@0 | 1109 | |
michael@0 | 1110 | nsTableFrame::InvalidateTableFrame(rowFrame, oldRowRect, |
michael@0 | 1111 | oldRowVisualOverflow, |
michael@0 | 1112 | false); |
michael@0 | 1113 | |
michael@0 | 1114 | if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { |
michael@0 | 1115 | // The row frame is incomplete and all of the rowspan 1 cells' block frames split |
michael@0 | 1116 | if ((rowMetrics.Height() <= rowReflowState.AvailableHeight()) || isTopOfPage) { |
michael@0 | 1117 | // The row stays on this page because either it split ok or we're on the top of page. |
michael@0 | 1118 | // If top of page and the height exceeded the avail height, then there will be data loss |
michael@0 | 1119 | NS_ASSERTION(rowMetrics.Height() <= rowReflowState.AvailableHeight(), |
michael@0 | 1120 | "data loss - incomplete row needed more height than available, on top of page"); |
michael@0 | 1121 | CreateContinuingRowFrame(*aPresContext, *rowFrame, (nsIFrame**)&contRow); |
michael@0 | 1122 | if (contRow) { |
michael@0 | 1123 | aDesiredSize.Height() += rowMetrics.Height(); |
michael@0 | 1124 | if (prevRowFrame) |
michael@0 | 1125 | aDesiredSize.Height() += cellSpacingY; |
michael@0 | 1126 | } |
michael@0 | 1127 | else return NS_ERROR_NULL_POINTER; |
michael@0 | 1128 | } |
michael@0 | 1129 | else { |
michael@0 | 1130 | // Put the row on the next page to give it more height |
michael@0 | 1131 | rowIsOnPage = false; |
michael@0 | 1132 | } |
michael@0 | 1133 | } |
michael@0 | 1134 | else { |
michael@0 | 1135 | // The row frame is complete because either (1) its minimum height is greater than the |
michael@0 | 1136 | // available height we gave it, or (2) it may have been given a larger height through |
michael@0 | 1137 | // style than its content, or (3) it contains a rowspan >1 cell which hasn't been |
michael@0 | 1138 | // reflowed with a constrained height yet (we will find out when SplitSpanningCells is |
michael@0 | 1139 | // called below) |
michael@0 | 1140 | if (rowMetrics.Height() > availSize.height || |
michael@0 | 1141 | (NS_INLINE_IS_BREAK_BEFORE(aStatus) && !aRowForcedPageBreak)) { |
michael@0 | 1142 | // cases (1) and (2) |
michael@0 | 1143 | if (isTopOfPage) { |
michael@0 | 1144 | // We're on top of the page, so keep the row on this page. There will be data loss. |
michael@0 | 1145 | // Push the row frame that follows |
michael@0 | 1146 | nsTableRowFrame* nextRowFrame = rowFrame->GetNextRow(); |
michael@0 | 1147 | if (nextRowFrame) { |
michael@0 | 1148 | aStatus = NS_FRAME_NOT_COMPLETE; |
michael@0 | 1149 | } |
michael@0 | 1150 | aDesiredSize.Height() += rowMetrics.Height(); |
michael@0 | 1151 | if (prevRowFrame) |
michael@0 | 1152 | aDesiredSize.Height() += cellSpacingY; |
michael@0 | 1153 | NS_WARNING("data loss - complete row needed more height than available, on top of page"); |
michael@0 | 1154 | } |
michael@0 | 1155 | else { |
michael@0 | 1156 | // We're not on top of the page, so put the row on the next page to give it more height |
michael@0 | 1157 | rowIsOnPage = false; |
michael@0 | 1158 | } |
michael@0 | 1159 | } |
michael@0 | 1160 | } |
michael@0 | 1161 | } //if (!prevRowFrame || (availHeight - aDesiredSize.Height() > pageHeight / 20)) |
michael@0 | 1162 | else { |
michael@0 | 1163 | // put the row on the next page to give it more height |
michael@0 | 1164 | rowIsOnPage = false; |
michael@0 | 1165 | } |
michael@0 | 1166 | |
michael@0 | 1167 | nsTableRowFrame* lastRowThisPage = rowFrame; |
michael@0 | 1168 | nscoord spanningRowBottom = availHeight; |
michael@0 | 1169 | if (!rowIsOnPage) { |
michael@0 | 1170 | NS_ASSERTION(!contRow, "We should not have created a continuation if none of this row fits"); |
michael@0 | 1171 | if (!aRowForcedPageBreak && ShouldAvoidBreakInside(aReflowState)) { |
michael@0 | 1172 | aStatus = NS_INLINE_LINE_BREAK_BEFORE(); |
michael@0 | 1173 | break; |
michael@0 | 1174 | } |
michael@0 | 1175 | if (prevRowFrame) { |
michael@0 | 1176 | spanningRowBottom = prevRowFrame->GetRect().YMost(); |
michael@0 | 1177 | lastRowThisPage = prevRowFrame; |
michael@0 | 1178 | isTopOfPage = (lastRowThisPage == firstRowThisPage) && aReflowState.mFlags.mIsTopOfPage; |
michael@0 | 1179 | aStatus = NS_FRAME_NOT_COMPLETE; |
michael@0 | 1180 | } |
michael@0 | 1181 | else { |
michael@0 | 1182 | // We can't push children, so let our parent reflow us again with more space |
michael@0 | 1183 | aDesiredSize.Height() = rowRect.YMost(); |
michael@0 | 1184 | aStatus = NS_FRAME_COMPLETE; |
michael@0 | 1185 | break; |
michael@0 | 1186 | } |
michael@0 | 1187 | } |
michael@0 | 1188 | // reflow the cells with rowspan >1 that occur on the page |
michael@0 | 1189 | |
michael@0 | 1190 | nsTableRowFrame* firstTruncatedRow; |
michael@0 | 1191 | nscoord yMost; |
michael@0 | 1192 | SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, *firstRowThisPage, |
michael@0 | 1193 | *lastRowThisPage, aReflowState.mFlags.mIsTopOfPage, spanningRowBottom, contRow, |
michael@0 | 1194 | firstTruncatedRow, yMost); |
michael@0 | 1195 | if (firstTruncatedRow) { |
michael@0 | 1196 | // A rowspan >1 cell did not fit (and could not split) in the space we gave it |
michael@0 | 1197 | if (firstTruncatedRow == firstRowThisPage) { |
michael@0 | 1198 | if (aReflowState.mFlags.mIsTopOfPage) { |
michael@0 | 1199 | NS_WARNING("data loss in a row spanned cell"); |
michael@0 | 1200 | } |
michael@0 | 1201 | else { |
michael@0 | 1202 | // We can't push children, so let our parent reflow us again with more space |
michael@0 | 1203 | aDesiredSize.Height() = rowRect.YMost(); |
michael@0 | 1204 | aStatus = NS_FRAME_COMPLETE; |
michael@0 | 1205 | UndoContinuedRow(aPresContext, contRow); |
michael@0 | 1206 | contRow = nullptr; |
michael@0 | 1207 | } |
michael@0 | 1208 | } |
michael@0 | 1209 | else { // (firstTruncatedRow != firstRowThisPage) |
michael@0 | 1210 | // Try to put firstTruncateRow on the next page |
michael@0 | 1211 | nsTableRowFrame* rowBefore = ::GetRowBefore(*firstRowThisPage, *firstTruncatedRow); |
michael@0 | 1212 | nscoord oldSpanningRowBottom = spanningRowBottom; |
michael@0 | 1213 | spanningRowBottom = rowBefore->GetRect().YMost(); |
michael@0 | 1214 | |
michael@0 | 1215 | UndoContinuedRow(aPresContext, contRow); |
michael@0 | 1216 | contRow = nullptr; |
michael@0 | 1217 | nsTableRowFrame* oldLastRowThisPage = lastRowThisPage; |
michael@0 | 1218 | lastRowThisPage = rowBefore; |
michael@0 | 1219 | aStatus = NS_FRAME_NOT_COMPLETE; |
michael@0 | 1220 | |
michael@0 | 1221 | // Call SplitSpanningCells again with rowBefore as the last row on the page |
michael@0 | 1222 | SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, |
michael@0 | 1223 | *firstRowThisPage, *rowBefore, aReflowState.mFlags.mIsTopOfPage, |
michael@0 | 1224 | spanningRowBottom, contRow, firstTruncatedRow, aDesiredSize.Height()); |
michael@0 | 1225 | if (firstTruncatedRow) { |
michael@0 | 1226 | if (aReflowState.mFlags.mIsTopOfPage) { |
michael@0 | 1227 | // We were better off with the 1st call to SplitSpanningCells, do it again |
michael@0 | 1228 | UndoContinuedRow(aPresContext, contRow); |
michael@0 | 1229 | contRow = nullptr; |
michael@0 | 1230 | lastRowThisPage = oldLastRowThisPage; |
michael@0 | 1231 | spanningRowBottom = oldSpanningRowBottom; |
michael@0 | 1232 | SplitSpanningCells(*aPresContext, aReflowState, *aTableFrame, *firstRowThisPage, |
michael@0 | 1233 | *lastRowThisPage, aReflowState.mFlags.mIsTopOfPage, spanningRowBottom, contRow, |
michael@0 | 1234 | firstTruncatedRow, aDesiredSize.Height()); |
michael@0 | 1235 | NS_WARNING("data loss in a row spanned cell"); |
michael@0 | 1236 | } |
michael@0 | 1237 | else { |
michael@0 | 1238 | // Let our parent reflow us again with more space |
michael@0 | 1239 | aDesiredSize.Height() = rowRect.YMost(); |
michael@0 | 1240 | aStatus = NS_FRAME_COMPLETE; |
michael@0 | 1241 | UndoContinuedRow(aPresContext, contRow); |
michael@0 | 1242 | contRow = nullptr; |
michael@0 | 1243 | } |
michael@0 | 1244 | } |
michael@0 | 1245 | } // if (firstTruncatedRow == firstRowThisPage) |
michael@0 | 1246 | } // if (firstTruncatedRow) |
michael@0 | 1247 | else { |
michael@0 | 1248 | aDesiredSize.Height() = std::max(aDesiredSize.Height(), yMost); |
michael@0 | 1249 | if (contRow) { |
michael@0 | 1250 | aStatus = NS_FRAME_NOT_COMPLETE; |
michael@0 | 1251 | } |
michael@0 | 1252 | } |
michael@0 | 1253 | if (NS_FRAME_IS_NOT_COMPLETE(aStatus) && !contRow) { |
michael@0 | 1254 | nsTableRowFrame* nextRow = lastRowThisPage->GetNextRow(); |
michael@0 | 1255 | if (nextRow) { |
michael@0 | 1256 | PushChildren(nextRow, lastRowThisPage); |
michael@0 | 1257 | } |
michael@0 | 1258 | } |
michael@0 | 1259 | break; |
michael@0 | 1260 | } // if (rowRect.YMost() > availHeight) |
michael@0 | 1261 | else { |
michael@0 | 1262 | aDesiredSize.Height() = rowRect.YMost(); |
michael@0 | 1263 | prevRowFrame = rowFrame; |
michael@0 | 1264 | // see if there is a page break after the row |
michael@0 | 1265 | nsTableRowFrame* nextRow = rowFrame->GetNextRow(); |
michael@0 | 1266 | if (nextRow && nsTableFrame::PageBreakAfter(rowFrame, nextRow)) { |
michael@0 | 1267 | PushChildren(nextRow, rowFrame); |
michael@0 | 1268 | aStatus = NS_FRAME_NOT_COMPLETE; |
michael@0 | 1269 | break; |
michael@0 | 1270 | } |
michael@0 | 1271 | } |
michael@0 | 1272 | // after the 1st row that has a height, we can't be on top |
michael@0 | 1273 | // of the page anymore. |
michael@0 | 1274 | isTopOfPage = isTopOfPage && rowRect.YMost() == 0; |
michael@0 | 1275 | } |
michael@0 | 1276 | return NS_OK; |
michael@0 | 1277 | } |
michael@0 | 1278 | |
michael@0 | 1279 | /** Layout the entire row group. |
michael@0 | 1280 | * This method stacks rows vertically according to HTML 4.0 rules. |
michael@0 | 1281 | * Rows are responsible for layout of their children. |
michael@0 | 1282 | */ |
michael@0 | 1283 | nsresult |
michael@0 | 1284 | nsTableRowGroupFrame::Reflow(nsPresContext* aPresContext, |
michael@0 | 1285 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 1286 | const nsHTMLReflowState& aReflowState, |
michael@0 | 1287 | nsReflowStatus& aStatus) |
michael@0 | 1288 | { |
michael@0 | 1289 | DO_GLOBAL_REFLOW_COUNT("nsTableRowGroupFrame"); |
michael@0 | 1290 | DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); |
michael@0 | 1291 | |
michael@0 | 1292 | nsresult rv = NS_OK; |
michael@0 | 1293 | aStatus = NS_FRAME_COMPLETE; |
michael@0 | 1294 | |
michael@0 | 1295 | // Row geometry may be going to change so we need to invalidate any row cursor. |
michael@0 | 1296 | ClearRowCursor(); |
michael@0 | 1297 | |
michael@0 | 1298 | // see if a special height reflow needs to occur due to having a pct height |
michael@0 | 1299 | nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState); |
michael@0 | 1300 | |
michael@0 | 1301 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 1302 | nsRowGroupReflowState state(aReflowState, tableFrame); |
michael@0 | 1303 | const nsStyleVisibility* groupVis = StyleVisibility(); |
michael@0 | 1304 | bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible); |
michael@0 | 1305 | if (collapseGroup) { |
michael@0 | 1306 | tableFrame->SetNeedToCollapse(true); |
michael@0 | 1307 | } |
michael@0 | 1308 | |
michael@0 | 1309 | // Check for an overflow list |
michael@0 | 1310 | MoveOverflowToChildList(); |
michael@0 | 1311 | |
michael@0 | 1312 | // Reflow the existing frames. |
michael@0 | 1313 | bool splitDueToPageBreak = false; |
michael@0 | 1314 | rv = ReflowChildren(aPresContext, aDesiredSize, state, aStatus, |
michael@0 | 1315 | &splitDueToPageBreak); |
michael@0 | 1316 | |
michael@0 | 1317 | // See if all the frames fit. Do not try to split anything if we're |
michael@0 | 1318 | // not paginated ... we can't split across columns yet. |
michael@0 | 1319 | if (aReflowState.mFlags.mTableIsSplittable && |
michael@0 | 1320 | NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() && |
michael@0 | 1321 | (NS_FRAME_NOT_COMPLETE == aStatus || splitDueToPageBreak || |
michael@0 | 1322 | aDesiredSize.Height() > aReflowState.AvailableHeight())) { |
michael@0 | 1323 | // Nope, find a place to split the row group |
michael@0 | 1324 | bool specialReflow = (bool)aReflowState.mFlags.mSpecialHeightReflow; |
michael@0 | 1325 | ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = false; |
michael@0 | 1326 | |
michael@0 | 1327 | SplitRowGroup(aPresContext, aDesiredSize, aReflowState, tableFrame, aStatus, |
michael@0 | 1328 | splitDueToPageBreak); |
michael@0 | 1329 | |
michael@0 | 1330 | ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = specialReflow; |
michael@0 | 1331 | } |
michael@0 | 1332 | |
michael@0 | 1333 | // XXXmats The following is just bogus. We leave it here for now because |
michael@0 | 1334 | // ReflowChildren should pull up rows from our next-in-flow before returning |
michael@0 | 1335 | // a Complete status, but doesn't (bug 804888). |
michael@0 | 1336 | if (GetNextInFlow() && GetNextInFlow()->GetFirstPrincipalChild()) { |
michael@0 | 1337 | NS_FRAME_SET_INCOMPLETE(aStatus); |
michael@0 | 1338 | } |
michael@0 | 1339 | |
michael@0 | 1340 | SetHasStyleHeight((NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()) && |
michael@0 | 1341 | (aReflowState.ComputedHeight() > 0)); |
michael@0 | 1342 | |
michael@0 | 1343 | // just set our width to what was available. The table will calculate the width and not use our value. |
michael@0 | 1344 | aDesiredSize.Width() = aReflowState.AvailableWidth(); |
michael@0 | 1345 | |
michael@0 | 1346 | aDesiredSize.UnionOverflowAreasWithDesiredBounds(); |
michael@0 | 1347 | |
michael@0 | 1348 | // If our parent is in initial reflow, it'll handle invalidating our |
michael@0 | 1349 | // entire overflow rect. |
michael@0 | 1350 | if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) && |
michael@0 | 1351 | nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) { |
michael@0 | 1352 | InvalidateFrame(); |
michael@0 | 1353 | } |
michael@0 | 1354 | |
michael@0 | 1355 | FinishAndStoreOverflow(&aDesiredSize); |
michael@0 | 1356 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
michael@0 | 1357 | return rv; |
michael@0 | 1358 | } |
michael@0 | 1359 | |
michael@0 | 1360 | bool |
michael@0 | 1361 | nsTableRowGroupFrame::UpdateOverflow() |
michael@0 | 1362 | { |
michael@0 | 1363 | // Row cursor invariants depend on the visual overflow area of the rows, |
michael@0 | 1364 | // which may have changed, so we need to clear the cursor now. |
michael@0 | 1365 | ClearRowCursor(); |
michael@0 | 1366 | return nsContainerFrame::UpdateOverflow(); |
michael@0 | 1367 | } |
michael@0 | 1368 | |
michael@0 | 1369 | /* virtual */ void |
michael@0 | 1370 | nsTableRowGroupFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) |
michael@0 | 1371 | { |
michael@0 | 1372 | nsContainerFrame::DidSetStyleContext(aOldStyleContext); |
michael@0 | 1373 | |
michael@0 | 1374 | if (!aOldStyleContext) //avoid this on init |
michael@0 | 1375 | return; |
michael@0 | 1376 | |
michael@0 | 1377 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 1378 | if (tableFrame->IsBorderCollapse() && |
michael@0 | 1379 | tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) { |
michael@0 | 1380 | nsIntRect damageArea(0, GetStartRowIndex(), tableFrame->GetColCount(), |
michael@0 | 1381 | GetRowCount()); |
michael@0 | 1382 | tableFrame->AddBCDamageArea(damageArea); |
michael@0 | 1383 | } |
michael@0 | 1384 | } |
michael@0 | 1385 | |
michael@0 | 1386 | nsresult |
michael@0 | 1387 | nsTableRowGroupFrame::AppendFrames(ChildListID aListID, |
michael@0 | 1388 | nsFrameList& aFrameList) |
michael@0 | 1389 | { |
michael@0 | 1390 | NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
michael@0 | 1391 | |
michael@0 | 1392 | ClearRowCursor(); |
michael@0 | 1393 | |
michael@0 | 1394 | // collect the new row frames in an array |
michael@0 | 1395 | // XXXbz why are we doing the QI stuff? There shouldn't be any non-rows here. |
michael@0 | 1396 | nsAutoTArray<nsTableRowFrame*, 8> rows; |
michael@0 | 1397 | for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { |
michael@0 | 1398 | nsTableRowFrame *rowFrame = do_QueryFrame(e.get()); |
michael@0 | 1399 | NS_ASSERTION(rowFrame, "Unexpected frame; frame constructor screwed up"); |
michael@0 | 1400 | if (rowFrame) { |
michael@0 | 1401 | NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW == |
michael@0 | 1402 | e.get()->StyleDisplay()->mDisplay, |
michael@0 | 1403 | "wrong display type on rowframe"); |
michael@0 | 1404 | rows.AppendElement(rowFrame); |
michael@0 | 1405 | } |
michael@0 | 1406 | } |
michael@0 | 1407 | |
michael@0 | 1408 | int32_t rowIndex = GetRowCount(); |
michael@0 | 1409 | // Append the frames to the sibling chain |
michael@0 | 1410 | mFrames.AppendFrames(nullptr, aFrameList); |
michael@0 | 1411 | |
michael@0 | 1412 | if (rows.Length() > 0) { |
michael@0 | 1413 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 1414 | tableFrame->AppendRows(this, rowIndex, rows); |
michael@0 | 1415 | PresContext()->PresShell()-> |
michael@0 | 1416 | FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
michael@0 | 1417 | NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 1418 | tableFrame->SetGeometryDirty(); |
michael@0 | 1419 | } |
michael@0 | 1420 | |
michael@0 | 1421 | return NS_OK; |
michael@0 | 1422 | } |
michael@0 | 1423 | |
michael@0 | 1424 | nsresult |
michael@0 | 1425 | nsTableRowGroupFrame::InsertFrames(ChildListID aListID, |
michael@0 | 1426 | nsIFrame* aPrevFrame, |
michael@0 | 1427 | nsFrameList& aFrameList) |
michael@0 | 1428 | { |
michael@0 | 1429 | NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
michael@0 | 1430 | NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, |
michael@0 | 1431 | "inserting after sibling frame with different parent"); |
michael@0 | 1432 | |
michael@0 | 1433 | ClearRowCursor(); |
michael@0 | 1434 | |
michael@0 | 1435 | // collect the new row frames in an array |
michael@0 | 1436 | // XXXbz why are we doing the QI stuff? There shouldn't be any non-rows here. |
michael@0 | 1437 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 1438 | nsTArray<nsTableRowFrame*> rows; |
michael@0 | 1439 | bool gotFirstRow = false; |
michael@0 | 1440 | for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { |
michael@0 | 1441 | nsTableRowFrame *rowFrame = do_QueryFrame(e.get()); |
michael@0 | 1442 | NS_ASSERTION(rowFrame, "Unexpected frame; frame constructor screwed up"); |
michael@0 | 1443 | if (rowFrame) { |
michael@0 | 1444 | NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW == |
michael@0 | 1445 | e.get()->StyleDisplay()->mDisplay, |
michael@0 | 1446 | "wrong display type on rowframe"); |
michael@0 | 1447 | rows.AppendElement(rowFrame); |
michael@0 | 1448 | if (!gotFirstRow) { |
michael@0 | 1449 | rowFrame->SetFirstInserted(true); |
michael@0 | 1450 | gotFirstRow = true; |
michael@0 | 1451 | tableFrame->SetRowInserted(true); |
michael@0 | 1452 | } |
michael@0 | 1453 | } |
michael@0 | 1454 | } |
michael@0 | 1455 | |
michael@0 | 1456 | int32_t startRowIndex = GetStartRowIndex(); |
michael@0 | 1457 | // Insert the frames in the sibling chain |
michael@0 | 1458 | mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList); |
michael@0 | 1459 | |
michael@0 | 1460 | int32_t numRows = rows.Length(); |
michael@0 | 1461 | if (numRows > 0) { |
michael@0 | 1462 | nsTableRowFrame* prevRow = (nsTableRowFrame *)nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame, nsGkAtoms::tableRowFrame); |
michael@0 | 1463 | int32_t rowIndex = (prevRow) ? prevRow->GetRowIndex() + 1 : startRowIndex; |
michael@0 | 1464 | tableFrame->InsertRows(this, rows, rowIndex, true); |
michael@0 | 1465 | |
michael@0 | 1466 | PresContext()->PresShell()-> |
michael@0 | 1467 | FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
michael@0 | 1468 | NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 1469 | tableFrame->SetGeometryDirty(); |
michael@0 | 1470 | } |
michael@0 | 1471 | return NS_OK; |
michael@0 | 1472 | } |
michael@0 | 1473 | |
michael@0 | 1474 | nsresult |
michael@0 | 1475 | nsTableRowGroupFrame::RemoveFrame(ChildListID aListID, |
michael@0 | 1476 | nsIFrame* aOldFrame) |
michael@0 | 1477 | { |
michael@0 | 1478 | NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
michael@0 | 1479 | |
michael@0 | 1480 | ClearRowCursor(); |
michael@0 | 1481 | |
michael@0 | 1482 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 1483 | // XXX why are we doing the QI stuff? There shouldn't be any non-rows here. |
michael@0 | 1484 | nsTableRowFrame* rowFrame = do_QueryFrame(aOldFrame); |
michael@0 | 1485 | if (rowFrame) { |
michael@0 | 1486 | // remove the rows from the table (and flag a rebalance) |
michael@0 | 1487 | tableFrame->RemoveRows(*rowFrame, 1, true); |
michael@0 | 1488 | |
michael@0 | 1489 | PresContext()->PresShell()-> |
michael@0 | 1490 | FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
michael@0 | 1491 | NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 1492 | tableFrame->SetGeometryDirty(); |
michael@0 | 1493 | } |
michael@0 | 1494 | mFrames.DestroyFrame(aOldFrame); |
michael@0 | 1495 | |
michael@0 | 1496 | return NS_OK; |
michael@0 | 1497 | } |
michael@0 | 1498 | |
michael@0 | 1499 | /* virtual */ nsMargin |
michael@0 | 1500 | nsTableRowGroupFrame::GetUsedMargin() const |
michael@0 | 1501 | { |
michael@0 | 1502 | return nsMargin(0,0,0,0); |
michael@0 | 1503 | } |
michael@0 | 1504 | |
michael@0 | 1505 | /* virtual */ nsMargin |
michael@0 | 1506 | nsTableRowGroupFrame::GetUsedBorder() const |
michael@0 | 1507 | { |
michael@0 | 1508 | return nsMargin(0,0,0,0); |
michael@0 | 1509 | } |
michael@0 | 1510 | |
michael@0 | 1511 | /* virtual */ nsMargin |
michael@0 | 1512 | nsTableRowGroupFrame::GetUsedPadding() const |
michael@0 | 1513 | { |
michael@0 | 1514 | return nsMargin(0,0,0,0); |
michael@0 | 1515 | } |
michael@0 | 1516 | |
michael@0 | 1517 | nscoord |
michael@0 | 1518 | nsTableRowGroupFrame::GetHeightBasis(const nsHTMLReflowState& aReflowState) |
michael@0 | 1519 | { |
michael@0 | 1520 | nscoord result = 0; |
michael@0 | 1521 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 1522 | if ((aReflowState.ComputedHeight() > 0) && (aReflowState.ComputedHeight() < NS_UNCONSTRAINEDSIZE)) { |
michael@0 | 1523 | nscoord cellSpacing = std::max(0, GetRowCount() - 1) * tableFrame->GetCellSpacingY(); |
michael@0 | 1524 | result = aReflowState.ComputedHeight() - cellSpacing; |
michael@0 | 1525 | } |
michael@0 | 1526 | else { |
michael@0 | 1527 | const nsHTMLReflowState* parentRS = aReflowState.parentReflowState; |
michael@0 | 1528 | if (parentRS && (tableFrame != parentRS->frame)) { |
michael@0 | 1529 | parentRS = parentRS->parentReflowState; |
michael@0 | 1530 | } |
michael@0 | 1531 | if (parentRS && (tableFrame == parentRS->frame) && |
michael@0 | 1532 | (parentRS->ComputedHeight() > 0) && (parentRS->ComputedHeight() < NS_UNCONSTRAINEDSIZE)) { |
michael@0 | 1533 | nscoord cellSpacing = std::max(0, tableFrame->GetRowCount() + 1) * tableFrame->GetCellSpacingY(); |
michael@0 | 1534 | result = parentRS->ComputedHeight() - cellSpacing; |
michael@0 | 1535 | } |
michael@0 | 1536 | } |
michael@0 | 1537 | |
michael@0 | 1538 | return result; |
michael@0 | 1539 | } |
michael@0 | 1540 | |
michael@0 | 1541 | bool |
michael@0 | 1542 | nsTableRowGroupFrame::IsSimpleRowFrame(nsTableFrame* aTableFrame, |
michael@0 | 1543 | nsIFrame* aFrame) |
michael@0 | 1544 | { |
michael@0 | 1545 | // Make sure it's a row frame and not a row group frame |
michael@0 | 1546 | nsTableRowFrame *rowFrame = do_QueryFrame(aFrame); |
michael@0 | 1547 | if (rowFrame) { |
michael@0 | 1548 | int32_t rowIndex = rowFrame->GetRowIndex(); |
michael@0 | 1549 | |
michael@0 | 1550 | // It's a simple row frame if there are no cells that span into or |
michael@0 | 1551 | // across the row |
michael@0 | 1552 | int32_t numEffCols = aTableFrame->GetEffectiveColCount(); |
michael@0 | 1553 | if (!aTableFrame->RowIsSpannedInto(rowIndex, numEffCols) && |
michael@0 | 1554 | !aTableFrame->RowHasSpanningCells(rowIndex, numEffCols)) { |
michael@0 | 1555 | return true; |
michael@0 | 1556 | } |
michael@0 | 1557 | } |
michael@0 | 1558 | |
michael@0 | 1559 | return false; |
michael@0 | 1560 | } |
michael@0 | 1561 | |
michael@0 | 1562 | nsIAtom* |
michael@0 | 1563 | nsTableRowGroupFrame::GetType() const |
michael@0 | 1564 | { |
michael@0 | 1565 | return nsGkAtoms::tableRowGroupFrame; |
michael@0 | 1566 | } |
michael@0 | 1567 | |
michael@0 | 1568 | /** find page break before the first row **/ |
michael@0 | 1569 | bool |
michael@0 | 1570 | nsTableRowGroupFrame::HasInternalBreakBefore() const |
michael@0 | 1571 | { |
michael@0 | 1572 | nsIFrame* firstChild = mFrames.FirstChild(); |
michael@0 | 1573 | if (!firstChild) |
michael@0 | 1574 | return false; |
michael@0 | 1575 | return firstChild->StyleDisplay()->mBreakBefore; |
michael@0 | 1576 | } |
michael@0 | 1577 | |
michael@0 | 1578 | /** find page break after the last row **/ |
michael@0 | 1579 | bool |
michael@0 | 1580 | nsTableRowGroupFrame::HasInternalBreakAfter() const |
michael@0 | 1581 | { |
michael@0 | 1582 | nsIFrame* lastChild = mFrames.LastChild(); |
michael@0 | 1583 | if (!lastChild) |
michael@0 | 1584 | return false; |
michael@0 | 1585 | return lastChild->StyleDisplay()->mBreakAfter; |
michael@0 | 1586 | } |
michael@0 | 1587 | /* ----- global methods ----- */ |
michael@0 | 1588 | |
michael@0 | 1589 | nsIFrame* |
michael@0 | 1590 | NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
michael@0 | 1591 | { |
michael@0 | 1592 | return new (aPresShell) nsTableRowGroupFrame(aContext); |
michael@0 | 1593 | } |
michael@0 | 1594 | |
michael@0 | 1595 | NS_IMPL_FRAMEARENA_HELPERS(nsTableRowGroupFrame) |
michael@0 | 1596 | |
michael@0 | 1597 | #ifdef DEBUG_FRAME_DUMP |
michael@0 | 1598 | nsresult |
michael@0 | 1599 | nsTableRowGroupFrame::GetFrameName(nsAString& aResult) const |
michael@0 | 1600 | { |
michael@0 | 1601 | return MakeFrameName(NS_LITERAL_STRING("TableRowGroup"), aResult); |
michael@0 | 1602 | } |
michael@0 | 1603 | #endif |
michael@0 | 1604 | |
michael@0 | 1605 | nsMargin* |
michael@0 | 1606 | nsTableRowGroupFrame::GetBCBorderWidth(nsMargin& aBorder) |
michael@0 | 1607 | { |
michael@0 | 1608 | aBorder.left = aBorder.right = aBorder.top = aBorder.bottom = 0; |
michael@0 | 1609 | |
michael@0 | 1610 | nsTableRowFrame* firstRowFrame = nullptr; |
michael@0 | 1611 | nsTableRowFrame* lastRowFrame = nullptr; |
michael@0 | 1612 | for (nsTableRowFrame* rowFrame = GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) { |
michael@0 | 1613 | if (!firstRowFrame) { |
michael@0 | 1614 | firstRowFrame = rowFrame; |
michael@0 | 1615 | } |
michael@0 | 1616 | lastRowFrame = rowFrame; |
michael@0 | 1617 | } |
michael@0 | 1618 | if (firstRowFrame) { |
michael@0 | 1619 | aBorder.top = nsPresContext::CSSPixelsToAppUnits(firstRowFrame->GetTopBCBorderWidth()); |
michael@0 | 1620 | aBorder.bottom = nsPresContext::CSSPixelsToAppUnits(lastRowFrame->GetBottomBCBorderWidth()); |
michael@0 | 1621 | } |
michael@0 | 1622 | |
michael@0 | 1623 | return &aBorder; |
michael@0 | 1624 | } |
michael@0 | 1625 | |
michael@0 | 1626 | void nsTableRowGroupFrame::SetContinuousBCBorderWidth(uint8_t aForSide, |
michael@0 | 1627 | BCPixelSize aPixelValue) |
michael@0 | 1628 | { |
michael@0 | 1629 | switch (aForSide) { |
michael@0 | 1630 | case NS_SIDE_RIGHT: |
michael@0 | 1631 | mRightContBorderWidth = aPixelValue; |
michael@0 | 1632 | return; |
michael@0 | 1633 | case NS_SIDE_BOTTOM: |
michael@0 | 1634 | mBottomContBorderWidth = aPixelValue; |
michael@0 | 1635 | return; |
michael@0 | 1636 | case NS_SIDE_LEFT: |
michael@0 | 1637 | mLeftContBorderWidth = aPixelValue; |
michael@0 | 1638 | return; |
michael@0 | 1639 | default: |
michael@0 | 1640 | NS_ERROR("invalid NS_SIDE argument"); |
michael@0 | 1641 | } |
michael@0 | 1642 | } |
michael@0 | 1643 | |
michael@0 | 1644 | //nsILineIterator methods |
michael@0 | 1645 | int32_t |
michael@0 | 1646 | nsTableRowGroupFrame::GetNumLines() |
michael@0 | 1647 | { |
michael@0 | 1648 | return GetRowCount(); |
michael@0 | 1649 | } |
michael@0 | 1650 | |
michael@0 | 1651 | bool |
michael@0 | 1652 | nsTableRowGroupFrame::GetDirection() |
michael@0 | 1653 | { |
michael@0 | 1654 | nsTableFrame* table = nsTableFrame::GetTableFrame(this); |
michael@0 | 1655 | return (NS_STYLE_DIRECTION_RTL == |
michael@0 | 1656 | table->StyleVisibility()->mDirection); |
michael@0 | 1657 | } |
michael@0 | 1658 | |
michael@0 | 1659 | NS_IMETHODIMP |
michael@0 | 1660 | nsTableRowGroupFrame::GetLine(int32_t aLineNumber, |
michael@0 | 1661 | nsIFrame** aFirstFrameOnLine, |
michael@0 | 1662 | int32_t* aNumFramesOnLine, |
michael@0 | 1663 | nsRect& aLineBounds, |
michael@0 | 1664 | uint32_t* aLineFlags) |
michael@0 | 1665 | { |
michael@0 | 1666 | NS_ENSURE_ARG_POINTER(aFirstFrameOnLine); |
michael@0 | 1667 | NS_ENSURE_ARG_POINTER(aNumFramesOnLine); |
michael@0 | 1668 | NS_ENSURE_ARG_POINTER(aLineFlags); |
michael@0 | 1669 | |
michael@0 | 1670 | nsTableFrame* table = nsTableFrame::GetTableFrame(this); |
michael@0 | 1671 | nsTableCellMap* cellMap = table->GetCellMap(); |
michael@0 | 1672 | |
michael@0 | 1673 | *aLineFlags = 0; |
michael@0 | 1674 | *aFirstFrameOnLine = nullptr; |
michael@0 | 1675 | *aNumFramesOnLine = 0; |
michael@0 | 1676 | aLineBounds.SetRect(0, 0, 0, 0); |
michael@0 | 1677 | |
michael@0 | 1678 | if ((aLineNumber < 0) || (aLineNumber >= GetRowCount())) { |
michael@0 | 1679 | return NS_OK; |
michael@0 | 1680 | } |
michael@0 | 1681 | aLineNumber += GetStartRowIndex(); |
michael@0 | 1682 | |
michael@0 | 1683 | *aNumFramesOnLine = cellMap->GetNumCellsOriginatingInRow(aLineNumber); |
michael@0 | 1684 | if (*aNumFramesOnLine == 0) { |
michael@0 | 1685 | return NS_OK; |
michael@0 | 1686 | } |
michael@0 | 1687 | int32_t colCount = table->GetColCount(); |
michael@0 | 1688 | for (int32_t i = 0; i < colCount; i++) { |
michael@0 | 1689 | CellData* data = cellMap->GetDataAt(aLineNumber, i); |
michael@0 | 1690 | if (data && data->IsOrig()) { |
michael@0 | 1691 | *aFirstFrameOnLine = (nsIFrame*)data->GetCellFrame(); |
michael@0 | 1692 | nsIFrame* parent = (*aFirstFrameOnLine)->GetParent(); |
michael@0 | 1693 | aLineBounds = parent->GetRect(); |
michael@0 | 1694 | return NS_OK; |
michael@0 | 1695 | } |
michael@0 | 1696 | } |
michael@0 | 1697 | NS_ERROR("cellmap is lying"); |
michael@0 | 1698 | return NS_ERROR_FAILURE; |
michael@0 | 1699 | } |
michael@0 | 1700 | |
michael@0 | 1701 | int32_t |
michael@0 | 1702 | nsTableRowGroupFrame::FindLineContaining(nsIFrame* aFrame, int32_t aStartLine) |
michael@0 | 1703 | { |
michael@0 | 1704 | NS_ENSURE_TRUE(aFrame, -1); |
michael@0 | 1705 | |
michael@0 | 1706 | nsTableRowFrame *rowFrame = do_QueryFrame(aFrame); |
michael@0 | 1707 | NS_ASSERTION(rowFrame, "RowGroup contains a frame that is not a row"); |
michael@0 | 1708 | |
michael@0 | 1709 | int32_t rowIndexInGroup = rowFrame->GetRowIndex() - GetStartRowIndex(); |
michael@0 | 1710 | |
michael@0 | 1711 | return rowIndexInGroup >= aStartLine ? rowIndexInGroup : -1; |
michael@0 | 1712 | } |
michael@0 | 1713 | |
michael@0 | 1714 | NS_IMETHODIMP |
michael@0 | 1715 | nsTableRowGroupFrame::CheckLineOrder(int32_t aLine, |
michael@0 | 1716 | bool *aIsReordered, |
michael@0 | 1717 | nsIFrame **aFirstVisual, |
michael@0 | 1718 | nsIFrame **aLastVisual) |
michael@0 | 1719 | { |
michael@0 | 1720 | *aIsReordered = false; |
michael@0 | 1721 | *aFirstVisual = nullptr; |
michael@0 | 1722 | *aLastVisual = nullptr; |
michael@0 | 1723 | return NS_OK; |
michael@0 | 1724 | } |
michael@0 | 1725 | |
michael@0 | 1726 | NS_IMETHODIMP |
michael@0 | 1727 | nsTableRowGroupFrame::FindFrameAt(int32_t aLineNumber, |
michael@0 | 1728 | nscoord aX, |
michael@0 | 1729 | nsIFrame** aFrameFound, |
michael@0 | 1730 | bool* aXIsBeforeFirstFrame, |
michael@0 | 1731 | bool* aXIsAfterLastFrame) |
michael@0 | 1732 | { |
michael@0 | 1733 | nsTableFrame* table = nsTableFrame::GetTableFrame(this); |
michael@0 | 1734 | nsTableCellMap* cellMap = table->GetCellMap(); |
michael@0 | 1735 | |
michael@0 | 1736 | *aFrameFound = nullptr; |
michael@0 | 1737 | *aXIsBeforeFirstFrame = true; |
michael@0 | 1738 | *aXIsAfterLastFrame = false; |
michael@0 | 1739 | |
michael@0 | 1740 | aLineNumber += GetStartRowIndex(); |
michael@0 | 1741 | int32_t numCells = cellMap->GetNumCellsOriginatingInRow(aLineNumber); |
michael@0 | 1742 | if (numCells == 0) { |
michael@0 | 1743 | return NS_OK; |
michael@0 | 1744 | } |
michael@0 | 1745 | |
michael@0 | 1746 | nsIFrame* frame = nullptr; |
michael@0 | 1747 | int32_t colCount = table->GetColCount(); |
michael@0 | 1748 | for (int32_t i = 0; i < colCount; i++) { |
michael@0 | 1749 | CellData* data = cellMap->GetDataAt(aLineNumber, i); |
michael@0 | 1750 | if (data && data->IsOrig()) { |
michael@0 | 1751 | frame = (nsIFrame*)data->GetCellFrame(); |
michael@0 | 1752 | break; |
michael@0 | 1753 | } |
michael@0 | 1754 | } |
michael@0 | 1755 | NS_ASSERTION(frame, "cellmap is lying"); |
michael@0 | 1756 | bool isRTL = (NS_STYLE_DIRECTION_RTL == |
michael@0 | 1757 | table->StyleVisibility()->mDirection); |
michael@0 | 1758 | |
michael@0 | 1759 | nsIFrame* closestFromLeft = nullptr; |
michael@0 | 1760 | nsIFrame* closestFromRight = nullptr; |
michael@0 | 1761 | int32_t n = numCells; |
michael@0 | 1762 | nsIFrame* firstFrame = frame; |
michael@0 | 1763 | while (n--) { |
michael@0 | 1764 | nsRect rect = frame->GetRect(); |
michael@0 | 1765 | if (rect.width > 0) { |
michael@0 | 1766 | // If aX is inside this frame - this is it |
michael@0 | 1767 | if (rect.x <= aX && rect.XMost() > aX) { |
michael@0 | 1768 | closestFromLeft = closestFromRight = frame; |
michael@0 | 1769 | break; |
michael@0 | 1770 | } |
michael@0 | 1771 | if (rect.x < aX) { |
michael@0 | 1772 | if (!closestFromLeft || |
michael@0 | 1773 | rect.XMost() > closestFromLeft->GetRect().XMost()) |
michael@0 | 1774 | closestFromLeft = frame; |
michael@0 | 1775 | } |
michael@0 | 1776 | else { |
michael@0 | 1777 | if (!closestFromRight || |
michael@0 | 1778 | rect.x < closestFromRight->GetRect().x) |
michael@0 | 1779 | closestFromRight = frame; |
michael@0 | 1780 | } |
michael@0 | 1781 | } |
michael@0 | 1782 | frame = frame->GetNextSibling(); |
michael@0 | 1783 | } |
michael@0 | 1784 | if (!closestFromLeft && !closestFromRight) { |
michael@0 | 1785 | // All frames were zero-width. Just take the first one. |
michael@0 | 1786 | closestFromLeft = closestFromRight = firstFrame; |
michael@0 | 1787 | } |
michael@0 | 1788 | *aXIsBeforeFirstFrame = isRTL ? !closestFromRight : !closestFromLeft; |
michael@0 | 1789 | *aXIsAfterLastFrame = isRTL ? !closestFromLeft : !closestFromRight; |
michael@0 | 1790 | if (closestFromLeft == closestFromRight) { |
michael@0 | 1791 | *aFrameFound = closestFromLeft; |
michael@0 | 1792 | } |
michael@0 | 1793 | else if (!closestFromLeft) { |
michael@0 | 1794 | *aFrameFound = closestFromRight; |
michael@0 | 1795 | } |
michael@0 | 1796 | else if (!closestFromRight) { |
michael@0 | 1797 | *aFrameFound = closestFromLeft; |
michael@0 | 1798 | } |
michael@0 | 1799 | else { // we're between two frames |
michael@0 | 1800 | nscoord delta = closestFromRight->GetRect().x - |
michael@0 | 1801 | closestFromLeft->GetRect().XMost(); |
michael@0 | 1802 | if (aX < closestFromLeft->GetRect().XMost() + delta/2) |
michael@0 | 1803 | *aFrameFound = closestFromLeft; |
michael@0 | 1804 | else |
michael@0 | 1805 | *aFrameFound = closestFromRight; |
michael@0 | 1806 | } |
michael@0 | 1807 | return NS_OK; |
michael@0 | 1808 | } |
michael@0 | 1809 | |
michael@0 | 1810 | NS_IMETHODIMP |
michael@0 | 1811 | nsTableRowGroupFrame::GetNextSiblingOnLine(nsIFrame*& aFrame, |
michael@0 | 1812 | int32_t aLineNumber) |
michael@0 | 1813 | { |
michael@0 | 1814 | NS_ENSURE_ARG_POINTER(aFrame); |
michael@0 | 1815 | aFrame = aFrame->GetNextSibling(); |
michael@0 | 1816 | return NS_OK; |
michael@0 | 1817 | } |
michael@0 | 1818 | |
michael@0 | 1819 | //end nsLineIterator methods |
michael@0 | 1820 | |
michael@0 | 1821 | static void |
michael@0 | 1822 | DestroyFrameCursorData(void* aPropertyValue) |
michael@0 | 1823 | { |
michael@0 | 1824 | delete static_cast<nsTableRowGroupFrame::FrameCursorData*>(aPropertyValue); |
michael@0 | 1825 | } |
michael@0 | 1826 | |
michael@0 | 1827 | NS_DECLARE_FRAME_PROPERTY(RowCursorProperty, DestroyFrameCursorData) |
michael@0 | 1828 | |
michael@0 | 1829 | void |
michael@0 | 1830 | nsTableRowGroupFrame::ClearRowCursor() |
michael@0 | 1831 | { |
michael@0 | 1832 | if (!(GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR)) |
michael@0 | 1833 | return; |
michael@0 | 1834 | |
michael@0 | 1835 | RemoveStateBits(NS_ROWGROUP_HAS_ROW_CURSOR); |
michael@0 | 1836 | Properties().Delete(RowCursorProperty()); |
michael@0 | 1837 | } |
michael@0 | 1838 | |
michael@0 | 1839 | nsTableRowGroupFrame::FrameCursorData* |
michael@0 | 1840 | nsTableRowGroupFrame::SetupRowCursor() |
michael@0 | 1841 | { |
michael@0 | 1842 | if (GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR) { |
michael@0 | 1843 | // We already have a valid row cursor. Don't waste time rebuilding it. |
michael@0 | 1844 | return nullptr; |
michael@0 | 1845 | } |
michael@0 | 1846 | |
michael@0 | 1847 | nsIFrame* f = mFrames.FirstChild(); |
michael@0 | 1848 | int32_t count; |
michael@0 | 1849 | for (count = 0; f && count < MIN_ROWS_NEEDING_CURSOR; ++count) { |
michael@0 | 1850 | f = f->GetNextSibling(); |
michael@0 | 1851 | } |
michael@0 | 1852 | if (!f) { |
michael@0 | 1853 | // Less than MIN_ROWS_NEEDING_CURSOR rows, so just don't bother |
michael@0 | 1854 | return nullptr; |
michael@0 | 1855 | } |
michael@0 | 1856 | |
michael@0 | 1857 | FrameCursorData* data = new FrameCursorData(); |
michael@0 | 1858 | if (!data) |
michael@0 | 1859 | return nullptr; |
michael@0 | 1860 | Properties().Set(RowCursorProperty(), data); |
michael@0 | 1861 | AddStateBits(NS_ROWGROUP_HAS_ROW_CURSOR); |
michael@0 | 1862 | return data; |
michael@0 | 1863 | } |
michael@0 | 1864 | |
michael@0 | 1865 | nsIFrame* |
michael@0 | 1866 | nsTableRowGroupFrame::GetFirstRowContaining(nscoord aY, nscoord* aOverflowAbove) |
michael@0 | 1867 | { |
michael@0 | 1868 | if (!(GetStateBits() & NS_ROWGROUP_HAS_ROW_CURSOR)) |
michael@0 | 1869 | return nullptr; |
michael@0 | 1870 | |
michael@0 | 1871 | FrameCursorData* property = static_cast<FrameCursorData*> |
michael@0 | 1872 | (Properties().Get(RowCursorProperty())); |
michael@0 | 1873 | uint32_t cursorIndex = property->mCursorIndex; |
michael@0 | 1874 | uint32_t frameCount = property->mFrames.Length(); |
michael@0 | 1875 | if (cursorIndex >= frameCount) |
michael@0 | 1876 | return nullptr; |
michael@0 | 1877 | nsIFrame* cursorFrame = property->mFrames[cursorIndex]; |
michael@0 | 1878 | |
michael@0 | 1879 | // The cursor's frame list excludes frames with empty overflow-area, so |
michael@0 | 1880 | // we don't need to check that here. |
michael@0 | 1881 | |
michael@0 | 1882 | // We use property->mOverflowBelow here instead of computing the frame's |
michael@0 | 1883 | // true overflowArea.YMost(), because it is essential for the thresholds |
michael@0 | 1884 | // to form a monotonically increasing sequence. Otherwise we would break |
michael@0 | 1885 | // encountering a row whose overflowArea.YMost() is <= aY but which has |
michael@0 | 1886 | // a row above it containing cell(s) that span to include aY. |
michael@0 | 1887 | while (cursorIndex > 0 && |
michael@0 | 1888 | cursorFrame->GetRect().YMost() + property->mOverflowBelow > aY) { |
michael@0 | 1889 | --cursorIndex; |
michael@0 | 1890 | cursorFrame = property->mFrames[cursorIndex]; |
michael@0 | 1891 | } |
michael@0 | 1892 | while (cursorIndex + 1 < frameCount && |
michael@0 | 1893 | cursorFrame->GetRect().YMost() + property->mOverflowBelow <= aY) { |
michael@0 | 1894 | ++cursorIndex; |
michael@0 | 1895 | cursorFrame = property->mFrames[cursorIndex]; |
michael@0 | 1896 | } |
michael@0 | 1897 | |
michael@0 | 1898 | property->mCursorIndex = cursorIndex; |
michael@0 | 1899 | *aOverflowAbove = property->mOverflowAbove; |
michael@0 | 1900 | return cursorFrame; |
michael@0 | 1901 | } |
michael@0 | 1902 | |
michael@0 | 1903 | bool |
michael@0 | 1904 | nsTableRowGroupFrame::FrameCursorData::AppendFrame(nsIFrame* aFrame) |
michael@0 | 1905 | { |
michael@0 | 1906 | nsRect overflowRect = aFrame->GetVisualOverflowRect(); |
michael@0 | 1907 | if (overflowRect.IsEmpty()) |
michael@0 | 1908 | return true; |
michael@0 | 1909 | nscoord overflowAbove = -overflowRect.y; |
michael@0 | 1910 | nscoord overflowBelow = overflowRect.YMost() - aFrame->GetSize().height; |
michael@0 | 1911 | mOverflowAbove = std::max(mOverflowAbove, overflowAbove); |
michael@0 | 1912 | mOverflowBelow = std::max(mOverflowBelow, overflowBelow); |
michael@0 | 1913 | return mFrames.AppendElement(aFrame) != nullptr; |
michael@0 | 1914 | } |
michael@0 | 1915 | |
michael@0 | 1916 | void |
michael@0 | 1917 | nsTableRowGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey) |
michael@0 | 1918 | { |
michael@0 | 1919 | nsIFrame::InvalidateFrame(aDisplayItemKey); |
michael@0 | 1920 | GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey); |
michael@0 | 1921 | } |
michael@0 | 1922 | |
michael@0 | 1923 | void |
michael@0 | 1924 | nsTableRowGroupFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey) |
michael@0 | 1925 | { |
michael@0 | 1926 | nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey); |
michael@0 | 1927 | // If we have filters applied that would affects our bounds, then |
michael@0 | 1928 | // we get an inactive layer created and this is computed |
michael@0 | 1929 | // within FrameLayerBuilder |
michael@0 | 1930 | GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey); |
michael@0 | 1931 | } |