layout/tables/nsTableRowGroupFrame.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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 }

mercurial