Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
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 "nsTableRowFrame.h" |
michael@0 | 6 | #include "nsTableRowGroupFrame.h" |
michael@0 | 7 | #include "nsIPresShell.h" |
michael@0 | 8 | #include "nsPresContext.h" |
michael@0 | 9 | #include "nsStyleContext.h" |
michael@0 | 10 | #include "nsStyleConsts.h" |
michael@0 | 11 | #include "nsGkAtoms.h" |
michael@0 | 12 | #include "nsIContent.h" |
michael@0 | 13 | #include "nsTableFrame.h" |
michael@0 | 14 | #include "nsTableCellFrame.h" |
michael@0 | 15 | #include "nsCSSRendering.h" |
michael@0 | 16 | #include "nsHTMLParts.h" |
michael@0 | 17 | #include "nsTableColGroupFrame.h" |
michael@0 | 18 | #include "nsTableColFrame.h" |
michael@0 | 19 | #include "nsCOMPtr.h" |
michael@0 | 20 | #include "nsDisplayList.h" |
michael@0 | 21 | #include "nsIFrameInlines.h" |
michael@0 | 22 | #include <algorithm> |
michael@0 | 23 | |
michael@0 | 24 | using namespace mozilla; |
michael@0 | 25 | |
michael@0 | 26 | struct nsTableCellReflowState : public nsHTMLReflowState |
michael@0 | 27 | { |
michael@0 | 28 | nsTableCellReflowState(nsPresContext* aPresContext, |
michael@0 | 29 | const nsHTMLReflowState& aParentReflowState, |
michael@0 | 30 | nsIFrame* aFrame, |
michael@0 | 31 | const nsSize& aAvailableSpace, |
michael@0 | 32 | uint32_t aFlags = 0) |
michael@0 | 33 | : nsHTMLReflowState(aPresContext, aParentReflowState, aFrame, |
michael@0 | 34 | aAvailableSpace, -1, -1, aFlags) |
michael@0 | 35 | { |
michael@0 | 36 | } |
michael@0 | 37 | |
michael@0 | 38 | void FixUp(const nsSize& aAvailSpace); |
michael@0 | 39 | }; |
michael@0 | 40 | |
michael@0 | 41 | void nsTableCellReflowState::FixUp(const nsSize& aAvailSpace) |
michael@0 | 42 | { |
michael@0 | 43 | // fix the mComputed values during a pass 2 reflow since the cell can be a percentage base |
michael@0 | 44 | NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aAvailSpace.width, |
michael@0 | 45 | "have unconstrained width; this should only result from " |
michael@0 | 46 | "very large sizes, not attempts at intrinsic width " |
michael@0 | 47 | "calculation"); |
michael@0 | 48 | if (NS_UNCONSTRAINEDSIZE != ComputedWidth()) { |
michael@0 | 49 | nscoord computedWidth = |
michael@0 | 50 | aAvailSpace.width - mComputedBorderPadding.LeftRight(); |
michael@0 | 51 | computedWidth = std::max(0, computedWidth); |
michael@0 | 52 | SetComputedWidth(computedWidth); |
michael@0 | 53 | } |
michael@0 | 54 | if (NS_UNCONSTRAINEDSIZE != ComputedHeight() && |
michael@0 | 55 | NS_UNCONSTRAINEDSIZE != aAvailSpace.height) { |
michael@0 | 56 | nscoord computedHeight = |
michael@0 | 57 | aAvailSpace.height - mComputedBorderPadding.TopBottom(); |
michael@0 | 58 | computedHeight = std::max(0, computedHeight); |
michael@0 | 59 | SetComputedHeight(computedHeight); |
michael@0 | 60 | } |
michael@0 | 61 | } |
michael@0 | 62 | |
michael@0 | 63 | void |
michael@0 | 64 | nsTableRowFrame::InitChildReflowState(nsPresContext& aPresContext, |
michael@0 | 65 | const nsSize& aAvailSize, |
michael@0 | 66 | bool aBorderCollapse, |
michael@0 | 67 | nsTableCellReflowState& aReflowState) |
michael@0 | 68 | { |
michael@0 | 69 | nsMargin collapseBorder; |
michael@0 | 70 | nsMargin* pCollapseBorder = nullptr; |
michael@0 | 71 | if (aBorderCollapse) { |
michael@0 | 72 | // we only reflow cells, so don't need to check frame type |
michael@0 | 73 | nsBCTableCellFrame* bcCellFrame = (nsBCTableCellFrame*)aReflowState.frame; |
michael@0 | 74 | if (bcCellFrame) { |
michael@0 | 75 | pCollapseBorder = bcCellFrame->GetBorderWidth(collapseBorder); |
michael@0 | 76 | } |
michael@0 | 77 | } |
michael@0 | 78 | aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder); |
michael@0 | 79 | aReflowState.FixUp(aAvailSize); |
michael@0 | 80 | } |
michael@0 | 81 | |
michael@0 | 82 | void |
michael@0 | 83 | nsTableRowFrame::SetFixedHeight(nscoord aValue) |
michael@0 | 84 | { |
michael@0 | 85 | nscoord height = std::max(0, aValue); |
michael@0 | 86 | if (HasFixedHeight()) { |
michael@0 | 87 | if (height > mStyleFixedHeight) { |
michael@0 | 88 | mStyleFixedHeight = height; |
michael@0 | 89 | } |
michael@0 | 90 | } |
michael@0 | 91 | else { |
michael@0 | 92 | mStyleFixedHeight = height; |
michael@0 | 93 | if (height > 0) { |
michael@0 | 94 | SetHasFixedHeight(true); |
michael@0 | 95 | } |
michael@0 | 96 | } |
michael@0 | 97 | } |
michael@0 | 98 | |
michael@0 | 99 | void |
michael@0 | 100 | nsTableRowFrame::SetPctHeight(float aPctValue, |
michael@0 | 101 | bool aForce) |
michael@0 | 102 | { |
michael@0 | 103 | nscoord height = std::max(0, NSToCoordRound(aPctValue * 100.0f)); |
michael@0 | 104 | if (HasPctHeight()) { |
michael@0 | 105 | if ((height > mStylePctHeight) || aForce) { |
michael@0 | 106 | mStylePctHeight = height; |
michael@0 | 107 | } |
michael@0 | 108 | } |
michael@0 | 109 | else { |
michael@0 | 110 | mStylePctHeight = height; |
michael@0 | 111 | if (height > 0) { |
michael@0 | 112 | SetHasPctHeight(true); |
michael@0 | 113 | } |
michael@0 | 114 | } |
michael@0 | 115 | } |
michael@0 | 116 | |
michael@0 | 117 | /* ----------- nsTableRowFrame ---------- */ |
michael@0 | 118 | |
michael@0 | 119 | NS_QUERYFRAME_HEAD(nsTableRowFrame) |
michael@0 | 120 | NS_QUERYFRAME_ENTRY(nsTableRowFrame) |
michael@0 | 121 | NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) |
michael@0 | 122 | |
michael@0 | 123 | nsTableRowFrame::nsTableRowFrame(nsStyleContext* aContext) |
michael@0 | 124 | : nsContainerFrame(aContext) |
michael@0 | 125 | { |
michael@0 | 126 | mBits.mRowIndex = mBits.mFirstInserted = 0; |
michael@0 | 127 | ResetHeight(0); |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | nsTableRowFrame::~nsTableRowFrame() |
michael@0 | 131 | { |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | void |
michael@0 | 135 | nsTableRowFrame::Init(nsIContent* aContent, |
michael@0 | 136 | nsIFrame* aParent, |
michael@0 | 137 | nsIFrame* aPrevInFlow) |
michael@0 | 138 | { |
michael@0 | 139 | // Let the base class do its initialization |
michael@0 | 140 | nsContainerFrame::Init(aContent, aParent, aPrevInFlow); |
michael@0 | 141 | |
michael@0 | 142 | NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW == StyleDisplay()->mDisplay, |
michael@0 | 143 | "wrong display on table row frame"); |
michael@0 | 144 | |
michael@0 | 145 | if (aPrevInFlow) { |
michael@0 | 146 | // Set the row index |
michael@0 | 147 | nsTableRowFrame* rowFrame = (nsTableRowFrame*)aPrevInFlow; |
michael@0 | 148 | |
michael@0 | 149 | SetRowIndex(rowFrame->GetRowIndex()); |
michael@0 | 150 | } |
michael@0 | 151 | } |
michael@0 | 152 | |
michael@0 | 153 | void |
michael@0 | 154 | nsTableRowFrame::DestroyFrom(nsIFrame* aDestructRoot) |
michael@0 | 155 | { |
michael@0 | 156 | if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) { |
michael@0 | 157 | nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot); |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | nsContainerFrame::DestroyFrom(aDestructRoot); |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | /* virtual */ void |
michael@0 | 164 | nsTableRowFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) |
michael@0 | 165 | { |
michael@0 | 166 | nsContainerFrame::DidSetStyleContext(aOldStyleContext); |
michael@0 | 167 | |
michael@0 | 168 | if (!aOldStyleContext) //avoid this on init |
michael@0 | 169 | return; |
michael@0 | 170 | |
michael@0 | 171 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 172 | if (tableFrame->IsBorderCollapse() && |
michael@0 | 173 | tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) { |
michael@0 | 174 | nsIntRect damageArea(0, GetRowIndex(), tableFrame->GetColCount(), 1); |
michael@0 | 175 | tableFrame->AddBCDamageArea(damageArea); |
michael@0 | 176 | } |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | nsresult |
michael@0 | 180 | nsTableRowFrame::AppendFrames(ChildListID aListID, |
michael@0 | 181 | nsFrameList& aFrameList) |
michael@0 | 182 | { |
michael@0 | 183 | NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
michael@0 | 184 | |
michael@0 | 185 | const nsFrameList::Slice& newCells = mFrames.AppendFrames(nullptr, aFrameList); |
michael@0 | 186 | |
michael@0 | 187 | // Add the new cell frames to the table |
michael@0 | 188 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 189 | for (nsFrameList::Enumerator e(newCells) ; !e.AtEnd(); e.Next()) { |
michael@0 | 190 | nsIFrame *childFrame = e.get(); |
michael@0 | 191 | NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()),"Not a table cell frame/pseudo frame construction failure"); |
michael@0 | 192 | tableFrame->AppendCell(static_cast<nsTableCellFrame&>(*childFrame), GetRowIndex()); |
michael@0 | 193 | } |
michael@0 | 194 | |
michael@0 | 195 | PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
michael@0 | 196 | NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 197 | tableFrame->SetGeometryDirty(); |
michael@0 | 198 | |
michael@0 | 199 | return NS_OK; |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | |
michael@0 | 203 | nsresult |
michael@0 | 204 | nsTableRowFrame::InsertFrames(ChildListID aListID, |
michael@0 | 205 | nsIFrame* aPrevFrame, |
michael@0 | 206 | nsFrameList& aFrameList) |
michael@0 | 207 | { |
michael@0 | 208 | NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
michael@0 | 209 | NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, |
michael@0 | 210 | "inserting after sibling frame with different parent"); |
michael@0 | 211 | //Insert Frames in the frame list |
michael@0 | 212 | const nsFrameList::Slice& newCells = mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList); |
michael@0 | 213 | |
michael@0 | 214 | // Get the table frame |
michael@0 | 215 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 216 | nsIAtom* cellFrameType = tableFrame->IsBorderCollapse() ? nsGkAtoms::bcTableCellFrame : nsGkAtoms::tableCellFrame; |
michael@0 | 217 | nsTableCellFrame* prevCellFrame = (nsTableCellFrame *)nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame, cellFrameType); |
michael@0 | 218 | nsTArray<nsTableCellFrame*> cellChildren; |
michael@0 | 219 | for (nsFrameList::Enumerator e(newCells); !e.AtEnd(); e.Next()) { |
michael@0 | 220 | nsIFrame *childFrame = e.get(); |
michael@0 | 221 | NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()),"Not a table cell frame/pseudo frame construction failure"); |
michael@0 | 222 | cellChildren.AppendElement(static_cast<nsTableCellFrame*>(childFrame)); |
michael@0 | 223 | } |
michael@0 | 224 | // insert the cells into the cell map |
michael@0 | 225 | int32_t colIndex = -1; |
michael@0 | 226 | if (prevCellFrame) { |
michael@0 | 227 | prevCellFrame->GetColIndex(colIndex); |
michael@0 | 228 | } |
michael@0 | 229 | tableFrame->InsertCells(cellChildren, GetRowIndex(), colIndex); |
michael@0 | 230 | |
michael@0 | 231 | PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
michael@0 | 232 | NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 233 | tableFrame->SetGeometryDirty(); |
michael@0 | 234 | |
michael@0 | 235 | return NS_OK; |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | nsresult |
michael@0 | 239 | nsTableRowFrame::RemoveFrame(ChildListID aListID, |
michael@0 | 240 | nsIFrame* aOldFrame) |
michael@0 | 241 | { |
michael@0 | 242 | NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
michael@0 | 243 | |
michael@0 | 244 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 245 | nsTableCellFrame *cellFrame = do_QueryFrame(aOldFrame); |
michael@0 | 246 | if (cellFrame) { |
michael@0 | 247 | int32_t colIndex; |
michael@0 | 248 | cellFrame->GetColIndex(colIndex); |
michael@0 | 249 | // remove the cell from the cell map |
michael@0 | 250 | tableFrame->RemoveCell(cellFrame, GetRowIndex()); |
michael@0 | 251 | |
michael@0 | 252 | // Remove the frame and destroy it |
michael@0 | 253 | mFrames.DestroyFrame(aOldFrame); |
michael@0 | 254 | |
michael@0 | 255 | PresContext()->PresShell()-> |
michael@0 | 256 | FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
michael@0 | 257 | NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 258 | tableFrame->SetGeometryDirty(); |
michael@0 | 259 | } |
michael@0 | 260 | else { |
michael@0 | 261 | NS_ERROR("unexpected frame type"); |
michael@0 | 262 | return NS_ERROR_INVALID_ARG; |
michael@0 | 263 | } |
michael@0 | 264 | |
michael@0 | 265 | return NS_OK; |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | /* virtual */ nsMargin |
michael@0 | 269 | nsTableRowFrame::GetUsedMargin() const |
michael@0 | 270 | { |
michael@0 | 271 | return nsMargin(0,0,0,0); |
michael@0 | 272 | } |
michael@0 | 273 | |
michael@0 | 274 | /* virtual */ nsMargin |
michael@0 | 275 | nsTableRowFrame::GetUsedBorder() const |
michael@0 | 276 | { |
michael@0 | 277 | return nsMargin(0,0,0,0); |
michael@0 | 278 | } |
michael@0 | 279 | |
michael@0 | 280 | /* virtual */ nsMargin |
michael@0 | 281 | nsTableRowFrame::GetUsedPadding() const |
michael@0 | 282 | { |
michael@0 | 283 | return nsMargin(0,0,0,0); |
michael@0 | 284 | } |
michael@0 | 285 | |
michael@0 | 286 | nscoord |
michael@0 | 287 | GetHeightOfRowsSpannedBelowFirst(nsTableCellFrame& aTableCellFrame, |
michael@0 | 288 | nsTableFrame& aTableFrame) |
michael@0 | 289 | { |
michael@0 | 290 | nscoord height = 0; |
michael@0 | 291 | nscoord cellSpacingY = aTableFrame.GetCellSpacingY(); |
michael@0 | 292 | int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(aTableCellFrame); |
michael@0 | 293 | // add in height of rows spanned beyond the 1st one |
michael@0 | 294 | nsIFrame* nextRow = aTableCellFrame.GetParent()->GetNextSibling(); |
michael@0 | 295 | for (int32_t rowX = 1; ((rowX < rowSpan) && nextRow);) { |
michael@0 | 296 | if (nsGkAtoms::tableRowFrame == nextRow->GetType()) { |
michael@0 | 297 | height += nextRow->GetSize().height; |
michael@0 | 298 | rowX++; |
michael@0 | 299 | } |
michael@0 | 300 | height += cellSpacingY; |
michael@0 | 301 | nextRow = nextRow->GetNextSibling(); |
michael@0 | 302 | } |
michael@0 | 303 | return height; |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | nsTableCellFrame* |
michael@0 | 307 | nsTableRowFrame::GetFirstCell() |
michael@0 | 308 | { |
michael@0 | 309 | nsIFrame* childFrame = mFrames.FirstChild(); |
michael@0 | 310 | while (childFrame) { |
michael@0 | 311 | nsTableCellFrame *cellFrame = do_QueryFrame(childFrame); |
michael@0 | 312 | if (cellFrame) { |
michael@0 | 313 | return cellFrame; |
michael@0 | 314 | } |
michael@0 | 315 | childFrame = childFrame->GetNextSibling(); |
michael@0 | 316 | } |
michael@0 | 317 | return nullptr; |
michael@0 | 318 | } |
michael@0 | 319 | |
michael@0 | 320 | /** |
michael@0 | 321 | * Post-reflow hook. This is where the table row does its post-processing |
michael@0 | 322 | */ |
michael@0 | 323 | void |
michael@0 | 324 | nsTableRowFrame::DidResize() |
michael@0 | 325 | { |
michael@0 | 326 | // Resize and re-align the cell frames based on our row height |
michael@0 | 327 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 328 | nsTableIterator iter(*this); |
michael@0 | 329 | nsIFrame* childFrame = iter.First(); |
michael@0 | 330 | |
michael@0 | 331 | nsHTMLReflowMetrics desiredSize(GetWritingMode()); // ??? |
michael@0 | 332 | desiredSize.Width() = mRect.width; |
michael@0 | 333 | desiredSize.Height() = mRect.height; |
michael@0 | 334 | desiredSize.SetOverflowAreasToDesiredBounds(); |
michael@0 | 335 | |
michael@0 | 336 | while (childFrame) { |
michael@0 | 337 | nsTableCellFrame *cellFrame = do_QueryFrame(childFrame); |
michael@0 | 338 | if (cellFrame) { |
michael@0 | 339 | nscoord cellHeight = mRect.height + GetHeightOfRowsSpannedBelowFirst(*cellFrame, *tableFrame); |
michael@0 | 340 | |
michael@0 | 341 | // resize the cell's height |
michael@0 | 342 | nsRect cellRect = cellFrame->GetRect(); |
michael@0 | 343 | nsRect cellVisualOverflow = cellFrame->GetVisualOverflowRect(); |
michael@0 | 344 | if (cellRect.height != cellHeight) |
michael@0 | 345 | { |
michael@0 | 346 | cellFrame->SetSize(nsSize(cellRect.width, cellHeight)); |
michael@0 | 347 | nsTableFrame::InvalidateTableFrame(cellFrame, cellRect, |
michael@0 | 348 | cellVisualOverflow, |
michael@0 | 349 | false); |
michael@0 | 350 | } |
michael@0 | 351 | |
michael@0 | 352 | // realign cell content based on the new height. We might be able to |
michael@0 | 353 | // skip this if the height didn't change... maybe. Hard to tell. |
michael@0 | 354 | cellFrame->VerticallyAlignChild(mMaxCellAscent); |
michael@0 | 355 | |
michael@0 | 356 | // Always store the overflow, even if the height didn't change, since |
michael@0 | 357 | // we'll lose part of our overflow area otherwise. |
michael@0 | 358 | ConsiderChildOverflow(desiredSize.mOverflowAreas, cellFrame); |
michael@0 | 359 | |
michael@0 | 360 | // Note that if the cell's *content* needs to change in response |
michael@0 | 361 | // to this height, it will get a special height reflow. |
michael@0 | 362 | } |
michael@0 | 363 | // Get the next child |
michael@0 | 364 | childFrame = iter.Next(); |
michael@0 | 365 | } |
michael@0 | 366 | FinishAndStoreOverflow(&desiredSize); |
michael@0 | 367 | if (HasView()) { |
michael@0 | 368 | nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(), |
michael@0 | 369 | desiredSize.VisualOverflow(), 0); |
michael@0 | 370 | } |
michael@0 | 371 | // Let our base class do the usual work |
michael@0 | 372 | } |
michael@0 | 373 | |
michael@0 | 374 | // returns max-ascent amongst all cells that have 'vertical-align: baseline' |
michael@0 | 375 | // *including* cells with rowspans |
michael@0 | 376 | nscoord nsTableRowFrame::GetMaxCellAscent() const |
michael@0 | 377 | { |
michael@0 | 378 | return mMaxCellAscent; |
michael@0 | 379 | } |
michael@0 | 380 | |
michael@0 | 381 | nscoord nsTableRowFrame::GetRowBaseline() |
michael@0 | 382 | { |
michael@0 | 383 | if(mMaxCellAscent) |
michael@0 | 384 | return mMaxCellAscent; |
michael@0 | 385 | |
michael@0 | 386 | // If we don't have a baseline on any of the cells we go for the lowest |
michael@0 | 387 | // content edge of the inner block frames. |
michael@0 | 388 | // Every table cell has a cell frame with its border and padding. Inside |
michael@0 | 389 | // the cell is a block frame. The cell is as high as the tallest cell in |
michael@0 | 390 | // the parent row. As a consequence the block frame might not touch both |
michael@0 | 391 | // the top and the bottom padding of it parent cell frame at the same time. |
michael@0 | 392 | // |
michael@0 | 393 | // bbbbbbbbbbbbbbbbbb cell border: b |
michael@0 | 394 | // bppppppppppppppppb cell padding: p |
michael@0 | 395 | // bpxxxxxxxxxxxxxxpb inner block: x |
michael@0 | 396 | // bpx xpb |
michael@0 | 397 | // bpx xpb |
michael@0 | 398 | // bpx xpb |
michael@0 | 399 | // bpxxxxxxxxxxxxxxpb base line |
michael@0 | 400 | // bp pb |
michael@0 | 401 | // bp pb |
michael@0 | 402 | // bppppppppppppppppb |
michael@0 | 403 | // bbbbbbbbbbbbbbbbbb |
michael@0 | 404 | |
michael@0 | 405 | nsTableIterator iter(*this); |
michael@0 | 406 | nsIFrame* childFrame = iter.First(); |
michael@0 | 407 | nscoord ascent = 0; |
michael@0 | 408 | while (childFrame) { |
michael@0 | 409 | if (IS_TABLE_CELL(childFrame->GetType())) { |
michael@0 | 410 | nsIFrame* firstKid = childFrame->GetFirstPrincipalChild(); |
michael@0 | 411 | ascent = std::max(ascent, firstKid->GetRect().YMost()); |
michael@0 | 412 | } |
michael@0 | 413 | // Get the next child |
michael@0 | 414 | childFrame = iter.Next(); |
michael@0 | 415 | } |
michael@0 | 416 | return ascent; |
michael@0 | 417 | } |
michael@0 | 418 | nscoord |
michael@0 | 419 | nsTableRowFrame::GetHeight(nscoord aPctBasis) const |
michael@0 | 420 | { |
michael@0 | 421 | nscoord height = 0; |
michael@0 | 422 | if ((aPctBasis > 0) && HasPctHeight()) { |
michael@0 | 423 | height = NSToCoordRound(GetPctHeight() * (float)aPctBasis); |
michael@0 | 424 | } |
michael@0 | 425 | if (HasFixedHeight()) { |
michael@0 | 426 | height = std::max(height, GetFixedHeight()); |
michael@0 | 427 | } |
michael@0 | 428 | return std::max(height, GetContentHeight()); |
michael@0 | 429 | } |
michael@0 | 430 | |
michael@0 | 431 | void |
michael@0 | 432 | nsTableRowFrame::ResetHeight(nscoord aFixedHeight) |
michael@0 | 433 | { |
michael@0 | 434 | SetHasFixedHeight(false); |
michael@0 | 435 | SetHasPctHeight(false); |
michael@0 | 436 | SetFixedHeight(0); |
michael@0 | 437 | SetPctHeight(0); |
michael@0 | 438 | SetContentHeight(0); |
michael@0 | 439 | |
michael@0 | 440 | if (aFixedHeight > 0) { |
michael@0 | 441 | SetFixedHeight(aFixedHeight); |
michael@0 | 442 | } |
michael@0 | 443 | |
michael@0 | 444 | mMaxCellAscent = 0; |
michael@0 | 445 | mMaxCellDescent = 0; |
michael@0 | 446 | } |
michael@0 | 447 | |
michael@0 | 448 | void |
michael@0 | 449 | nsTableRowFrame::UpdateHeight(nscoord aHeight, |
michael@0 | 450 | nscoord aAscent, |
michael@0 | 451 | nscoord aDescent, |
michael@0 | 452 | nsTableFrame* aTableFrame, |
michael@0 | 453 | nsTableCellFrame* aCellFrame) |
michael@0 | 454 | { |
michael@0 | 455 | if (!aTableFrame || !aCellFrame) { |
michael@0 | 456 | NS_ASSERTION(false , "invalid call"); |
michael@0 | 457 | return; |
michael@0 | 458 | } |
michael@0 | 459 | |
michael@0 | 460 | if (aHeight != NS_UNCONSTRAINEDSIZE) { |
michael@0 | 461 | if (!(aCellFrame->HasVerticalAlignBaseline())) { // only the cell's height matters |
michael@0 | 462 | if (GetHeight() < aHeight) { |
michael@0 | 463 | int32_t rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame); |
michael@0 | 464 | if (rowSpan == 1) { |
michael@0 | 465 | SetContentHeight(aHeight); |
michael@0 | 466 | } |
michael@0 | 467 | } |
michael@0 | 468 | } |
michael@0 | 469 | else { // the alignment on the baseline can change the height |
michael@0 | 470 | NS_ASSERTION((aAscent != NS_UNCONSTRAINEDSIZE) && (aDescent != NS_UNCONSTRAINEDSIZE), "invalid call"); |
michael@0 | 471 | // see if this is a long ascender |
michael@0 | 472 | if (mMaxCellAscent < aAscent) { |
michael@0 | 473 | mMaxCellAscent = aAscent; |
michael@0 | 474 | } |
michael@0 | 475 | // see if this is a long descender and without rowspan |
michael@0 | 476 | if (mMaxCellDescent < aDescent) { |
michael@0 | 477 | int32_t rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame); |
michael@0 | 478 | if (rowSpan == 1) { |
michael@0 | 479 | mMaxCellDescent = aDescent; |
michael@0 | 480 | } |
michael@0 | 481 | } |
michael@0 | 482 | // keep the tallest height in sync |
michael@0 | 483 | if (GetHeight() < mMaxCellAscent + mMaxCellDescent) { |
michael@0 | 484 | SetContentHeight(mMaxCellAscent + mMaxCellDescent); |
michael@0 | 485 | } |
michael@0 | 486 | } |
michael@0 | 487 | } |
michael@0 | 488 | } |
michael@0 | 489 | |
michael@0 | 490 | nscoord |
michael@0 | 491 | nsTableRowFrame::CalcHeight(const nsHTMLReflowState& aReflowState) |
michael@0 | 492 | { |
michael@0 | 493 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 494 | nscoord computedHeight = (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight()) |
michael@0 | 495 | ? 0 : aReflowState.ComputedHeight(); |
michael@0 | 496 | ResetHeight(computedHeight); |
michael@0 | 497 | |
michael@0 | 498 | const nsStylePosition* position = StylePosition(); |
michael@0 | 499 | if (position->mHeight.ConvertsToLength()) { |
michael@0 | 500 | SetFixedHeight(nsRuleNode::ComputeCoordPercentCalc(position->mHeight, 0)); |
michael@0 | 501 | } |
michael@0 | 502 | else if (eStyleUnit_Percent == position->mHeight.GetUnit()) { |
michael@0 | 503 | SetPctHeight(position->mHeight.GetPercentValue()); |
michael@0 | 504 | } |
michael@0 | 505 | // calc() with percentages is treated like 'auto' on table rows. |
michael@0 | 506 | |
michael@0 | 507 | for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; |
michael@0 | 508 | kidFrame = kidFrame->GetNextSibling()) { |
michael@0 | 509 | nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame); |
michael@0 | 510 | if (cellFrame) { |
michael@0 | 511 | nsSize desSize = cellFrame->GetDesiredSize(); |
michael@0 | 512 | if ((NS_UNCONSTRAINEDSIZE == aReflowState.AvailableHeight()) && !GetPrevInFlow()) { |
michael@0 | 513 | CalculateCellActualHeight(cellFrame, desSize.height); |
michael@0 | 514 | } |
michael@0 | 515 | // height may have changed, adjust descent to absorb any excess difference |
michael@0 | 516 | nscoord ascent; |
michael@0 | 517 | if (!kidFrame->GetFirstPrincipalChild()->GetFirstPrincipalChild()) |
michael@0 | 518 | ascent = desSize.height; |
michael@0 | 519 | else |
michael@0 | 520 | ascent = cellFrame->GetCellBaseline(); |
michael@0 | 521 | nscoord descent = desSize.height - ascent; |
michael@0 | 522 | UpdateHeight(desSize.height, ascent, descent, tableFrame, cellFrame); |
michael@0 | 523 | } |
michael@0 | 524 | } |
michael@0 | 525 | return GetHeight(); |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | /** |
michael@0 | 529 | * We need a custom display item for table row backgrounds. This is only used |
michael@0 | 530 | * when the table row is the root of a stacking context (e.g., has 'opacity'). |
michael@0 | 531 | * Table row backgrounds can extend beyond the row frame bounds, when |
michael@0 | 532 | * the row contains row-spanning cells. |
michael@0 | 533 | */ |
michael@0 | 534 | class nsDisplayTableRowBackground : public nsDisplayTableItem { |
michael@0 | 535 | public: |
michael@0 | 536 | nsDisplayTableRowBackground(nsDisplayListBuilder* aBuilder, |
michael@0 | 537 | nsTableRowFrame* aFrame) : |
michael@0 | 538 | nsDisplayTableItem(aBuilder, aFrame) { |
michael@0 | 539 | MOZ_COUNT_CTOR(nsDisplayTableRowBackground); |
michael@0 | 540 | } |
michael@0 | 541 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 542 | virtual ~nsDisplayTableRowBackground() { |
michael@0 | 543 | MOZ_COUNT_DTOR(nsDisplayTableRowBackground); |
michael@0 | 544 | } |
michael@0 | 545 | #endif |
michael@0 | 546 | |
michael@0 | 547 | virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 548 | const nsDisplayItemGeometry* aGeometry, |
michael@0 | 549 | nsRegion *aInvalidRegion) MOZ_OVERRIDE; |
michael@0 | 550 | virtual void Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 551 | nsRenderingContext* aCtx) MOZ_OVERRIDE; |
michael@0 | 552 | NS_DISPLAY_DECL_NAME("TableRowBackground", TYPE_TABLE_ROW_BACKGROUND) |
michael@0 | 553 | }; |
michael@0 | 554 | |
michael@0 | 555 | void |
michael@0 | 556 | nsDisplayTableRowBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 557 | const nsDisplayItemGeometry* aGeometry, |
michael@0 | 558 | nsRegion *aInvalidRegion) |
michael@0 | 559 | { |
michael@0 | 560 | if (aBuilder->ShouldSyncDecodeImages()) { |
michael@0 | 561 | if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling())) { |
michael@0 | 562 | bool snap; |
michael@0 | 563 | aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); |
michael@0 | 564 | } |
michael@0 | 565 | } |
michael@0 | 566 | |
michael@0 | 567 | nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); |
michael@0 | 568 | } |
michael@0 | 569 | |
michael@0 | 570 | void |
michael@0 | 571 | nsDisplayTableRowBackground::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 572 | nsRenderingContext* aCtx) |
michael@0 | 573 | { |
michael@0 | 574 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(mFrame); |
michael@0 | 575 | TableBackgroundPainter painter(tableFrame, |
michael@0 | 576 | TableBackgroundPainter::eOrigin_TableRow, |
michael@0 | 577 | mFrame->PresContext(), *aCtx, |
michael@0 | 578 | mVisibleRect, ToReferenceFrame(), |
michael@0 | 579 | aBuilder->GetBackgroundPaintFlags()); |
michael@0 | 580 | painter.PaintRow(static_cast<nsTableRowFrame*>(mFrame)); |
michael@0 | 581 | } |
michael@0 | 582 | |
michael@0 | 583 | void |
michael@0 | 584 | nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
michael@0 | 585 | const nsRect& aDirtyRect, |
michael@0 | 586 | const nsDisplayListSet& aLists) |
michael@0 | 587 | { |
michael@0 | 588 | nsDisplayTableItem* item = nullptr; |
michael@0 | 589 | if (IsVisibleInSelection(aBuilder)) { |
michael@0 | 590 | bool isRoot = aBuilder->IsAtRootOfPseudoStackingContext(); |
michael@0 | 591 | if (isRoot) { |
michael@0 | 592 | // This background is created regardless of whether this frame is |
michael@0 | 593 | // visible or not. Visibility decisions are delegated to the |
michael@0 | 594 | // table background painter. |
michael@0 | 595 | // We would use nsDisplayGeneric for this rare case except that we |
michael@0 | 596 | // need the background to be larger than the row frame in some |
michael@0 | 597 | // cases. |
michael@0 | 598 | item = new (aBuilder) nsDisplayTableRowBackground(aBuilder, this); |
michael@0 | 599 | aLists.BorderBackground()->AppendNewToTop(item); |
michael@0 | 600 | } |
michael@0 | 601 | } |
michael@0 | 602 | nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item); |
michael@0 | 603 | } |
michael@0 | 604 | |
michael@0 | 605 | int |
michael@0 | 606 | nsTableRowFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const |
michael@0 | 607 | { |
michael@0 | 608 | int skip = 0; |
michael@0 | 609 | if (nullptr != GetPrevInFlow()) { |
michael@0 | 610 | skip |= LOGICAL_SIDE_B_START; |
michael@0 | 611 | } |
michael@0 | 612 | if (nullptr != GetNextInFlow()) { |
michael@0 | 613 | skip |= LOGICAL_SIDE_B_END; |
michael@0 | 614 | } |
michael@0 | 615 | return skip; |
michael@0 | 616 | } |
michael@0 | 617 | |
michael@0 | 618 | // Calculate the cell's actual height given its pass2 height. |
michael@0 | 619 | // Takes into account the specified height (in the style). |
michael@0 | 620 | // Modifies the desired height that is passed in. |
michael@0 | 621 | nsresult |
michael@0 | 622 | nsTableRowFrame::CalculateCellActualHeight(nsTableCellFrame* aCellFrame, |
michael@0 | 623 | nscoord& aDesiredHeight) |
michael@0 | 624 | { |
michael@0 | 625 | nscoord specifiedHeight = 0; |
michael@0 | 626 | |
michael@0 | 627 | // Get the height specified in the style information |
michael@0 | 628 | const nsStylePosition* position = aCellFrame->StylePosition(); |
michael@0 | 629 | |
michael@0 | 630 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 631 | int32_t rowSpan = tableFrame->GetEffectiveRowSpan(*aCellFrame); |
michael@0 | 632 | |
michael@0 | 633 | switch (position->mHeight.GetUnit()) { |
michael@0 | 634 | case eStyleUnit_Calc: { |
michael@0 | 635 | if (position->mHeight.CalcHasPercent()) { |
michael@0 | 636 | // Treat this like "auto" |
michael@0 | 637 | break; |
michael@0 | 638 | } |
michael@0 | 639 | // Fall through to the coord case |
michael@0 | 640 | } |
michael@0 | 641 | case eStyleUnit_Coord: { |
michael@0 | 642 | nscoord outsideBoxSizing = 0; |
michael@0 | 643 | // In quirks mode, table cell width should be content-box, but height |
michael@0 | 644 | // should be border-box. |
michael@0 | 645 | // Because of this historic anomaly, we do not use quirk.css |
michael@0 | 646 | // (since we can't specify one value of box-sizing for width and another |
michael@0 | 647 | // for height) |
michael@0 | 648 | if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks) { |
michael@0 | 649 | switch (position->mBoxSizing) { |
michael@0 | 650 | case NS_STYLE_BOX_SIZING_CONTENT: |
michael@0 | 651 | outsideBoxSizing = aCellFrame->GetUsedBorderAndPadding().TopBottom(); |
michael@0 | 652 | break; |
michael@0 | 653 | case NS_STYLE_BOX_SIZING_PADDING: |
michael@0 | 654 | outsideBoxSizing = aCellFrame->GetUsedBorder().TopBottom(); |
michael@0 | 655 | break; |
michael@0 | 656 | default: |
michael@0 | 657 | // NS_STYLE_BOX_SIZING_BORDER |
michael@0 | 658 | break; |
michael@0 | 659 | } |
michael@0 | 660 | } |
michael@0 | 661 | |
michael@0 | 662 | specifiedHeight = |
michael@0 | 663 | nsRuleNode::ComputeCoordPercentCalc(position->mHeight, 0) + |
michael@0 | 664 | outsideBoxSizing; |
michael@0 | 665 | |
michael@0 | 666 | if (1 == rowSpan) |
michael@0 | 667 | SetFixedHeight(specifiedHeight); |
michael@0 | 668 | break; |
michael@0 | 669 | } |
michael@0 | 670 | case eStyleUnit_Percent: { |
michael@0 | 671 | if (1 == rowSpan) |
michael@0 | 672 | SetPctHeight(position->mHeight.GetPercentValue()); |
michael@0 | 673 | // pct heights are handled when all of the cells are finished, so don't set specifiedHeight |
michael@0 | 674 | break; |
michael@0 | 675 | } |
michael@0 | 676 | case eStyleUnit_Auto: |
michael@0 | 677 | default: |
michael@0 | 678 | break; |
michael@0 | 679 | } |
michael@0 | 680 | |
michael@0 | 681 | // If the specified height is greater than the desired height, then use the specified height |
michael@0 | 682 | if (specifiedHeight > aDesiredHeight) |
michael@0 | 683 | aDesiredHeight = specifiedHeight; |
michael@0 | 684 | |
michael@0 | 685 | return NS_OK; |
michael@0 | 686 | } |
michael@0 | 687 | |
michael@0 | 688 | // Calculates the available width for the table cell based on the known |
michael@0 | 689 | // column widths taking into account column spans and column spacing |
michael@0 | 690 | static nscoord |
michael@0 | 691 | CalcAvailWidth(nsTableFrame& aTableFrame, |
michael@0 | 692 | nsTableCellFrame& aCellFrame, |
michael@0 | 693 | nscoord aCellSpacingX) |
michael@0 | 694 | { |
michael@0 | 695 | nscoord cellAvailWidth = 0; |
michael@0 | 696 | int32_t colIndex; |
michael@0 | 697 | aCellFrame.GetColIndex(colIndex); |
michael@0 | 698 | int32_t colspan = aTableFrame.GetEffectiveColSpan(aCellFrame); |
michael@0 | 699 | NS_ASSERTION(colspan > 0, "effective colspan should be positive"); |
michael@0 | 700 | |
michael@0 | 701 | for (int32_t spanX = 0; spanX < colspan; spanX++) { |
michael@0 | 702 | cellAvailWidth += aTableFrame.GetColumnWidth(colIndex + spanX); |
michael@0 | 703 | if (spanX > 0 && |
michael@0 | 704 | aTableFrame.ColumnHasCellSpacingBefore(colIndex + spanX)) { |
michael@0 | 705 | cellAvailWidth += aCellSpacingX; |
michael@0 | 706 | } |
michael@0 | 707 | } |
michael@0 | 708 | return cellAvailWidth; |
michael@0 | 709 | } |
michael@0 | 710 | |
michael@0 | 711 | nscoord |
michael@0 | 712 | GetSpaceBetween(int32_t aPrevColIndex, |
michael@0 | 713 | int32_t aColIndex, |
michael@0 | 714 | int32_t aColSpan, |
michael@0 | 715 | nsTableFrame& aTableFrame, |
michael@0 | 716 | nscoord aCellSpacingX, |
michael@0 | 717 | bool aIsLeftToRight, |
michael@0 | 718 | bool aCheckVisibility) |
michael@0 | 719 | { |
michael@0 | 720 | nscoord space = 0; |
michael@0 | 721 | int32_t colX; |
michael@0 | 722 | if (aIsLeftToRight) { |
michael@0 | 723 | for (colX = aPrevColIndex + 1; aColIndex > colX; colX++) { |
michael@0 | 724 | bool isCollapsed = false; |
michael@0 | 725 | if (!aCheckVisibility) { |
michael@0 | 726 | space += aTableFrame.GetColumnWidth(colX); |
michael@0 | 727 | } |
michael@0 | 728 | else { |
michael@0 | 729 | nsTableColFrame* colFrame = aTableFrame.GetColFrame(colX); |
michael@0 | 730 | const nsStyleVisibility* colVis = colFrame->StyleVisibility(); |
michael@0 | 731 | bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible); |
michael@0 | 732 | nsIFrame* cgFrame = colFrame->GetParent(); |
michael@0 | 733 | const nsStyleVisibility* groupVis = cgFrame->StyleVisibility(); |
michael@0 | 734 | bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == |
michael@0 | 735 | groupVis->mVisible); |
michael@0 | 736 | isCollapsed = collapseCol || collapseGroup; |
michael@0 | 737 | if (!isCollapsed) |
michael@0 | 738 | space += aTableFrame.GetColumnWidth(colX); |
michael@0 | 739 | } |
michael@0 | 740 | if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) { |
michael@0 | 741 | space += aCellSpacingX; |
michael@0 | 742 | } |
michael@0 | 743 | } |
michael@0 | 744 | } |
michael@0 | 745 | else { |
michael@0 | 746 | int32_t lastCol = aColIndex + aColSpan - 1; |
michael@0 | 747 | for (colX = aPrevColIndex - 1; colX > lastCol; colX--) { |
michael@0 | 748 | bool isCollapsed = false; |
michael@0 | 749 | if (!aCheckVisibility) { |
michael@0 | 750 | space += aTableFrame.GetColumnWidth(colX); |
michael@0 | 751 | } |
michael@0 | 752 | else { |
michael@0 | 753 | nsTableColFrame* colFrame = aTableFrame.GetColFrame(colX); |
michael@0 | 754 | const nsStyleVisibility* colVis = colFrame->StyleVisibility(); |
michael@0 | 755 | bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible); |
michael@0 | 756 | nsIFrame* cgFrame = colFrame->GetParent(); |
michael@0 | 757 | const nsStyleVisibility* groupVis = cgFrame->StyleVisibility(); |
michael@0 | 758 | bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == |
michael@0 | 759 | groupVis->mVisible); |
michael@0 | 760 | isCollapsed = collapseCol || collapseGroup; |
michael@0 | 761 | if (!isCollapsed) |
michael@0 | 762 | space += aTableFrame.GetColumnWidth(colX); |
michael@0 | 763 | } |
michael@0 | 764 | if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) { |
michael@0 | 765 | space += aCellSpacingX; |
michael@0 | 766 | } |
michael@0 | 767 | } |
michael@0 | 768 | } |
michael@0 | 769 | return space; |
michael@0 | 770 | } |
michael@0 | 771 | |
michael@0 | 772 | // subtract the heights of aRow's prev in flows from the unpaginated height |
michael@0 | 773 | static |
michael@0 | 774 | nscoord CalcHeightFromUnpaginatedHeight(nsPresContext* aPresContext, |
michael@0 | 775 | nsTableRowFrame& aRow) |
michael@0 | 776 | { |
michael@0 | 777 | nscoord height = 0; |
michael@0 | 778 | nsTableRowFrame* firstInFlow = |
michael@0 | 779 | static_cast<nsTableRowFrame*>(aRow.FirstInFlow()); |
michael@0 | 780 | if (firstInFlow->HasUnpaginatedHeight()) { |
michael@0 | 781 | height = firstInFlow->GetUnpaginatedHeight(aPresContext); |
michael@0 | 782 | for (nsIFrame* prevInFlow = aRow.GetPrevInFlow(); prevInFlow; |
michael@0 | 783 | prevInFlow = prevInFlow->GetPrevInFlow()) { |
michael@0 | 784 | height -= prevInFlow->GetSize().height; |
michael@0 | 785 | } |
michael@0 | 786 | } |
michael@0 | 787 | return std::max(height, 0); |
michael@0 | 788 | } |
michael@0 | 789 | |
michael@0 | 790 | nsresult |
michael@0 | 791 | nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext, |
michael@0 | 792 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 793 | const nsHTMLReflowState& aReflowState, |
michael@0 | 794 | nsTableFrame& aTableFrame, |
michael@0 | 795 | nsReflowStatus& aStatus) |
michael@0 | 796 | { |
michael@0 | 797 | aStatus = NS_FRAME_COMPLETE; |
michael@0 | 798 | |
michael@0 | 799 | // XXXldb Should we be checking constrained height instead? |
michael@0 | 800 | const bool isPaginated = aPresContext->IsPaginated(); |
michael@0 | 801 | const bool borderCollapse = aTableFrame.IsBorderCollapse(); |
michael@0 | 802 | nsresult rv = NS_OK; |
michael@0 | 803 | nscoord cellSpacingX = aTableFrame.GetCellSpacingX(); |
michael@0 | 804 | int32_t cellColSpan = 1; // must be defined here so it's set properly for non-cell kids |
michael@0 | 805 | |
michael@0 | 806 | nsTableIterator iter(*this); |
michael@0 | 807 | // remember the col index of the previous cell to handle rowspans into this row |
michael@0 | 808 | int32_t firstPrevColIndex = (iter.IsLeftToRight()) ? -1 : aTableFrame.GetColCount(); |
michael@0 | 809 | int32_t prevColIndex = firstPrevColIndex; |
michael@0 | 810 | nscoord x = 0; // running total of children x offset |
michael@0 | 811 | |
michael@0 | 812 | // This computes the max of all cell heights |
michael@0 | 813 | nscoord cellMaxHeight = 0; |
michael@0 | 814 | |
michael@0 | 815 | // Reflow each of our existing cell frames |
michael@0 | 816 | for (nsIFrame* kidFrame = iter.First(); kidFrame; kidFrame = iter.Next()) { |
michael@0 | 817 | nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame); |
michael@0 | 818 | if (!cellFrame) { |
michael@0 | 819 | // XXXldb nsCSSFrameConstructor needs to enforce this! |
michael@0 | 820 | NS_NOTREACHED("yikes, a non-row child"); |
michael@0 | 821 | |
michael@0 | 822 | // it's an unknown frame type, give it a generic reflow and ignore the results |
michael@0 | 823 | nsTableCellReflowState kidReflowState(aPresContext, aReflowState, |
michael@0 | 824 | kidFrame, nsSize(0,0), |
michael@0 | 825 | nsHTMLReflowState::CALLER_WILL_INIT); |
michael@0 | 826 | InitChildReflowState(*aPresContext, nsSize(0,0), false, kidReflowState); |
michael@0 | 827 | nsHTMLReflowMetrics desiredSize(aReflowState); |
michael@0 | 828 | nsReflowStatus status; |
michael@0 | 829 | ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, 0, 0, 0, status); |
michael@0 | 830 | kidFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED); |
michael@0 | 831 | |
michael@0 | 832 | continue; |
michael@0 | 833 | } |
michael@0 | 834 | |
michael@0 | 835 | // See if we should only reflow the dirty child frames |
michael@0 | 836 | bool doReflowChild = true; |
michael@0 | 837 | if (!aReflowState.ShouldReflowAllKids() && |
michael@0 | 838 | !aTableFrame.IsGeometryDirty() && |
michael@0 | 839 | !NS_SUBTREE_DIRTY(kidFrame)) { |
michael@0 | 840 | if (!aReflowState.mFlags.mSpecialHeightReflow) |
michael@0 | 841 | doReflowChild = false; |
michael@0 | 842 | } |
michael@0 | 843 | else if ((NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight())) { |
michael@0 | 844 | // We don't reflow a rowspan >1 cell here with a constrained height. |
michael@0 | 845 | // That happens in nsTableRowGroupFrame::SplitSpanningCells. |
michael@0 | 846 | if (aTableFrame.GetEffectiveRowSpan(*cellFrame) > 1) { |
michael@0 | 847 | doReflowChild = false; |
michael@0 | 848 | } |
michael@0 | 849 | } |
michael@0 | 850 | if (aReflowState.mFlags.mSpecialHeightReflow) { |
michael@0 | 851 | if (!isPaginated && !(cellFrame->GetStateBits() & |
michael@0 | 852 | NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { |
michael@0 | 853 | continue; |
michael@0 | 854 | } |
michael@0 | 855 | } |
michael@0 | 856 | |
michael@0 | 857 | int32_t cellColIndex; |
michael@0 | 858 | cellFrame->GetColIndex(cellColIndex); |
michael@0 | 859 | cellColSpan = aTableFrame.GetEffectiveColSpan(*cellFrame); |
michael@0 | 860 | |
michael@0 | 861 | // If the adjacent cell is in a prior row (because of a rowspan) add in the space |
michael@0 | 862 | if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) || |
michael@0 | 863 | (!iter.IsLeftToRight() && (prevColIndex != cellColIndex + cellColSpan))) { |
michael@0 | 864 | x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, aTableFrame, |
michael@0 | 865 | cellSpacingX, iter.IsLeftToRight(), false); |
michael@0 | 866 | } |
michael@0 | 867 | |
michael@0 | 868 | // remember the rightmost (ltr) or leftmost (rtl) column this cell spans into |
michael@0 | 869 | prevColIndex = (iter.IsLeftToRight()) ? cellColIndex + (cellColSpan - 1) : cellColIndex; |
michael@0 | 870 | |
michael@0 | 871 | // Reflow the child frame |
michael@0 | 872 | nsRect kidRect = kidFrame->GetRect(); |
michael@0 | 873 | nsRect kidVisualOverflow = kidFrame->GetVisualOverflowRect(); |
michael@0 | 874 | bool firstReflow = |
michael@0 | 875 | (kidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; |
michael@0 | 876 | |
michael@0 | 877 | if (doReflowChild) { |
michael@0 | 878 | // Calculate the available width for the table cell using the known column widths |
michael@0 | 879 | nscoord availCellWidth = |
michael@0 | 880 | CalcAvailWidth(aTableFrame, *cellFrame, cellSpacingX); |
michael@0 | 881 | |
michael@0 | 882 | nsHTMLReflowMetrics desiredSize(aReflowState); |
michael@0 | 883 | |
michael@0 | 884 | // If the avail width is not the same as last time we reflowed the cell or |
michael@0 | 885 | // the cell wants to be bigger than what was available last time or |
michael@0 | 886 | // it is a style change reflow or we are printing, then we must reflow the |
michael@0 | 887 | // cell. Otherwise we can skip the reflow. |
michael@0 | 888 | // XXXldb Why is this condition distinct from doReflowChild above? |
michael@0 | 889 | nsSize cellDesiredSize = cellFrame->GetDesiredSize(); |
michael@0 | 890 | if ((availCellWidth != cellFrame->GetPriorAvailWidth()) || |
michael@0 | 891 | (cellDesiredSize.width > cellFrame->GetPriorAvailWidth()) || |
michael@0 | 892 | (GetStateBits() & NS_FRAME_IS_DIRTY) || |
michael@0 | 893 | isPaginated || |
michael@0 | 894 | NS_SUBTREE_DIRTY(cellFrame) || |
michael@0 | 895 | // See if it needs a special reflow, or if it had one that we need to undo. |
michael@0 | 896 | (cellFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) || |
michael@0 | 897 | HasPctHeight()) { |
michael@0 | 898 | // Reflow the cell to fit the available width, height |
michael@0 | 899 | // XXX The old IR_ChildIsDirty code used availCellWidth here. |
michael@0 | 900 | nsSize kidAvailSize(availCellWidth, aReflowState.AvailableHeight()); |
michael@0 | 901 | |
michael@0 | 902 | // Reflow the child |
michael@0 | 903 | nsTableCellReflowState kidReflowState(aPresContext, aReflowState, |
michael@0 | 904 | kidFrame, kidAvailSize, |
michael@0 | 905 | nsHTMLReflowState::CALLER_WILL_INIT); |
michael@0 | 906 | InitChildReflowState(*aPresContext, kidAvailSize, borderCollapse, |
michael@0 | 907 | kidReflowState); |
michael@0 | 908 | |
michael@0 | 909 | nsReflowStatus status; |
michael@0 | 910 | rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, |
michael@0 | 911 | x, 0, 0, status); |
michael@0 | 912 | |
michael@0 | 913 | // allow the table to determine if/how the table needs to be rebalanced |
michael@0 | 914 | // If any of the cells are not complete, then we're not complete |
michael@0 | 915 | if (NS_FRAME_IS_NOT_COMPLETE(status)) { |
michael@0 | 916 | aStatus = NS_FRAME_NOT_COMPLETE; |
michael@0 | 917 | } |
michael@0 | 918 | } |
michael@0 | 919 | else { |
michael@0 | 920 | if (x != kidRect.x) { |
michael@0 | 921 | kidFrame->InvalidateFrameSubtree(); |
michael@0 | 922 | } |
michael@0 | 923 | |
michael@0 | 924 | desiredSize.Width() = cellDesiredSize.width; |
michael@0 | 925 | desiredSize.Height() = cellDesiredSize.height; |
michael@0 | 926 | desiredSize.mOverflowAreas = cellFrame->GetOverflowAreas(); |
michael@0 | 927 | |
michael@0 | 928 | // if we are in a floated table, our position is not yet established, so we cannot reposition our views |
michael@0 | 929 | // the containing block will do this for us after positioning the table |
michael@0 | 930 | if (!aTableFrame.IsFloating()) { |
michael@0 | 931 | // Because we may have moved the frame we need to make sure any views are |
michael@0 | 932 | // positioned properly. We have to do this, because any one of our parent |
michael@0 | 933 | // frames could have moved and we have no way of knowing... |
michael@0 | 934 | nsTableFrame::RePositionViews(kidFrame); |
michael@0 | 935 | } |
michael@0 | 936 | } |
michael@0 | 937 | |
michael@0 | 938 | if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableHeight()) { |
michael@0 | 939 | if (!GetPrevInFlow()) { |
michael@0 | 940 | // Calculate the cell's actual height given its pass2 height. This |
michael@0 | 941 | // function takes into account the specified height (in the style) |
michael@0 | 942 | CalculateCellActualHeight(cellFrame, desiredSize.Height()); |
michael@0 | 943 | } |
michael@0 | 944 | // height may have changed, adjust descent to absorb any excess difference |
michael@0 | 945 | nscoord ascent; |
michael@0 | 946 | if (!kidFrame->GetFirstPrincipalChild()->GetFirstPrincipalChild()) |
michael@0 | 947 | ascent = desiredSize.Height(); |
michael@0 | 948 | else |
michael@0 | 949 | ascent = ((nsTableCellFrame *)kidFrame)->GetCellBaseline(); |
michael@0 | 950 | nscoord descent = desiredSize.Height() - ascent; |
michael@0 | 951 | UpdateHeight(desiredSize.Height(), ascent, descent, &aTableFrame, cellFrame); |
michael@0 | 952 | } |
michael@0 | 953 | else { |
michael@0 | 954 | cellMaxHeight = std::max(cellMaxHeight, desiredSize.Height()); |
michael@0 | 955 | int32_t rowSpan = aTableFrame.GetEffectiveRowSpan((nsTableCellFrame&)*kidFrame); |
michael@0 | 956 | if (1 == rowSpan) { |
michael@0 | 957 | SetContentHeight(cellMaxHeight); |
michael@0 | 958 | } |
michael@0 | 959 | } |
michael@0 | 960 | |
michael@0 | 961 | // Place the child |
michael@0 | 962 | desiredSize.Width() = availCellWidth; |
michael@0 | 963 | |
michael@0 | 964 | FinishReflowChild(kidFrame, aPresContext, desiredSize, nullptr, x, 0, 0); |
michael@0 | 965 | |
michael@0 | 966 | nsTableFrame::InvalidateTableFrame(kidFrame, kidRect, kidVisualOverflow, |
michael@0 | 967 | firstReflow); |
michael@0 | 968 | |
michael@0 | 969 | x += desiredSize.Width(); |
michael@0 | 970 | } |
michael@0 | 971 | else { |
michael@0 | 972 | if (kidRect.x != x) { |
michael@0 | 973 | // Invalidate the old position |
michael@0 | 974 | kidFrame->InvalidateFrameSubtree(); |
michael@0 | 975 | // move to the new position |
michael@0 | 976 | kidFrame->SetPosition(nsPoint(x, kidRect.y)); |
michael@0 | 977 | nsTableFrame::RePositionViews(kidFrame); |
michael@0 | 978 | // invalidate the new position |
michael@0 | 979 | kidFrame->InvalidateFrameSubtree(); |
michael@0 | 980 | } |
michael@0 | 981 | // we need to account for the cell's width even if it isn't reflowed |
michael@0 | 982 | x += kidRect.width; |
michael@0 | 983 | |
michael@0 | 984 | if (kidFrame->GetNextInFlow()) { |
michael@0 | 985 | aStatus = NS_FRAME_NOT_COMPLETE; |
michael@0 | 986 | } |
michael@0 | 987 | } |
michael@0 | 988 | ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame); |
michael@0 | 989 | x += cellSpacingX; |
michael@0 | 990 | } |
michael@0 | 991 | |
michael@0 | 992 | // just set our width to what was available. The table will calculate the width and not use our value. |
michael@0 | 993 | aDesiredSize.Width() = aReflowState.AvailableWidth(); |
michael@0 | 994 | |
michael@0 | 995 | if (aReflowState.mFlags.mSpecialHeightReflow) { |
michael@0 | 996 | aDesiredSize.Height() = mRect.height; |
michael@0 | 997 | } |
michael@0 | 998 | else if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableHeight()) { |
michael@0 | 999 | aDesiredSize.Height() = CalcHeight(aReflowState); |
michael@0 | 1000 | if (GetPrevInFlow()) { |
michael@0 | 1001 | nscoord height = CalcHeightFromUnpaginatedHeight(aPresContext, *this); |
michael@0 | 1002 | aDesiredSize.Height() = std::max(aDesiredSize.Height(), height); |
michael@0 | 1003 | } |
michael@0 | 1004 | else { |
michael@0 | 1005 | if (isPaginated && HasStyleHeight()) { |
michael@0 | 1006 | // set the unpaginated height so next in flows can try to honor it |
michael@0 | 1007 | SetHasUnpaginatedHeight(true); |
michael@0 | 1008 | SetUnpaginatedHeight(aPresContext, aDesiredSize.Height()); |
michael@0 | 1009 | } |
michael@0 | 1010 | if (isPaginated && HasUnpaginatedHeight()) { |
michael@0 | 1011 | aDesiredSize.Height() = std::max(aDesiredSize.Height(), GetUnpaginatedHeight(aPresContext)); |
michael@0 | 1012 | } |
michael@0 | 1013 | } |
michael@0 | 1014 | } |
michael@0 | 1015 | else { // constrained height, paginated |
michael@0 | 1016 | // Compute the height we should have from style (subtracting the |
michael@0 | 1017 | // height from our prev-in-flows from the style height) |
michael@0 | 1018 | nscoord styleHeight = CalcHeightFromUnpaginatedHeight(aPresContext, *this); |
michael@0 | 1019 | if (styleHeight > aReflowState.AvailableHeight()) { |
michael@0 | 1020 | styleHeight = aReflowState.AvailableHeight(); |
michael@0 | 1021 | NS_FRAME_SET_INCOMPLETE(aStatus); |
michael@0 | 1022 | } |
michael@0 | 1023 | aDesiredSize.Height() = std::max(cellMaxHeight, styleHeight); |
michael@0 | 1024 | } |
michael@0 | 1025 | aDesiredSize.UnionOverflowAreasWithDesiredBounds(); |
michael@0 | 1026 | FinishAndStoreOverflow(&aDesiredSize); |
michael@0 | 1027 | return rv; |
michael@0 | 1028 | } |
michael@0 | 1029 | |
michael@0 | 1030 | /** Layout the entire row. |
michael@0 | 1031 | * This method stacks cells horizontally according to HTML 4.0 rules. |
michael@0 | 1032 | */ |
michael@0 | 1033 | nsresult |
michael@0 | 1034 | nsTableRowFrame::Reflow(nsPresContext* aPresContext, |
michael@0 | 1035 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 1036 | const nsHTMLReflowState& aReflowState, |
michael@0 | 1037 | nsReflowStatus& aStatus) |
michael@0 | 1038 | { |
michael@0 | 1039 | DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame"); |
michael@0 | 1040 | DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); |
michael@0 | 1041 | nsresult rv = NS_OK; |
michael@0 | 1042 | |
michael@0 | 1043 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 1044 | const nsStyleVisibility* rowVis = StyleVisibility(); |
michael@0 | 1045 | bool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible); |
michael@0 | 1046 | if (collapseRow) { |
michael@0 | 1047 | tableFrame->SetNeedToCollapse(true); |
michael@0 | 1048 | } |
michael@0 | 1049 | |
michael@0 | 1050 | // see if a special height reflow needs to occur due to having a pct height |
michael@0 | 1051 | nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState); |
michael@0 | 1052 | |
michael@0 | 1053 | // See if we have a cell with specified/pct height |
michael@0 | 1054 | InitHasCellWithStyleHeight(tableFrame); |
michael@0 | 1055 | |
michael@0 | 1056 | rv = ReflowChildren(aPresContext, aDesiredSize, aReflowState, *tableFrame, |
michael@0 | 1057 | aStatus); |
michael@0 | 1058 | |
michael@0 | 1059 | if (aPresContext->IsPaginated() && !NS_FRAME_IS_FULLY_COMPLETE(aStatus) && |
michael@0 | 1060 | ShouldAvoidBreakInside(aReflowState)) { |
michael@0 | 1061 | aStatus = NS_INLINE_LINE_BREAK_BEFORE(); |
michael@0 | 1062 | } |
michael@0 | 1063 | |
michael@0 | 1064 | // just set our width to what was available. The table will calculate the width and not use our value. |
michael@0 | 1065 | aDesiredSize.Width() = aReflowState.AvailableWidth(); |
michael@0 | 1066 | |
michael@0 | 1067 | // If our parent is in initial reflow, it'll handle invalidating our |
michael@0 | 1068 | // entire overflow rect. |
michael@0 | 1069 | if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) && |
michael@0 | 1070 | nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) { |
michael@0 | 1071 | InvalidateFrame(); |
michael@0 | 1072 | } |
michael@0 | 1073 | |
michael@0 | 1074 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
michael@0 | 1075 | return rv; |
michael@0 | 1076 | } |
michael@0 | 1077 | |
michael@0 | 1078 | /** |
michael@0 | 1079 | * This function is called by the row group frame's SplitRowGroup() code when |
michael@0 | 1080 | * pushing a row frame that has cell frames that span into it. The cell frame |
michael@0 | 1081 | * should be reflowed with the specified height |
michael@0 | 1082 | */ |
michael@0 | 1083 | nscoord |
michael@0 | 1084 | nsTableRowFrame::ReflowCellFrame(nsPresContext* aPresContext, |
michael@0 | 1085 | const nsHTMLReflowState& aReflowState, |
michael@0 | 1086 | bool aIsTopOfPage, |
michael@0 | 1087 | nsTableCellFrame* aCellFrame, |
michael@0 | 1088 | nscoord aAvailableHeight, |
michael@0 | 1089 | nsReflowStatus& aStatus) |
michael@0 | 1090 | { |
michael@0 | 1091 | // Reflow the cell frame with the specified height. Use the existing width |
michael@0 | 1092 | nsRect cellRect = aCellFrame->GetRect(); |
michael@0 | 1093 | nsRect cellVisualOverflow = aCellFrame->GetVisualOverflowRect(); |
michael@0 | 1094 | |
michael@0 | 1095 | nsSize availSize(cellRect.width, aAvailableHeight); |
michael@0 | 1096 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
michael@0 | 1097 | bool borderCollapse = tableFrame->IsBorderCollapse(); |
michael@0 | 1098 | nsTableCellReflowState cellReflowState(aPresContext, aReflowState, |
michael@0 | 1099 | aCellFrame, availSize, |
michael@0 | 1100 | nsHTMLReflowState::CALLER_WILL_INIT); |
michael@0 | 1101 | InitChildReflowState(*aPresContext, availSize, borderCollapse, cellReflowState); |
michael@0 | 1102 | cellReflowState.mFlags.mIsTopOfPage = aIsTopOfPage; |
michael@0 | 1103 | |
michael@0 | 1104 | nsHTMLReflowMetrics desiredSize(aReflowState); |
michael@0 | 1105 | |
michael@0 | 1106 | ReflowChild(aCellFrame, aPresContext, desiredSize, cellReflowState, |
michael@0 | 1107 | 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus); |
michael@0 | 1108 | bool fullyComplete = NS_FRAME_IS_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus); |
michael@0 | 1109 | if (fullyComplete) { |
michael@0 | 1110 | desiredSize.Height() = aAvailableHeight; |
michael@0 | 1111 | } |
michael@0 | 1112 | aCellFrame->SetSize(nsSize(cellRect.width, desiredSize.Height())); |
michael@0 | 1113 | |
michael@0 | 1114 | // Note: VerticallyAlignChild can affect the overflow rect. |
michael@0 | 1115 | // XXX What happens if this cell has 'vertical-align: baseline' ? |
michael@0 | 1116 | // XXX Why is it assumed that the cell's ascent hasn't changed ? |
michael@0 | 1117 | if (fullyComplete) { |
michael@0 | 1118 | aCellFrame->VerticallyAlignChild(mMaxCellAscent); |
michael@0 | 1119 | } |
michael@0 | 1120 | |
michael@0 | 1121 | nsTableFrame::InvalidateTableFrame(aCellFrame, cellRect, |
michael@0 | 1122 | cellVisualOverflow, |
michael@0 | 1123 | (aCellFrame->GetStateBits() & |
michael@0 | 1124 | NS_FRAME_FIRST_REFLOW) != 0); |
michael@0 | 1125 | |
michael@0 | 1126 | aCellFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED); |
michael@0 | 1127 | |
michael@0 | 1128 | return desiredSize.Height(); |
michael@0 | 1129 | } |
michael@0 | 1130 | |
michael@0 | 1131 | nscoord |
michael@0 | 1132 | nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset, |
michael@0 | 1133 | nscoord aWidth, |
michael@0 | 1134 | bool aCollapseGroup, |
michael@0 | 1135 | bool& aDidCollapse) |
michael@0 | 1136 | { |
michael@0 | 1137 | const nsStyleVisibility* rowVis = StyleVisibility(); |
michael@0 | 1138 | bool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible); |
michael@0 | 1139 | nsTableFrame* tableFrame = static_cast<nsTableFrame*>( |
michael@0 | 1140 | nsTableFrame::GetTableFrame(this)->FirstInFlow()); |
michael@0 | 1141 | if (collapseRow) { |
michael@0 | 1142 | tableFrame->SetNeedToCollapse(true); |
michael@0 | 1143 | } |
michael@0 | 1144 | |
michael@0 | 1145 | if (aRowOffset != 0) { |
michael@0 | 1146 | // We're moving, so invalidate our old position |
michael@0 | 1147 | InvalidateFrameSubtree(); |
michael@0 | 1148 | } |
michael@0 | 1149 | |
michael@0 | 1150 | nsRect rowRect = GetRect(); |
michael@0 | 1151 | nsRect oldRect = rowRect; |
michael@0 | 1152 | nsRect oldVisualOverflow = GetVisualOverflowRect(); |
michael@0 | 1153 | |
michael@0 | 1154 | rowRect.y -= aRowOffset; |
michael@0 | 1155 | rowRect.width = aWidth; |
michael@0 | 1156 | nsOverflowAreas overflow; |
michael@0 | 1157 | nscoord shift = 0; |
michael@0 | 1158 | nscoord cellSpacingX = tableFrame->GetCellSpacingX(); |
michael@0 | 1159 | nscoord cellSpacingY = tableFrame->GetCellSpacingY(); |
michael@0 | 1160 | |
michael@0 | 1161 | if (aCollapseGroup || collapseRow) { |
michael@0 | 1162 | nsTableCellFrame* cellFrame = GetFirstCell(); |
michael@0 | 1163 | aDidCollapse = true; |
michael@0 | 1164 | shift = rowRect.height + cellSpacingY; |
michael@0 | 1165 | while (cellFrame) { |
michael@0 | 1166 | nsRect cRect = cellFrame->GetRect(); |
michael@0 | 1167 | // If aRowOffset != 0, there's no point in invalidating the cells, since |
michael@0 | 1168 | // we've already invalidated our overflow area. Note that we _do_ still |
michael@0 | 1169 | // need to invalidate if our row is not moving, because the cell might |
michael@0 | 1170 | // span out of this row, so invalidating our row rect won't do enough. |
michael@0 | 1171 | if (aRowOffset == 0) { |
michael@0 | 1172 | InvalidateFrame(); |
michael@0 | 1173 | } |
michael@0 | 1174 | cRect.height = 0; |
michael@0 | 1175 | cellFrame->SetRect(cRect); |
michael@0 | 1176 | cellFrame = cellFrame->GetNextCell(); |
michael@0 | 1177 | } |
michael@0 | 1178 | rowRect.height = 0; |
michael@0 | 1179 | } |
michael@0 | 1180 | else { // row is not collapsed |
michael@0 | 1181 | nsTableIterator iter(*this); |
michael@0 | 1182 | // remember the col index of the previous cell to handle rowspans into this |
michael@0 | 1183 | // row |
michael@0 | 1184 | int32_t firstPrevColIndex = (iter.IsLeftToRight()) ? -1 : |
michael@0 | 1185 | tableFrame->GetColCount(); |
michael@0 | 1186 | int32_t prevColIndex = firstPrevColIndex; |
michael@0 | 1187 | nscoord x = 0; // running total of children x offset |
michael@0 | 1188 | |
michael@0 | 1189 | int32_t colIncrement = iter.IsLeftToRight() ? 1 : -1; |
michael@0 | 1190 | |
michael@0 | 1191 | //nscoord x = cellSpacingX; |
michael@0 | 1192 | |
michael@0 | 1193 | nsIFrame* kidFrame = iter.First(); |
michael@0 | 1194 | while (kidFrame) { |
michael@0 | 1195 | nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame); |
michael@0 | 1196 | if (cellFrame) { |
michael@0 | 1197 | int32_t cellColIndex; |
michael@0 | 1198 | cellFrame->GetColIndex(cellColIndex); |
michael@0 | 1199 | int32_t cellColSpan = tableFrame->GetEffectiveColSpan(*cellFrame); |
michael@0 | 1200 | |
michael@0 | 1201 | // If the adjacent cell is in a prior row (because of a rowspan) add in |
michael@0 | 1202 | // the space |
michael@0 | 1203 | if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) || |
michael@0 | 1204 | (!iter.IsLeftToRight() && |
michael@0 | 1205 | (prevColIndex != cellColIndex + cellColSpan))) { |
michael@0 | 1206 | x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, |
michael@0 | 1207 | *tableFrame, cellSpacingX, iter.IsLeftToRight(), |
michael@0 | 1208 | true); |
michael@0 | 1209 | } |
michael@0 | 1210 | nsRect cRect(x, 0, 0, rowRect.height); |
michael@0 | 1211 | |
michael@0 | 1212 | // remember the rightmost (ltr) or leftmost (rtl) column this cell |
michael@0 | 1213 | // spans into |
michael@0 | 1214 | prevColIndex = (iter.IsLeftToRight()) ? |
michael@0 | 1215 | cellColIndex + (cellColSpan - 1) : cellColIndex; |
michael@0 | 1216 | int32_t startIndex = (iter.IsLeftToRight()) ? |
michael@0 | 1217 | cellColIndex : cellColIndex + (cellColSpan - 1); |
michael@0 | 1218 | int32_t actualColSpan = cellColSpan; |
michael@0 | 1219 | bool isVisible = false; |
michael@0 | 1220 | for (int32_t colX = startIndex; actualColSpan > 0; |
michael@0 | 1221 | colX += colIncrement, actualColSpan--) { |
michael@0 | 1222 | |
michael@0 | 1223 | nsTableColFrame* colFrame = tableFrame->GetColFrame(colX); |
michael@0 | 1224 | const nsStyleVisibility* colVis = colFrame->StyleVisibility(); |
michael@0 | 1225 | bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == |
michael@0 | 1226 | colVis->mVisible); |
michael@0 | 1227 | nsIFrame* cgFrame = colFrame->GetParent(); |
michael@0 | 1228 | const nsStyleVisibility* groupVis = cgFrame->StyleVisibility(); |
michael@0 | 1229 | bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == |
michael@0 | 1230 | groupVis->mVisible); |
michael@0 | 1231 | bool isCollapsed = collapseCol || collapseGroup; |
michael@0 | 1232 | if (!isCollapsed) { |
michael@0 | 1233 | cRect.width += tableFrame->GetColumnWidth(colX); |
michael@0 | 1234 | isVisible = true; |
michael@0 | 1235 | if ((actualColSpan > 1)) { |
michael@0 | 1236 | nsTableColFrame* nextColFrame = |
michael@0 | 1237 | tableFrame->GetColFrame(colX + colIncrement); |
michael@0 | 1238 | const nsStyleVisibility* nextColVis = |
michael@0 | 1239 | nextColFrame->StyleVisibility(); |
michael@0 | 1240 | if ( (NS_STYLE_VISIBILITY_COLLAPSE != nextColVis->mVisible) && |
michael@0 | 1241 | tableFrame->ColumnHasCellSpacingBefore(colX + colIncrement)) { |
michael@0 | 1242 | cRect.width += cellSpacingX; |
michael@0 | 1243 | } |
michael@0 | 1244 | } |
michael@0 | 1245 | } |
michael@0 | 1246 | } |
michael@0 | 1247 | x += cRect.width; |
michael@0 | 1248 | if (isVisible) |
michael@0 | 1249 | x += cellSpacingX; |
michael@0 | 1250 | int32_t actualRowSpan = tableFrame->GetEffectiveRowSpan(*cellFrame); |
michael@0 | 1251 | nsTableRowFrame* rowFrame = GetNextRow(); |
michael@0 | 1252 | for (actualRowSpan--; actualRowSpan > 0 && rowFrame; actualRowSpan--) { |
michael@0 | 1253 | const nsStyleVisibility* nextRowVis = rowFrame->StyleVisibility(); |
michael@0 | 1254 | bool collapseNextRow = (NS_STYLE_VISIBILITY_COLLAPSE == |
michael@0 | 1255 | nextRowVis->mVisible); |
michael@0 | 1256 | if (!collapseNextRow) { |
michael@0 | 1257 | nsRect nextRect = rowFrame->GetRect(); |
michael@0 | 1258 | cRect.height += nextRect.height + cellSpacingY; |
michael@0 | 1259 | } |
michael@0 | 1260 | rowFrame = rowFrame->GetNextRow(); |
michael@0 | 1261 | } |
michael@0 | 1262 | |
michael@0 | 1263 | nsRect oldCellRect = cellFrame->GetRect(); |
michael@0 | 1264 | nsRect oldCellVisualOverflow = cellFrame->GetVisualOverflowRect(); |
michael@0 | 1265 | |
michael@0 | 1266 | if (aRowOffset == 0 && cRect.TopLeft() != oldCellRect.TopLeft()) { |
michael@0 | 1267 | // We're moving the cell. Invalidate the old overflow area |
michael@0 | 1268 | cellFrame->InvalidateFrameSubtree(); |
michael@0 | 1269 | } |
michael@0 | 1270 | |
michael@0 | 1271 | cellFrame->SetRect(cRect); |
michael@0 | 1272 | |
michael@0 | 1273 | // XXXbz This looks completely bogus in the cases when we didn't |
michael@0 | 1274 | // collapse the cell! |
michael@0 | 1275 | nsRect cellBounds(0, 0, cRect.width, cRect.height); |
michael@0 | 1276 | nsOverflowAreas cellOverflow(cellBounds, cellBounds); |
michael@0 | 1277 | cellFrame->FinishAndStoreOverflow(cellOverflow, cRect.Size()); |
michael@0 | 1278 | nsTableFrame::RePositionViews(cellFrame); |
michael@0 | 1279 | ConsiderChildOverflow(overflow, cellFrame); |
michael@0 | 1280 | |
michael@0 | 1281 | if (aRowOffset == 0) { |
michael@0 | 1282 | nsTableFrame::InvalidateTableFrame(cellFrame, oldCellRect, |
michael@0 | 1283 | oldCellVisualOverflow, |
michael@0 | 1284 | false); |
michael@0 | 1285 | } |
michael@0 | 1286 | } |
michael@0 | 1287 | kidFrame = iter.Next(); // Get the next child |
michael@0 | 1288 | } |
michael@0 | 1289 | } |
michael@0 | 1290 | |
michael@0 | 1291 | SetRect(rowRect); |
michael@0 | 1292 | overflow.UnionAllWith(nsRect(0, 0, rowRect.width, rowRect.height)); |
michael@0 | 1293 | FinishAndStoreOverflow(overflow, rowRect.Size()); |
michael@0 | 1294 | |
michael@0 | 1295 | nsTableFrame::RePositionViews(this); |
michael@0 | 1296 | nsTableFrame::InvalidateTableFrame(this, oldRect, oldVisualOverflow, false); |
michael@0 | 1297 | return shift; |
michael@0 | 1298 | } |
michael@0 | 1299 | |
michael@0 | 1300 | /* |
michael@0 | 1301 | * The following method is called by the row group frame's SplitRowGroup() |
michael@0 | 1302 | * when it creates a continuing cell frame and wants to insert it into the |
michael@0 | 1303 | * row's child list. |
michael@0 | 1304 | */ |
michael@0 | 1305 | void |
michael@0 | 1306 | nsTableRowFrame::InsertCellFrame(nsTableCellFrame* aFrame, |
michael@0 | 1307 | int32_t aColIndex) |
michael@0 | 1308 | { |
michael@0 | 1309 | // Find the cell frame where col index < aColIndex |
michael@0 | 1310 | nsTableCellFrame* priorCell = nullptr; |
michael@0 | 1311 | for (nsIFrame* child = mFrames.FirstChild(); child; |
michael@0 | 1312 | child = child->GetNextSibling()) { |
michael@0 | 1313 | nsTableCellFrame *cellFrame = do_QueryFrame(child); |
michael@0 | 1314 | if (cellFrame) { |
michael@0 | 1315 | int32_t colIndex; |
michael@0 | 1316 | cellFrame->GetColIndex(colIndex); |
michael@0 | 1317 | if (colIndex < aColIndex) { |
michael@0 | 1318 | priorCell = cellFrame; |
michael@0 | 1319 | } |
michael@0 | 1320 | else break; |
michael@0 | 1321 | } |
michael@0 | 1322 | } |
michael@0 | 1323 | mFrames.InsertFrame(this, priorCell, aFrame); |
michael@0 | 1324 | } |
michael@0 | 1325 | |
michael@0 | 1326 | nsIAtom* |
michael@0 | 1327 | nsTableRowFrame::GetType() const |
michael@0 | 1328 | { |
michael@0 | 1329 | return nsGkAtoms::tableRowFrame; |
michael@0 | 1330 | } |
michael@0 | 1331 | |
michael@0 | 1332 | nsTableRowFrame* |
michael@0 | 1333 | nsTableRowFrame::GetNextRow() const |
michael@0 | 1334 | { |
michael@0 | 1335 | nsIFrame* childFrame = GetNextSibling(); |
michael@0 | 1336 | while (childFrame) { |
michael@0 | 1337 | nsTableRowFrame *rowFrame = do_QueryFrame(childFrame); |
michael@0 | 1338 | if (rowFrame) { |
michael@0 | 1339 | NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW == childFrame->StyleDisplay()->mDisplay, "wrong display type on rowframe"); |
michael@0 | 1340 | return rowFrame; |
michael@0 | 1341 | } |
michael@0 | 1342 | childFrame = childFrame->GetNextSibling(); |
michael@0 | 1343 | } |
michael@0 | 1344 | return nullptr; |
michael@0 | 1345 | } |
michael@0 | 1346 | |
michael@0 | 1347 | NS_DECLARE_FRAME_PROPERTY(RowUnpaginatedHeightProperty, nullptr) |
michael@0 | 1348 | |
michael@0 | 1349 | void |
michael@0 | 1350 | nsTableRowFrame::SetUnpaginatedHeight(nsPresContext* aPresContext, |
michael@0 | 1351 | nscoord aValue) |
michael@0 | 1352 | { |
michael@0 | 1353 | NS_ASSERTION(!GetPrevInFlow(), "program error"); |
michael@0 | 1354 | // Get the property |
michael@0 | 1355 | aPresContext->PropertyTable()-> |
michael@0 | 1356 | Set(this, RowUnpaginatedHeightProperty(), NS_INT32_TO_PTR(aValue)); |
michael@0 | 1357 | } |
michael@0 | 1358 | |
michael@0 | 1359 | nscoord |
michael@0 | 1360 | nsTableRowFrame::GetUnpaginatedHeight(nsPresContext* aPresContext) |
michael@0 | 1361 | { |
michael@0 | 1362 | FrameProperties props = FirstInFlow()->Properties(); |
michael@0 | 1363 | return NS_PTR_TO_INT32(props.Get(RowUnpaginatedHeightProperty())); |
michael@0 | 1364 | } |
michael@0 | 1365 | |
michael@0 | 1366 | void nsTableRowFrame::SetContinuousBCBorderWidth(uint8_t aForSide, |
michael@0 | 1367 | BCPixelSize aPixelValue) |
michael@0 | 1368 | { |
michael@0 | 1369 | switch (aForSide) { |
michael@0 | 1370 | case NS_SIDE_RIGHT: |
michael@0 | 1371 | mRightContBorderWidth = aPixelValue; |
michael@0 | 1372 | return; |
michael@0 | 1373 | case NS_SIDE_TOP: |
michael@0 | 1374 | mTopContBorderWidth = aPixelValue; |
michael@0 | 1375 | return; |
michael@0 | 1376 | case NS_SIDE_LEFT: |
michael@0 | 1377 | mLeftContBorderWidth = aPixelValue; |
michael@0 | 1378 | return; |
michael@0 | 1379 | default: |
michael@0 | 1380 | NS_ERROR("invalid NS_SIDE arg"); |
michael@0 | 1381 | } |
michael@0 | 1382 | } |
michael@0 | 1383 | #ifdef ACCESSIBILITY |
michael@0 | 1384 | a11y::AccType |
michael@0 | 1385 | nsTableRowFrame::AccessibleType() |
michael@0 | 1386 | { |
michael@0 | 1387 | return a11y::eHTMLTableRowType; |
michael@0 | 1388 | } |
michael@0 | 1389 | #endif |
michael@0 | 1390 | /** |
michael@0 | 1391 | * Sets the NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT bit to indicate whether |
michael@0 | 1392 | * this row has any cells that have non-auto-height. (Row-spanning |
michael@0 | 1393 | * cells are ignored.) |
michael@0 | 1394 | */ |
michael@0 | 1395 | void nsTableRowFrame::InitHasCellWithStyleHeight(nsTableFrame* aTableFrame) |
michael@0 | 1396 | { |
michael@0 | 1397 | nsTableIterator iter(*this); |
michael@0 | 1398 | |
michael@0 | 1399 | for (nsIFrame* kidFrame = iter.First(); kidFrame; kidFrame = iter.Next()) { |
michael@0 | 1400 | nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame); |
michael@0 | 1401 | if (!cellFrame) { |
michael@0 | 1402 | NS_NOTREACHED("Table row has a non-cell child."); |
michael@0 | 1403 | continue; |
michael@0 | 1404 | } |
michael@0 | 1405 | // Ignore row-spanning cells |
michael@0 | 1406 | const nsStyleCoord &cellHeight = cellFrame->StylePosition()->mHeight; |
michael@0 | 1407 | if (aTableFrame->GetEffectiveRowSpan(*cellFrame) == 1 && |
michael@0 | 1408 | cellHeight.GetUnit() != eStyleUnit_Auto && |
michael@0 | 1409 | /* calc() with percentages treated like 'auto' */ |
michael@0 | 1410 | (!cellHeight.IsCalcUnit() || !cellHeight.HasPercent())) { |
michael@0 | 1411 | AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT); |
michael@0 | 1412 | return; |
michael@0 | 1413 | } |
michael@0 | 1414 | } |
michael@0 | 1415 | RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT); |
michael@0 | 1416 | } |
michael@0 | 1417 | |
michael@0 | 1418 | void |
michael@0 | 1419 | nsTableRowFrame::InvalidateFrame(uint32_t aDisplayItemKey) |
michael@0 | 1420 | { |
michael@0 | 1421 | nsIFrame::InvalidateFrame(aDisplayItemKey); |
michael@0 | 1422 | GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey); |
michael@0 | 1423 | } |
michael@0 | 1424 | |
michael@0 | 1425 | void |
michael@0 | 1426 | nsTableRowFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey) |
michael@0 | 1427 | { |
michael@0 | 1428 | nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey); |
michael@0 | 1429 | // If we have filters applied that would affects our bounds, then |
michael@0 | 1430 | // we get an inactive layer created and this is computed |
michael@0 | 1431 | // within FrameLayerBuilder |
michael@0 | 1432 | GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey); |
michael@0 | 1433 | } |
michael@0 | 1434 | |
michael@0 | 1435 | /* ----- global methods ----- */ |
michael@0 | 1436 | |
michael@0 | 1437 | nsIFrame* |
michael@0 | 1438 | NS_NewTableRowFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
michael@0 | 1439 | { |
michael@0 | 1440 | return new (aPresShell) nsTableRowFrame(aContext); |
michael@0 | 1441 | } |
michael@0 | 1442 | |
michael@0 | 1443 | NS_IMPL_FRAMEARENA_HELPERS(nsTableRowFrame) |
michael@0 | 1444 | |
michael@0 | 1445 | #ifdef DEBUG_FRAME_DUMP |
michael@0 | 1446 | nsresult |
michael@0 | 1447 | nsTableRowFrame::GetFrameName(nsAString& aResult) const |
michael@0 | 1448 | { |
michael@0 | 1449 | return MakeFrameName(NS_LITERAL_STRING("TableRow"), aResult); |
michael@0 | 1450 | } |
michael@0 | 1451 | #endif |