Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set ts=2 sw=2 et tw=80: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "mozilla/Likely.h" |
michael@0 | 8 | #include "mozilla/MathAlgorithms.h" |
michael@0 | 9 | |
michael@0 | 10 | #include "nsCOMPtr.h" |
michael@0 | 11 | #include "nsTableFrame.h" |
michael@0 | 12 | #include "nsRenderingContext.h" |
michael@0 | 13 | #include "nsStyleContext.h" |
michael@0 | 14 | #include "nsStyleConsts.h" |
michael@0 | 15 | #include "nsIContent.h" |
michael@0 | 16 | #include "nsCellMap.h" |
michael@0 | 17 | #include "nsTableCellFrame.h" |
michael@0 | 18 | #include "nsHTMLParts.h" |
michael@0 | 19 | #include "nsTableColFrame.h" |
michael@0 | 20 | #include "nsTableColGroupFrame.h" |
michael@0 | 21 | #include "nsTableRowFrame.h" |
michael@0 | 22 | #include "nsTableRowGroupFrame.h" |
michael@0 | 23 | #include "nsTableOuterFrame.h" |
michael@0 | 24 | #include "nsTablePainter.h" |
michael@0 | 25 | |
michael@0 | 26 | #include "BasicTableLayoutStrategy.h" |
michael@0 | 27 | #include "FixedTableLayoutStrategy.h" |
michael@0 | 28 | |
michael@0 | 29 | #include "nsPresContext.h" |
michael@0 | 30 | #include "nsContentUtils.h" |
michael@0 | 31 | #include "nsCSSRendering.h" |
michael@0 | 32 | #include "nsGkAtoms.h" |
michael@0 | 33 | #include "nsCSSAnonBoxes.h" |
michael@0 | 34 | #include "nsIPresShell.h" |
michael@0 | 35 | #include "nsIDOMElement.h" |
michael@0 | 36 | #include "nsIDOMHTMLElement.h" |
michael@0 | 37 | #include "nsIScriptError.h" |
michael@0 | 38 | #include "nsFrameManager.h" |
michael@0 | 39 | #include "nsError.h" |
michael@0 | 40 | #include "nsAutoPtr.h" |
michael@0 | 41 | #include "nsCSSFrameConstructor.h" |
michael@0 | 42 | #include "nsStyleSet.h" |
michael@0 | 43 | #include "nsDisplayList.h" |
michael@0 | 44 | #include "nsIScrollableFrame.h" |
michael@0 | 45 | #include "nsCSSProps.h" |
michael@0 | 46 | #include "RestyleTracker.h" |
michael@0 | 47 | #include <algorithm> |
michael@0 | 48 | |
michael@0 | 49 | using namespace mozilla; |
michael@0 | 50 | using namespace mozilla::layout; |
michael@0 | 51 | |
michael@0 | 52 | /******************************************************************************** |
michael@0 | 53 | ** nsTableReflowState ** |
michael@0 | 54 | ********************************************************************************/ |
michael@0 | 55 | |
michael@0 | 56 | struct nsTableReflowState { |
michael@0 | 57 | |
michael@0 | 58 | // the real reflow state |
michael@0 | 59 | const nsHTMLReflowState& reflowState; |
michael@0 | 60 | |
michael@0 | 61 | // The table's available size |
michael@0 | 62 | nsSize availSize; |
michael@0 | 63 | |
michael@0 | 64 | // Stationary x-offset |
michael@0 | 65 | nscoord x; |
michael@0 | 66 | |
michael@0 | 67 | // Running y-offset |
michael@0 | 68 | nscoord y; |
michael@0 | 69 | |
michael@0 | 70 | nsTableReflowState(nsPresContext& aPresContext, |
michael@0 | 71 | const nsHTMLReflowState& aReflowState, |
michael@0 | 72 | nsTableFrame& aTableFrame, |
michael@0 | 73 | nscoord aAvailWidth, |
michael@0 | 74 | nscoord aAvailHeight) |
michael@0 | 75 | : reflowState(aReflowState) |
michael@0 | 76 | { |
michael@0 | 77 | Init(aPresContext, aTableFrame, aAvailWidth, aAvailHeight); |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | void Init(nsPresContext& aPresContext, |
michael@0 | 81 | nsTableFrame& aTableFrame, |
michael@0 | 82 | nscoord aAvailWidth, |
michael@0 | 83 | nscoord aAvailHeight) |
michael@0 | 84 | { |
michael@0 | 85 | nsTableFrame* table = static_cast<nsTableFrame*>(aTableFrame.FirstInFlow()); |
michael@0 | 86 | nsMargin borderPadding = table->GetChildAreaOffset(&reflowState); |
michael@0 | 87 | nscoord cellSpacingX = table->GetCellSpacingX(); |
michael@0 | 88 | |
michael@0 | 89 | x = borderPadding.left + cellSpacingX; |
michael@0 | 90 | y = borderPadding.top; //cellspacing added during reflow |
michael@0 | 91 | |
michael@0 | 92 | availSize.width = aAvailWidth; |
michael@0 | 93 | if (NS_UNCONSTRAINEDSIZE != availSize.width) { |
michael@0 | 94 | availSize.width -= borderPadding.left + borderPadding.right |
michael@0 | 95 | + (2 * cellSpacingX); |
michael@0 | 96 | availSize.width = std::max(0, availSize.width); |
michael@0 | 97 | } |
michael@0 | 98 | |
michael@0 | 99 | availSize.height = aAvailHeight; |
michael@0 | 100 | if (NS_UNCONSTRAINEDSIZE != availSize.height) { |
michael@0 | 101 | availSize.height -= borderPadding.top + borderPadding.bottom |
michael@0 | 102 | + (2 * table->GetCellSpacingY()); |
michael@0 | 103 | availSize.height = std::max(0, availSize.height); |
michael@0 | 104 | } |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | nsTableReflowState(nsPresContext& aPresContext, |
michael@0 | 108 | const nsHTMLReflowState& aReflowState, |
michael@0 | 109 | nsTableFrame& aTableFrame) |
michael@0 | 110 | : reflowState(aReflowState) |
michael@0 | 111 | { |
michael@0 | 112 | Init(aPresContext, aTableFrame, aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); |
michael@0 | 113 | } |
michael@0 | 114 | |
michael@0 | 115 | }; |
michael@0 | 116 | |
michael@0 | 117 | /******************************************************************************** |
michael@0 | 118 | ** nsTableFrame ** |
michael@0 | 119 | ********************************************************************************/ |
michael@0 | 120 | |
michael@0 | 121 | struct BCPropertyData |
michael@0 | 122 | { |
michael@0 | 123 | BCPropertyData() : mTopBorderWidth(0), mRightBorderWidth(0), |
michael@0 | 124 | mBottomBorderWidth(0), mLeftBorderWidth(0), |
michael@0 | 125 | mLeftCellBorderWidth(0), mRightCellBorderWidth(0) {} |
michael@0 | 126 | nsIntRect mDamageArea; |
michael@0 | 127 | BCPixelSize mTopBorderWidth; |
michael@0 | 128 | BCPixelSize mRightBorderWidth; |
michael@0 | 129 | BCPixelSize mBottomBorderWidth; |
michael@0 | 130 | BCPixelSize mLeftBorderWidth; |
michael@0 | 131 | BCPixelSize mLeftCellBorderWidth; |
michael@0 | 132 | BCPixelSize mRightCellBorderWidth; |
michael@0 | 133 | }; |
michael@0 | 134 | |
michael@0 | 135 | nsIFrame* |
michael@0 | 136 | nsTableFrame::GetParentStyleContextFrame() const |
michael@0 | 137 | { |
michael@0 | 138 | // Since our parent, the table outer frame, returned this frame, we |
michael@0 | 139 | // must return whatever our parent would normally have returned. |
michael@0 | 140 | |
michael@0 | 141 | NS_PRECONDITION(mParent, "table constructed without outer table"); |
michael@0 | 142 | if (!mContent->GetParent() && !StyleContext()->GetPseudo()) { |
michael@0 | 143 | // We're the root. We have no style context parent. |
michael@0 | 144 | return nullptr; |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | return static_cast<nsFrame*>(GetParent())->DoGetParentStyleContextFrame(); |
michael@0 | 148 | } |
michael@0 | 149 | |
michael@0 | 150 | |
michael@0 | 151 | nsIAtom* |
michael@0 | 152 | nsTableFrame::GetType() const |
michael@0 | 153 | { |
michael@0 | 154 | return nsGkAtoms::tableFrame; |
michael@0 | 155 | } |
michael@0 | 156 | |
michael@0 | 157 | |
michael@0 | 158 | nsTableFrame::nsTableFrame(nsStyleContext* aContext) |
michael@0 | 159 | : nsContainerFrame(aContext), |
michael@0 | 160 | mCellMap(nullptr), |
michael@0 | 161 | mTableLayoutStrategy(nullptr) |
michael@0 | 162 | { |
michael@0 | 163 | memset(&mBits, 0, sizeof(mBits)); |
michael@0 | 164 | } |
michael@0 | 165 | |
michael@0 | 166 | void |
michael@0 | 167 | nsTableFrame::Init(nsIContent* aContent, |
michael@0 | 168 | nsIFrame* aParent, |
michael@0 | 169 | nsIFrame* aPrevInFlow) |
michael@0 | 170 | { |
michael@0 | 171 | NS_PRECONDITION(!mCellMap, "Init called twice"); |
michael@0 | 172 | NS_PRECONDITION(!aPrevInFlow || |
michael@0 | 173 | aPrevInFlow->GetType() == nsGkAtoms::tableFrame, |
michael@0 | 174 | "prev-in-flow must be of same type"); |
michael@0 | 175 | |
michael@0 | 176 | // Let the base class do its processing |
michael@0 | 177 | nsContainerFrame::Init(aContent, aParent, aPrevInFlow); |
michael@0 | 178 | |
michael@0 | 179 | // see if border collapse is on, if so set it |
michael@0 | 180 | const nsStyleTableBorder* tableStyle = StyleTableBorder(); |
michael@0 | 181 | bool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse); |
michael@0 | 182 | SetBorderCollapse(borderCollapse); |
michael@0 | 183 | |
michael@0 | 184 | // Create the cell map if this frame is the first-in-flow. |
michael@0 | 185 | if (!aPrevInFlow) { |
michael@0 | 186 | mCellMap = new nsTableCellMap(*this, borderCollapse); |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | if (aPrevInFlow) { |
michael@0 | 190 | // set my width, because all frames in a table flow are the same width and |
michael@0 | 191 | // code in nsTableOuterFrame depends on this being set |
michael@0 | 192 | mRect.width = aPrevInFlow->GetSize().width; |
michael@0 | 193 | } |
michael@0 | 194 | else { |
michael@0 | 195 | NS_ASSERTION(!mTableLayoutStrategy, "strategy was created before Init was called"); |
michael@0 | 196 | // create the strategy |
michael@0 | 197 | if (IsAutoLayout()) |
michael@0 | 198 | mTableLayoutStrategy = new BasicTableLayoutStrategy(this); |
michael@0 | 199 | else |
michael@0 | 200 | mTableLayoutStrategy = new FixedTableLayoutStrategy(this); |
michael@0 | 201 | } |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | nsTableFrame::~nsTableFrame() |
michael@0 | 205 | { |
michael@0 | 206 | delete mCellMap; |
michael@0 | 207 | delete mTableLayoutStrategy; |
michael@0 | 208 | } |
michael@0 | 209 | |
michael@0 | 210 | void |
michael@0 | 211 | nsTableFrame::DestroyFrom(nsIFrame* aDestructRoot) |
michael@0 | 212 | { |
michael@0 | 213 | mColGroups.DestroyFramesFrom(aDestructRoot); |
michael@0 | 214 | nsContainerFrame::DestroyFrom(aDestructRoot); |
michael@0 | 215 | } |
michael@0 | 216 | |
michael@0 | 217 | // Make sure any views are positioned properly |
michael@0 | 218 | void |
michael@0 | 219 | nsTableFrame::RePositionViews(nsIFrame* aFrame) |
michael@0 | 220 | { |
michael@0 | 221 | nsContainerFrame::PositionFrameView(aFrame); |
michael@0 | 222 | nsContainerFrame::PositionChildViews(aFrame); |
michael@0 | 223 | } |
michael@0 | 224 | |
michael@0 | 225 | static bool |
michael@0 | 226 | IsRepeatedFrame(nsIFrame* kidFrame) |
michael@0 | 227 | { |
michael@0 | 228 | return (kidFrame->GetType() == nsGkAtoms::tableRowFrame || |
michael@0 | 229 | kidFrame->GetType() == nsGkAtoms::tableRowGroupFrame) && |
michael@0 | 230 | (kidFrame->GetStateBits() & NS_REPEATED_ROW_OR_ROWGROUP); |
michael@0 | 231 | } |
michael@0 | 232 | |
michael@0 | 233 | bool |
michael@0 | 234 | nsTableFrame::PageBreakAfter(nsIFrame* aSourceFrame, |
michael@0 | 235 | nsIFrame* aNextFrame) |
michael@0 | 236 | { |
michael@0 | 237 | const nsStyleDisplay* display = aSourceFrame->StyleDisplay(); |
michael@0 | 238 | nsTableRowGroupFrame* prevRg = do_QueryFrame(aSourceFrame); |
michael@0 | 239 | // don't allow a page break after a repeated element ... |
michael@0 | 240 | if ((display->mBreakAfter || (prevRg && prevRg->HasInternalBreakAfter())) && |
michael@0 | 241 | !IsRepeatedFrame(aSourceFrame)) { |
michael@0 | 242 | return !(aNextFrame && IsRepeatedFrame(aNextFrame)); // or before |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | if (aNextFrame) { |
michael@0 | 246 | display = aNextFrame->StyleDisplay(); |
michael@0 | 247 | // don't allow a page break before a repeated element ... |
michael@0 | 248 | nsTableRowGroupFrame* nextRg = do_QueryFrame(aNextFrame); |
michael@0 | 249 | if ((display->mBreakBefore || |
michael@0 | 250 | (nextRg && nextRg->HasInternalBreakBefore())) && |
michael@0 | 251 | !IsRepeatedFrame(aNextFrame)) { |
michael@0 | 252 | return !IsRepeatedFrame(aSourceFrame); // or after |
michael@0 | 253 | } |
michael@0 | 254 | } |
michael@0 | 255 | return false; |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | typedef nsTArray<nsIFrame*> FrameTArray; |
michael@0 | 259 | |
michael@0 | 260 | /* static */ void |
michael@0 | 261 | nsTableFrame::DestroyPositionedTablePartArray(void* aPropertyValue) |
michael@0 | 262 | { |
michael@0 | 263 | auto positionedObjs = static_cast<FrameTArray*>(aPropertyValue); |
michael@0 | 264 | delete positionedObjs; |
michael@0 | 265 | } |
michael@0 | 266 | |
michael@0 | 267 | /* static */ void |
michael@0 | 268 | nsTableFrame::RegisterPositionedTablePart(nsIFrame* aFrame) |
michael@0 | 269 | { |
michael@0 | 270 | // Supporting relative positioning for table parts other than table cells has |
michael@0 | 271 | // the potential to break sites that apply 'position: relative' to those |
michael@0 | 272 | // parts, expecting nothing to happen. We warn at the console to make tracking |
michael@0 | 273 | // down the issue easy. |
michael@0 | 274 | if (nsGkAtoms::tableCellFrame != aFrame->GetType()) { |
michael@0 | 275 | nsIContent* content = aFrame->GetContent(); |
michael@0 | 276 | nsPresContext* presContext = aFrame->PresContext(); |
michael@0 | 277 | if (content && !presContext->HasWarnedAboutPositionedTableParts()) { |
michael@0 | 278 | presContext->SetHasWarnedAboutPositionedTableParts(); |
michael@0 | 279 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
michael@0 | 280 | NS_LITERAL_CSTRING("Layout: Tables"), |
michael@0 | 281 | content->OwnerDoc(), |
michael@0 | 282 | nsContentUtils::eLAYOUT_PROPERTIES, |
michael@0 | 283 | "TablePartRelPosWarning"); |
michael@0 | 284 | } |
michael@0 | 285 | } |
michael@0 | 286 | |
michael@0 | 287 | nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(aFrame); |
michael@0 | 288 | MOZ_ASSERT(tableFrame, "Should have a table frame here"); |
michael@0 | 289 | tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation()); |
michael@0 | 290 | |
michael@0 | 291 | // Retrieve the positioned parts array for this table. |
michael@0 | 292 | FrameProperties props = tableFrame->Properties(); |
michael@0 | 293 | auto positionedParts = |
michael@0 | 294 | static_cast<FrameTArray*>(props.Get(PositionedTablePartArray())); |
michael@0 | 295 | |
michael@0 | 296 | // Lazily create the array if it doesn't exist yet. |
michael@0 | 297 | if (!positionedParts) { |
michael@0 | 298 | positionedParts = new FrameTArray; |
michael@0 | 299 | props.Set(PositionedTablePartArray(), positionedParts); |
michael@0 | 300 | } |
michael@0 | 301 | |
michael@0 | 302 | // Add this frame to the list. |
michael@0 | 303 | positionedParts->AppendElement(aFrame); |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | /* static */ void |
michael@0 | 307 | nsTableFrame::UnregisterPositionedTablePart(nsIFrame* aFrame, |
michael@0 | 308 | nsIFrame* aDestructRoot) |
michael@0 | 309 | { |
michael@0 | 310 | // Retrieve the table frame, and ensure that we hit aDestructRoot on the way. |
michael@0 | 311 | // If we don't, that means that the table frame will be destroyed, so we don't |
michael@0 | 312 | // need to bother with unregistering this frame. |
michael@0 | 313 | nsTableFrame* tableFrame = GetTableFramePassingThrough(aDestructRoot, aFrame); |
michael@0 | 314 | if (!tableFrame) { |
michael@0 | 315 | return; |
michael@0 | 316 | } |
michael@0 | 317 | tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation()); |
michael@0 | 318 | |
michael@0 | 319 | // Retrieve the positioned parts array for this table. |
michael@0 | 320 | FrameProperties props = tableFrame->Properties(); |
michael@0 | 321 | auto positionedParts = |
michael@0 | 322 | static_cast<FrameTArray*>(props.Get(PositionedTablePartArray())); |
michael@0 | 323 | |
michael@0 | 324 | // Remove the frame. |
michael@0 | 325 | MOZ_ASSERT(positionedParts && |
michael@0 | 326 | positionedParts->IndexOf(aFrame) != FrameTArray::NoIndex, |
michael@0 | 327 | "Asked to unregister a positioned table part that wasn't registered"); |
michael@0 | 328 | if (positionedParts) { |
michael@0 | 329 | positionedParts->RemoveElement(aFrame); |
michael@0 | 330 | } |
michael@0 | 331 | } |
michael@0 | 332 | |
michael@0 | 333 | // XXX this needs to be cleaned up so that the frame constructor breaks out col group |
michael@0 | 334 | // frames into a separate child list, bug 343048. |
michael@0 | 335 | nsresult |
michael@0 | 336 | nsTableFrame::SetInitialChildList(ChildListID aListID, |
michael@0 | 337 | nsFrameList& aChildList) |
michael@0 | 338 | { |
michael@0 | 339 | |
michael@0 | 340 | if (!mFrames.IsEmpty() || !mColGroups.IsEmpty()) { |
michael@0 | 341 | // We already have child frames which means we've already been |
michael@0 | 342 | // initialized |
michael@0 | 343 | NS_NOTREACHED("unexpected second call to SetInitialChildList"); |
michael@0 | 344 | return NS_ERROR_UNEXPECTED; |
michael@0 | 345 | } |
michael@0 | 346 | if (aListID != kPrincipalList) { |
michael@0 | 347 | // All we know about is the principal child list. |
michael@0 | 348 | NS_NOTREACHED("unknown frame list"); |
michael@0 | 349 | return NS_ERROR_INVALID_ARG; |
michael@0 | 350 | } |
michael@0 | 351 | |
michael@0 | 352 | // XXXbz the below code is an icky cesspit that's only needed in its current |
michael@0 | 353 | // form for two reasons: |
michael@0 | 354 | // 1) Both rowgroups and column groups come in on the principal child list. |
michael@0 | 355 | while (aChildList.NotEmpty()) { |
michael@0 | 356 | nsIFrame* childFrame = aChildList.FirstChild(); |
michael@0 | 357 | aChildList.RemoveFirstChild(); |
michael@0 | 358 | const nsStyleDisplay* childDisplay = childFrame->StyleDisplay(); |
michael@0 | 359 | |
michael@0 | 360 | if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == childDisplay->mDisplay) { |
michael@0 | 361 | NS_ASSERTION(nsGkAtoms::tableColGroupFrame == childFrame->GetType(), |
michael@0 | 362 | "This is not a colgroup"); |
michael@0 | 363 | mColGroups.AppendFrame(nullptr, childFrame); |
michael@0 | 364 | } |
michael@0 | 365 | else { // row groups and unknown frames go on the main list for now |
michael@0 | 366 | mFrames.AppendFrame(nullptr, childFrame); |
michael@0 | 367 | } |
michael@0 | 368 | } |
michael@0 | 369 | |
michael@0 | 370 | // If we have a prev-in-flow, then we're a table that has been split and |
michael@0 | 371 | // so don't treat this like an append |
michael@0 | 372 | if (!GetPrevInFlow()) { |
michael@0 | 373 | // process col groups first so that real cols get constructed before |
michael@0 | 374 | // anonymous ones due to cells in rows. |
michael@0 | 375 | InsertColGroups(0, mColGroups); |
michael@0 | 376 | InsertRowGroups(mFrames); |
michael@0 | 377 | // calc collapsing borders |
michael@0 | 378 | if (IsBorderCollapse()) { |
michael@0 | 379 | SetFullBCDamageArea(); |
michael@0 | 380 | } |
michael@0 | 381 | } |
michael@0 | 382 | |
michael@0 | 383 | return NS_OK; |
michael@0 | 384 | } |
michael@0 | 385 | |
michael@0 | 386 | void nsTableFrame::AttributeChangedFor(nsIFrame* aFrame, |
michael@0 | 387 | nsIContent* aContent, |
michael@0 | 388 | nsIAtom* aAttribute) |
michael@0 | 389 | { |
michael@0 | 390 | nsTableCellFrame *cellFrame = do_QueryFrame(aFrame); |
michael@0 | 391 | if (cellFrame) { |
michael@0 | 392 | if ((nsGkAtoms::rowspan == aAttribute) || |
michael@0 | 393 | (nsGkAtoms::colspan == aAttribute)) { |
michael@0 | 394 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 395 | if (cellMap) { |
michael@0 | 396 | // for now just remove the cell from the map and reinsert it |
michael@0 | 397 | int32_t rowIndex, colIndex; |
michael@0 | 398 | cellFrame->GetRowIndex(rowIndex); |
michael@0 | 399 | cellFrame->GetColIndex(colIndex); |
michael@0 | 400 | RemoveCell(cellFrame, rowIndex); |
michael@0 | 401 | nsAutoTArray<nsTableCellFrame*, 1> cells; |
michael@0 | 402 | cells.AppendElement(cellFrame); |
michael@0 | 403 | InsertCells(cells, rowIndex, colIndex - 1); |
michael@0 | 404 | |
michael@0 | 405 | // XXX Should this use eStyleChange? It currently doesn't need |
michael@0 | 406 | // to, but it might given more optimization. |
michael@0 | 407 | PresContext()->PresShell()-> |
michael@0 | 408 | FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY); |
michael@0 | 409 | } |
michael@0 | 410 | } |
michael@0 | 411 | } |
michael@0 | 412 | } |
michael@0 | 413 | |
michael@0 | 414 | |
michael@0 | 415 | /* ****** CellMap methods ******* */ |
michael@0 | 416 | |
michael@0 | 417 | /* return the effective col count */ |
michael@0 | 418 | int32_t nsTableFrame::GetEffectiveColCount() const |
michael@0 | 419 | { |
michael@0 | 420 | int32_t colCount = GetColCount(); |
michael@0 | 421 | if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto) { |
michael@0 | 422 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 423 | if (!cellMap) { |
michael@0 | 424 | return 0; |
michael@0 | 425 | } |
michael@0 | 426 | // don't count cols at the end that don't have originating cells |
michael@0 | 427 | for (int32_t colX = colCount - 1; colX >= 0; colX--) { |
michael@0 | 428 | if (cellMap->GetNumCellsOriginatingInCol(colX) > 0) { |
michael@0 | 429 | break; |
michael@0 | 430 | } |
michael@0 | 431 | colCount--; |
michael@0 | 432 | } |
michael@0 | 433 | } |
michael@0 | 434 | return colCount; |
michael@0 | 435 | } |
michael@0 | 436 | |
michael@0 | 437 | int32_t nsTableFrame::GetIndexOfLastRealCol() |
michael@0 | 438 | { |
michael@0 | 439 | int32_t numCols = mColFrames.Length(); |
michael@0 | 440 | if (numCols > 0) { |
michael@0 | 441 | for (int32_t colX = numCols - 1; colX >= 0; colX--) { |
michael@0 | 442 | nsTableColFrame* colFrame = GetColFrame(colX); |
michael@0 | 443 | if (colFrame) { |
michael@0 | 444 | if (eColAnonymousCell != colFrame->GetColType()) { |
michael@0 | 445 | return colX; |
michael@0 | 446 | } |
michael@0 | 447 | } |
michael@0 | 448 | } |
michael@0 | 449 | } |
michael@0 | 450 | return -1; |
michael@0 | 451 | } |
michael@0 | 452 | |
michael@0 | 453 | nsTableColFrame* |
michael@0 | 454 | nsTableFrame::GetColFrame(int32_t aColIndex) const |
michael@0 | 455 | { |
michael@0 | 456 | NS_ASSERTION(!GetPrevInFlow(), "GetColFrame called on next in flow"); |
michael@0 | 457 | int32_t numCols = mColFrames.Length(); |
michael@0 | 458 | if ((aColIndex >= 0) && (aColIndex < numCols)) { |
michael@0 | 459 | return mColFrames.ElementAt(aColIndex); |
michael@0 | 460 | } |
michael@0 | 461 | else { |
michael@0 | 462 | NS_ERROR("invalid col index"); |
michael@0 | 463 | return nullptr; |
michael@0 | 464 | } |
michael@0 | 465 | } |
michael@0 | 466 | |
michael@0 | 467 | int32_t nsTableFrame::GetEffectiveRowSpan(int32_t aRowIndex, |
michael@0 | 468 | const nsTableCellFrame& aCell) const |
michael@0 | 469 | { |
michael@0 | 470 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 471 | NS_PRECONDITION (nullptr != cellMap, "bad call, cellMap not yet allocated."); |
michael@0 | 472 | |
michael@0 | 473 | int32_t colIndex; |
michael@0 | 474 | aCell.GetColIndex(colIndex); |
michael@0 | 475 | return cellMap->GetEffectiveRowSpan(aRowIndex, colIndex); |
michael@0 | 476 | } |
michael@0 | 477 | |
michael@0 | 478 | int32_t nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell, |
michael@0 | 479 | nsCellMap* aCellMap) |
michael@0 | 480 | { |
michael@0 | 481 | nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1); |
michael@0 | 482 | |
michael@0 | 483 | int32_t colIndex, rowIndex; |
michael@0 | 484 | aCell.GetColIndex(colIndex); |
michael@0 | 485 | aCell.GetRowIndex(rowIndex); |
michael@0 | 486 | |
michael@0 | 487 | if (aCellMap) |
michael@0 | 488 | return aCellMap->GetRowSpan(rowIndex, colIndex, true); |
michael@0 | 489 | else |
michael@0 | 490 | return tableCellMap->GetEffectiveRowSpan(rowIndex, colIndex); |
michael@0 | 491 | } |
michael@0 | 492 | |
michael@0 | 493 | int32_t nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell, |
michael@0 | 494 | nsCellMap* aCellMap) const |
michael@0 | 495 | { |
michael@0 | 496 | nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1); |
michael@0 | 497 | |
michael@0 | 498 | int32_t colIndex, rowIndex; |
michael@0 | 499 | aCell.GetColIndex(colIndex); |
michael@0 | 500 | aCell.GetRowIndex(rowIndex); |
michael@0 | 501 | bool ignore; |
michael@0 | 502 | |
michael@0 | 503 | if (aCellMap) |
michael@0 | 504 | return aCellMap->GetEffectiveColSpan(*tableCellMap, rowIndex, colIndex, ignore); |
michael@0 | 505 | else |
michael@0 | 506 | return tableCellMap->GetEffectiveColSpan(rowIndex, colIndex); |
michael@0 | 507 | } |
michael@0 | 508 | |
michael@0 | 509 | bool nsTableFrame::HasMoreThanOneCell(int32_t aRowIndex) const |
michael@0 | 510 | { |
michael@0 | 511 | nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1); |
michael@0 | 512 | return tableCellMap->HasMoreThanOneCell(aRowIndex); |
michael@0 | 513 | } |
michael@0 | 514 | |
michael@0 | 515 | void nsTableFrame::AdjustRowIndices(int32_t aRowIndex, |
michael@0 | 516 | int32_t aAdjustment) |
michael@0 | 517 | { |
michael@0 | 518 | // Iterate over the row groups and adjust the row indices of all rows |
michael@0 | 519 | // whose index is >= aRowIndex. |
michael@0 | 520 | RowGroupArray rowGroups; |
michael@0 | 521 | OrderRowGroups(rowGroups); |
michael@0 | 522 | |
michael@0 | 523 | for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { |
michael@0 | 524 | rowGroups[rgX]->AdjustRowIndices(aRowIndex, aAdjustment); |
michael@0 | 525 | } |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | |
michael@0 | 529 | void nsTableFrame::ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude) |
michael@0 | 530 | { |
michael@0 | 531 | // Iterate over the row groups and adjust the row indices of all rows |
michael@0 | 532 | // omit the rowgroups that will be inserted later |
michael@0 | 533 | RowGroupArray rowGroups; |
michael@0 | 534 | OrderRowGroups(rowGroups); |
michael@0 | 535 | |
michael@0 | 536 | int32_t rowIndex = 0; |
michael@0 | 537 | nsTHashtable<nsPtrHashKey<nsTableRowGroupFrame> > excludeRowGroups; |
michael@0 | 538 | nsFrameList::Enumerator excludeRowGroupsEnumerator(aRowGroupsToExclude); |
michael@0 | 539 | while (!excludeRowGroupsEnumerator.AtEnd()) { |
michael@0 | 540 | excludeRowGroups.PutEntry(static_cast<nsTableRowGroupFrame*>(excludeRowGroupsEnumerator.get())); |
michael@0 | 541 | excludeRowGroupsEnumerator.Next(); |
michael@0 | 542 | } |
michael@0 | 543 | |
michael@0 | 544 | for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { |
michael@0 | 545 | nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
michael@0 | 546 | if (!excludeRowGroups.GetEntry(rgFrame)) { |
michael@0 | 547 | const nsFrameList& rowFrames = rgFrame->PrincipalChildList(); |
michael@0 | 548 | for (nsFrameList::Enumerator rows(rowFrames); !rows.AtEnd(); rows.Next()) { |
michael@0 | 549 | if (NS_STYLE_DISPLAY_TABLE_ROW==rows.get()->StyleDisplay()->mDisplay) { |
michael@0 | 550 | ((nsTableRowFrame *)rows.get())->SetRowIndex(rowIndex); |
michael@0 | 551 | rowIndex++; |
michael@0 | 552 | } |
michael@0 | 553 | } |
michael@0 | 554 | } |
michael@0 | 555 | } |
michael@0 | 556 | } |
michael@0 | 557 | void nsTableFrame::InsertColGroups(int32_t aStartColIndex, |
michael@0 | 558 | const nsFrameList::Slice& aColGroups) |
michael@0 | 559 | { |
michael@0 | 560 | int32_t colIndex = aStartColIndex; |
michael@0 | 561 | nsFrameList::Enumerator colGroups(aColGroups); |
michael@0 | 562 | for (; !colGroups.AtEnd(); colGroups.Next()) { |
michael@0 | 563 | MOZ_ASSERT(colGroups.get()->GetType() == nsGkAtoms::tableColGroupFrame); |
michael@0 | 564 | nsTableColGroupFrame* cgFrame = |
michael@0 | 565 | static_cast<nsTableColGroupFrame*>(colGroups.get()); |
michael@0 | 566 | cgFrame->SetStartColumnIndex(colIndex); |
michael@0 | 567 | // XXXbz this sucks. AddColsToTable will actually remove colgroups from |
michael@0 | 568 | // the list we're traversing! Need to fix things here. :( I guess this is |
michael@0 | 569 | // why the old code used pointer-to-last-frame as opposed to |
michael@0 | 570 | // pointer-to-frame-after-last.... |
michael@0 | 571 | |
michael@0 | 572 | // How about dealing with this by storing a const reference to the |
michael@0 | 573 | // mNextSibling of the framelist's last frame, instead of storing a pointer |
michael@0 | 574 | // to the first-after-next frame? Will involve making nsFrameList friend |
michael@0 | 575 | // of nsIFrame, but it's time for that anyway. |
michael@0 | 576 | cgFrame->AddColsToTable(colIndex, false, |
michael@0 | 577 | colGroups.get()->PrincipalChildList()); |
michael@0 | 578 | int32_t numCols = cgFrame->GetColCount(); |
michael@0 | 579 | colIndex += numCols; |
michael@0 | 580 | } |
michael@0 | 581 | |
michael@0 | 582 | nsFrameList::Enumerator remainingColgroups = colGroups.GetUnlimitedEnumerator(); |
michael@0 | 583 | if (!remainingColgroups.AtEnd()) { |
michael@0 | 584 | nsTableColGroupFrame::ResetColIndices( |
michael@0 | 585 | static_cast<nsTableColGroupFrame*>(remainingColgroups.get()), colIndex); |
michael@0 | 586 | } |
michael@0 | 587 | } |
michael@0 | 588 | |
michael@0 | 589 | void nsTableFrame::InsertCol(nsTableColFrame& aColFrame, |
michael@0 | 590 | int32_t aColIndex) |
michael@0 | 591 | { |
michael@0 | 592 | mColFrames.InsertElementAt(aColIndex, &aColFrame); |
michael@0 | 593 | nsTableColType insertedColType = aColFrame.GetColType(); |
michael@0 | 594 | int32_t numCacheCols = mColFrames.Length(); |
michael@0 | 595 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 596 | if (cellMap) { |
michael@0 | 597 | int32_t numMapCols = cellMap->GetColCount(); |
michael@0 | 598 | if (numCacheCols > numMapCols) { |
michael@0 | 599 | bool removedFromCache = false; |
michael@0 | 600 | if (eColAnonymousCell != insertedColType) { |
michael@0 | 601 | nsTableColFrame* lastCol = mColFrames.ElementAt(numCacheCols - 1); |
michael@0 | 602 | if (lastCol) { |
michael@0 | 603 | nsTableColType lastColType = lastCol->GetColType(); |
michael@0 | 604 | if (eColAnonymousCell == lastColType) { |
michael@0 | 605 | // remove the col from the cache |
michael@0 | 606 | mColFrames.RemoveElementAt(numCacheCols - 1); |
michael@0 | 607 | // remove the col from the eColGroupAnonymousCell col group |
michael@0 | 608 | nsTableColGroupFrame* lastColGroup = (nsTableColGroupFrame *)mColGroups.LastChild(); |
michael@0 | 609 | if (lastColGroup) { |
michael@0 | 610 | lastColGroup->RemoveChild(*lastCol, false); |
michael@0 | 611 | |
michael@0 | 612 | // remove the col group if it is empty |
michael@0 | 613 | if (lastColGroup->GetColCount() <= 0) { |
michael@0 | 614 | mColGroups.DestroyFrame((nsIFrame*)lastColGroup); |
michael@0 | 615 | } |
michael@0 | 616 | } |
michael@0 | 617 | removedFromCache = true; |
michael@0 | 618 | } |
michael@0 | 619 | } |
michael@0 | 620 | } |
michael@0 | 621 | if (!removedFromCache) { |
michael@0 | 622 | cellMap->AddColsAtEnd(1); |
michael@0 | 623 | } |
michael@0 | 624 | } |
michael@0 | 625 | } |
michael@0 | 626 | // for now, just bail and recalc all of the collapsing borders |
michael@0 | 627 | if (IsBorderCollapse()) { |
michael@0 | 628 | nsIntRect damageArea(aColIndex, 0, 1, GetRowCount()); |
michael@0 | 629 | AddBCDamageArea(damageArea); |
michael@0 | 630 | } |
michael@0 | 631 | } |
michael@0 | 632 | |
michael@0 | 633 | void nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame, |
michael@0 | 634 | int32_t aColIndex, |
michael@0 | 635 | bool aRemoveFromCache, |
michael@0 | 636 | bool aRemoveFromCellMap) |
michael@0 | 637 | { |
michael@0 | 638 | if (aRemoveFromCache) { |
michael@0 | 639 | mColFrames.RemoveElementAt(aColIndex); |
michael@0 | 640 | } |
michael@0 | 641 | if (aRemoveFromCellMap) { |
michael@0 | 642 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 643 | if (cellMap) { |
michael@0 | 644 | AppendAnonymousColFrames(1); |
michael@0 | 645 | } |
michael@0 | 646 | } |
michael@0 | 647 | // for now, just bail and recalc all of the collapsing borders |
michael@0 | 648 | if (IsBorderCollapse()) { |
michael@0 | 649 | nsIntRect damageArea(0, 0, GetColCount(), GetRowCount()); |
michael@0 | 650 | AddBCDamageArea(damageArea); |
michael@0 | 651 | } |
michael@0 | 652 | } |
michael@0 | 653 | |
michael@0 | 654 | /** Get the cell map for this table frame. It is not always mCellMap. |
michael@0 | 655 | * Only the first-in-flow has a legit cell map. |
michael@0 | 656 | */ |
michael@0 | 657 | nsTableCellMap* |
michael@0 | 658 | nsTableFrame::GetCellMap() const |
michael@0 | 659 | { |
michael@0 | 660 | return static_cast<nsTableFrame*>(FirstInFlow())->mCellMap; |
michael@0 | 661 | } |
michael@0 | 662 | |
michael@0 | 663 | // XXX this needs to be moved to nsCSSFrameConstructor |
michael@0 | 664 | nsTableColGroupFrame* |
michael@0 | 665 | nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType) |
michael@0 | 666 | { |
michael@0 | 667 | nsIContent* colGroupContent = GetContent(); |
michael@0 | 668 | nsPresContext* presContext = PresContext(); |
michael@0 | 669 | nsIPresShell *shell = presContext->PresShell(); |
michael@0 | 670 | |
michael@0 | 671 | nsRefPtr<nsStyleContext> colGroupStyle; |
michael@0 | 672 | colGroupStyle = shell->StyleSet()-> |
michael@0 | 673 | ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup, mStyleContext); |
michael@0 | 674 | // Create a col group frame |
michael@0 | 675 | nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle); |
michael@0 | 676 | ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType); |
michael@0 | 677 | newFrame->Init(colGroupContent, this, nullptr); |
michael@0 | 678 | return (nsTableColGroupFrame *)newFrame; |
michael@0 | 679 | } |
michael@0 | 680 | |
michael@0 | 681 | void |
michael@0 | 682 | nsTableFrame::AppendAnonymousColFrames(int32_t aNumColsToAdd) |
michael@0 | 683 | { |
michael@0 | 684 | // get the last col group frame |
michael@0 | 685 | nsTableColGroupFrame* colGroupFrame = |
michael@0 | 686 | static_cast<nsTableColGroupFrame*>(mColGroups.LastChild()); |
michael@0 | 687 | |
michael@0 | 688 | if (!colGroupFrame || |
michael@0 | 689 | (colGroupFrame->GetColType() != eColGroupAnonymousCell)) { |
michael@0 | 690 | int32_t colIndex = (colGroupFrame) ? |
michael@0 | 691 | colGroupFrame->GetStartColumnIndex() + |
michael@0 | 692 | colGroupFrame->GetColCount() : 0; |
michael@0 | 693 | colGroupFrame = CreateAnonymousColGroupFrame(eColGroupAnonymousCell); |
michael@0 | 694 | if (!colGroupFrame) { |
michael@0 | 695 | return; |
michael@0 | 696 | } |
michael@0 | 697 | // add the new frame to the child list |
michael@0 | 698 | mColGroups.AppendFrame(this, colGroupFrame); |
michael@0 | 699 | colGroupFrame->SetStartColumnIndex(colIndex); |
michael@0 | 700 | } |
michael@0 | 701 | AppendAnonymousColFrames(colGroupFrame, aNumColsToAdd, eColAnonymousCell, |
michael@0 | 702 | true); |
michael@0 | 703 | |
michael@0 | 704 | } |
michael@0 | 705 | |
michael@0 | 706 | // XXX this needs to be moved to nsCSSFrameConstructor |
michael@0 | 707 | // Right now it only creates the col frames at the end |
michael@0 | 708 | void |
michael@0 | 709 | nsTableFrame::AppendAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame, |
michael@0 | 710 | int32_t aNumColsToAdd, |
michael@0 | 711 | nsTableColType aColType, |
michael@0 | 712 | bool aAddToTable) |
michael@0 | 713 | { |
michael@0 | 714 | NS_PRECONDITION(aColGroupFrame, "null frame"); |
michael@0 | 715 | NS_PRECONDITION(aColType != eColAnonymousCol, "Shouldn't happen"); |
michael@0 | 716 | |
michael@0 | 717 | nsIPresShell *shell = PresContext()->PresShell(); |
michael@0 | 718 | |
michael@0 | 719 | // Get the last col frame |
michael@0 | 720 | nsFrameList newColFrames; |
michael@0 | 721 | |
michael@0 | 722 | int32_t startIndex = mColFrames.Length(); |
michael@0 | 723 | int32_t lastIndex = startIndex + aNumColsToAdd - 1; |
michael@0 | 724 | |
michael@0 | 725 | for (int32_t childX = startIndex; childX <= lastIndex; childX++) { |
michael@0 | 726 | nsIContent* iContent; |
michael@0 | 727 | nsRefPtr<nsStyleContext> styleContext; |
michael@0 | 728 | nsStyleContext* parentStyleContext; |
michael@0 | 729 | |
michael@0 | 730 | // all anonymous cols that we create here use a pseudo style context of the |
michael@0 | 731 | // col group |
michael@0 | 732 | iContent = aColGroupFrame->GetContent(); |
michael@0 | 733 | parentStyleContext = aColGroupFrame->StyleContext(); |
michael@0 | 734 | styleContext = shell->StyleSet()-> |
michael@0 | 735 | ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableCol, parentStyleContext); |
michael@0 | 736 | // ASSERTION to check for bug 54454 sneaking back in... |
michael@0 | 737 | NS_ASSERTION(iContent, "null content in CreateAnonymousColFrames"); |
michael@0 | 738 | |
michael@0 | 739 | // create the new col frame |
michael@0 | 740 | nsIFrame* colFrame = NS_NewTableColFrame(shell, styleContext); |
michael@0 | 741 | ((nsTableColFrame *) colFrame)->SetColType(aColType); |
michael@0 | 742 | colFrame->Init(iContent, aColGroupFrame, nullptr); |
michael@0 | 743 | |
michael@0 | 744 | newColFrames.AppendFrame(nullptr, colFrame); |
michael@0 | 745 | } |
michael@0 | 746 | nsFrameList& cols = aColGroupFrame->GetWritableChildList(); |
michael@0 | 747 | nsIFrame* oldLastCol = cols.LastChild(); |
michael@0 | 748 | const nsFrameList::Slice& newCols = |
michael@0 | 749 | cols.InsertFrames(nullptr, oldLastCol, newColFrames); |
michael@0 | 750 | if (aAddToTable) { |
michael@0 | 751 | // get the starting col index in the cache |
michael@0 | 752 | int32_t startColIndex; |
michael@0 | 753 | if (oldLastCol) { |
michael@0 | 754 | startColIndex = |
michael@0 | 755 | static_cast<nsTableColFrame*>(oldLastCol)->GetColIndex() + 1; |
michael@0 | 756 | } else { |
michael@0 | 757 | startColIndex = aColGroupFrame->GetStartColumnIndex(); |
michael@0 | 758 | } |
michael@0 | 759 | |
michael@0 | 760 | aColGroupFrame->AddColsToTable(startColIndex, true, newCols); |
michael@0 | 761 | } |
michael@0 | 762 | } |
michael@0 | 763 | |
michael@0 | 764 | void |
michael@0 | 765 | nsTableFrame::MatchCellMapToColCache(nsTableCellMap* aCellMap) |
michael@0 | 766 | { |
michael@0 | 767 | int32_t numColsInMap = GetColCount(); |
michael@0 | 768 | int32_t numColsInCache = mColFrames.Length(); |
michael@0 | 769 | int32_t numColsToAdd = numColsInMap - numColsInCache; |
michael@0 | 770 | if (numColsToAdd > 0) { |
michael@0 | 771 | // this sets the child list, updates the col cache and cell map |
michael@0 | 772 | AppendAnonymousColFrames(numColsToAdd); |
michael@0 | 773 | } |
michael@0 | 774 | if (numColsToAdd < 0) { |
michael@0 | 775 | int32_t numColsNotRemoved = DestroyAnonymousColFrames(-numColsToAdd); |
michael@0 | 776 | // if the cell map has fewer cols than the cache, correct it |
michael@0 | 777 | if (numColsNotRemoved > 0) { |
michael@0 | 778 | aCellMap->AddColsAtEnd(numColsNotRemoved); |
michael@0 | 779 | } |
michael@0 | 780 | } |
michael@0 | 781 | if (numColsToAdd && HasZeroColSpans()) { |
michael@0 | 782 | SetNeedColSpanExpansion(true); |
michael@0 | 783 | } |
michael@0 | 784 | if (NeedColSpanExpansion()) { |
michael@0 | 785 | // This flag can be set in two ways -- either by changing |
michael@0 | 786 | // the number of columns (that happens in the block above), |
michael@0 | 787 | // or by adding a cell with colspan="0" to the cellmap. To |
michael@0 | 788 | // handle the latter case we need to explicitly check the |
michael@0 | 789 | // flag here -- it may be set even if the number of columns |
michael@0 | 790 | // did not change. |
michael@0 | 791 | // |
michael@0 | 792 | // @see nsCellMap::AppendCell |
michael@0 | 793 | |
michael@0 | 794 | aCellMap->ExpandZeroColSpans(); |
michael@0 | 795 | } |
michael@0 | 796 | } |
michael@0 | 797 | |
michael@0 | 798 | void |
michael@0 | 799 | nsTableFrame::DidResizeColumns() |
michael@0 | 800 | { |
michael@0 | 801 | NS_PRECONDITION(!GetPrevInFlow(), |
michael@0 | 802 | "should only be called on first-in-flow"); |
michael@0 | 803 | if (mBits.mResizedColumns) |
michael@0 | 804 | return; // already marked |
michael@0 | 805 | |
michael@0 | 806 | for (nsTableFrame *f = this; f; |
michael@0 | 807 | f = static_cast<nsTableFrame*>(f->GetNextInFlow())) |
michael@0 | 808 | f->mBits.mResizedColumns = true; |
michael@0 | 809 | } |
michael@0 | 810 | |
michael@0 | 811 | void |
michael@0 | 812 | nsTableFrame::AppendCell(nsTableCellFrame& aCellFrame, |
michael@0 | 813 | int32_t aRowIndex) |
michael@0 | 814 | { |
michael@0 | 815 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 816 | if (cellMap) { |
michael@0 | 817 | nsIntRect damageArea(0,0,0,0); |
michael@0 | 818 | cellMap->AppendCell(aCellFrame, aRowIndex, true, damageArea); |
michael@0 | 819 | MatchCellMapToColCache(cellMap); |
michael@0 | 820 | if (IsBorderCollapse()) { |
michael@0 | 821 | AddBCDamageArea(damageArea); |
michael@0 | 822 | } |
michael@0 | 823 | } |
michael@0 | 824 | } |
michael@0 | 825 | |
michael@0 | 826 | void nsTableFrame::InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames, |
michael@0 | 827 | int32_t aRowIndex, |
michael@0 | 828 | int32_t aColIndexBefore) |
michael@0 | 829 | { |
michael@0 | 830 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 831 | if (cellMap) { |
michael@0 | 832 | nsIntRect damageArea(0,0,0,0); |
michael@0 | 833 | cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea); |
michael@0 | 834 | MatchCellMapToColCache(cellMap); |
michael@0 | 835 | if (IsBorderCollapse()) { |
michael@0 | 836 | AddBCDamageArea(damageArea); |
michael@0 | 837 | } |
michael@0 | 838 | } |
michael@0 | 839 | } |
michael@0 | 840 | |
michael@0 | 841 | // this removes the frames from the col group and table, but not the cell map |
michael@0 | 842 | int32_t |
michael@0 | 843 | nsTableFrame::DestroyAnonymousColFrames(int32_t aNumFrames) |
michael@0 | 844 | { |
michael@0 | 845 | // only remove cols that are of type eTypeAnonymous cell (they are at the end) |
michael@0 | 846 | int32_t endIndex = mColFrames.Length() - 1; |
michael@0 | 847 | int32_t startIndex = (endIndex - aNumFrames) + 1; |
michael@0 | 848 | int32_t numColsRemoved = 0; |
michael@0 | 849 | for (int32_t colX = endIndex; colX >= startIndex; colX--) { |
michael@0 | 850 | nsTableColFrame* colFrame = GetColFrame(colX); |
michael@0 | 851 | if (colFrame && (eColAnonymousCell == colFrame->GetColType())) { |
michael@0 | 852 | nsTableColGroupFrame* cgFrame = |
michael@0 | 853 | static_cast<nsTableColGroupFrame*>(colFrame->GetParent()); |
michael@0 | 854 | // remove the frame from the colgroup |
michael@0 | 855 | cgFrame->RemoveChild(*colFrame, false); |
michael@0 | 856 | // remove the frame from the cache, but not the cell map |
michael@0 | 857 | RemoveCol(nullptr, colX, true, false); |
michael@0 | 858 | numColsRemoved++; |
michael@0 | 859 | } |
michael@0 | 860 | else { |
michael@0 | 861 | break; |
michael@0 | 862 | } |
michael@0 | 863 | } |
michael@0 | 864 | return (aNumFrames - numColsRemoved); |
michael@0 | 865 | } |
michael@0 | 866 | |
michael@0 | 867 | void nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame, |
michael@0 | 868 | int32_t aRowIndex) |
michael@0 | 869 | { |
michael@0 | 870 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 871 | if (cellMap) { |
michael@0 | 872 | nsIntRect damageArea(0,0,0,0); |
michael@0 | 873 | cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea); |
michael@0 | 874 | MatchCellMapToColCache(cellMap); |
michael@0 | 875 | if (IsBorderCollapse()) { |
michael@0 | 876 | AddBCDamageArea(damageArea); |
michael@0 | 877 | } |
michael@0 | 878 | } |
michael@0 | 879 | } |
michael@0 | 880 | |
michael@0 | 881 | int32_t |
michael@0 | 882 | nsTableFrame::GetStartRowIndex(nsTableRowGroupFrame* aRowGroupFrame) |
michael@0 | 883 | { |
michael@0 | 884 | RowGroupArray orderedRowGroups; |
michael@0 | 885 | OrderRowGroups(orderedRowGroups); |
michael@0 | 886 | |
michael@0 | 887 | int32_t rowIndex = 0; |
michael@0 | 888 | for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { |
michael@0 | 889 | nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex]; |
michael@0 | 890 | if (rgFrame == aRowGroupFrame) { |
michael@0 | 891 | break; |
michael@0 | 892 | } |
michael@0 | 893 | int32_t numRows = rgFrame->GetRowCount(); |
michael@0 | 894 | rowIndex += numRows; |
michael@0 | 895 | } |
michael@0 | 896 | return rowIndex; |
michael@0 | 897 | } |
michael@0 | 898 | |
michael@0 | 899 | // this cannot extend beyond a single row group |
michael@0 | 900 | void nsTableFrame::AppendRows(nsTableRowGroupFrame* aRowGroupFrame, |
michael@0 | 901 | int32_t aRowIndex, |
michael@0 | 902 | nsTArray<nsTableRowFrame*>& aRowFrames) |
michael@0 | 903 | { |
michael@0 | 904 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 905 | if (cellMap) { |
michael@0 | 906 | int32_t absRowIndex = GetStartRowIndex(aRowGroupFrame) + aRowIndex; |
michael@0 | 907 | InsertRows(aRowGroupFrame, aRowFrames, absRowIndex, true); |
michael@0 | 908 | } |
michael@0 | 909 | } |
michael@0 | 910 | |
michael@0 | 911 | // this cannot extend beyond a single row group |
michael@0 | 912 | int32_t |
michael@0 | 913 | nsTableFrame::InsertRows(nsTableRowGroupFrame* aRowGroupFrame, |
michael@0 | 914 | nsTArray<nsTableRowFrame*>& aRowFrames, |
michael@0 | 915 | int32_t aRowIndex, |
michael@0 | 916 | bool aConsiderSpans) |
michael@0 | 917 | { |
michael@0 | 918 | #ifdef DEBUG_TABLE_CELLMAP |
michael@0 | 919 | printf("=== insertRowsBefore firstRow=%d \n", aRowIndex); |
michael@0 | 920 | Dump(true, false, true); |
michael@0 | 921 | #endif |
michael@0 | 922 | |
michael@0 | 923 | int32_t numColsToAdd = 0; |
michael@0 | 924 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 925 | if (cellMap) { |
michael@0 | 926 | nsIntRect damageArea(0,0,0,0); |
michael@0 | 927 | int32_t origNumRows = cellMap->GetRowCount(); |
michael@0 | 928 | int32_t numNewRows = aRowFrames.Length(); |
michael@0 | 929 | cellMap->InsertRows(aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea); |
michael@0 | 930 | MatchCellMapToColCache(cellMap); |
michael@0 | 931 | if (aRowIndex < origNumRows) { |
michael@0 | 932 | AdjustRowIndices(aRowIndex, numNewRows); |
michael@0 | 933 | } |
michael@0 | 934 | // assign the correct row indices to the new rows. If they were adjusted above |
michael@0 | 935 | // it may not have been done correctly because each row is constructed with index 0 |
michael@0 | 936 | for (int32_t rowY = 0; rowY < numNewRows; rowY++) { |
michael@0 | 937 | nsTableRowFrame* rowFrame = aRowFrames.ElementAt(rowY); |
michael@0 | 938 | rowFrame->SetRowIndex(aRowIndex + rowY); |
michael@0 | 939 | } |
michael@0 | 940 | if (IsBorderCollapse()) { |
michael@0 | 941 | AddBCDamageArea(damageArea); |
michael@0 | 942 | } |
michael@0 | 943 | } |
michael@0 | 944 | #ifdef DEBUG_TABLE_CELLMAP |
michael@0 | 945 | printf("=== insertRowsAfter \n"); |
michael@0 | 946 | Dump(true, false, true); |
michael@0 | 947 | #endif |
michael@0 | 948 | |
michael@0 | 949 | return numColsToAdd; |
michael@0 | 950 | } |
michael@0 | 951 | |
michael@0 | 952 | // this cannot extend beyond a single row group |
michael@0 | 953 | void nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame, |
michael@0 | 954 | int32_t aNumRowsToRemove, |
michael@0 | 955 | bool aConsiderSpans) |
michael@0 | 956 | { |
michael@0 | 957 | #ifdef TBD_OPTIMIZATION |
michael@0 | 958 | // decide if we need to rebalance. we have to do this here because the row group |
michael@0 | 959 | // cannot do it when it gets the dirty reflow corresponding to the frame being destroyed |
michael@0 | 960 | bool stopTelling = false; |
michael@0 | 961 | for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking); |
michael@0 | 962 | kidFrame = kidFrame->GetNextSibling()) { |
michael@0 | 963 | nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame); |
michael@0 | 964 | if (cellFrame) { |
michael@0 | 965 | stopTelling = tableFrame->CellChangedWidth(*cellFrame, cellFrame->GetPass1MaxElementWidth(), |
michael@0 | 966 | cellFrame->GetMaximumWidth(), true); |
michael@0 | 967 | } |
michael@0 | 968 | } |
michael@0 | 969 | // XXX need to consider what happens if there are cells that have rowspans |
michael@0 | 970 | // into the deleted row. Need to consider moving rows if a rebalance doesn't happen |
michael@0 | 971 | #endif |
michael@0 | 972 | |
michael@0 | 973 | int32_t firstRowIndex = aFirstRowFrame.GetRowIndex(); |
michael@0 | 974 | #ifdef DEBUG_TABLE_CELLMAP |
michael@0 | 975 | printf("=== removeRowsBefore firstRow=%d numRows=%d\n", firstRowIndex, aNumRowsToRemove); |
michael@0 | 976 | Dump(true, false, true); |
michael@0 | 977 | #endif |
michael@0 | 978 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 979 | if (cellMap) { |
michael@0 | 980 | nsIntRect damageArea(0,0,0,0); |
michael@0 | 981 | cellMap->RemoveRows(firstRowIndex, aNumRowsToRemove, aConsiderSpans, damageArea); |
michael@0 | 982 | MatchCellMapToColCache(cellMap); |
michael@0 | 983 | if (IsBorderCollapse()) { |
michael@0 | 984 | AddBCDamageArea(damageArea); |
michael@0 | 985 | } |
michael@0 | 986 | } |
michael@0 | 987 | AdjustRowIndices(firstRowIndex, -aNumRowsToRemove); |
michael@0 | 988 | #ifdef DEBUG_TABLE_CELLMAP |
michael@0 | 989 | printf("=== removeRowsAfter\n"); |
michael@0 | 990 | Dump(true, true, true); |
michael@0 | 991 | #endif |
michael@0 | 992 | } |
michael@0 | 993 | |
michael@0 | 994 | // collect the rows ancestors of aFrame |
michael@0 | 995 | int32_t |
michael@0 | 996 | nsTableFrame::CollectRows(nsIFrame* aFrame, |
michael@0 | 997 | nsTArray<nsTableRowFrame*>& aCollection) |
michael@0 | 998 | { |
michael@0 | 999 | NS_PRECONDITION(aFrame, "null frame"); |
michael@0 | 1000 | int32_t numRows = 0; |
michael@0 | 1001 | nsIFrame* childFrame = aFrame->GetFirstPrincipalChild(); |
michael@0 | 1002 | while (childFrame) { |
michael@0 | 1003 | aCollection.AppendElement(static_cast<nsTableRowFrame*>(childFrame)); |
michael@0 | 1004 | numRows++; |
michael@0 | 1005 | childFrame = childFrame->GetNextSibling(); |
michael@0 | 1006 | } |
michael@0 | 1007 | return numRows; |
michael@0 | 1008 | } |
michael@0 | 1009 | |
michael@0 | 1010 | void |
michael@0 | 1011 | nsTableFrame::InsertRowGroups(const nsFrameList::Slice& aRowGroups) |
michael@0 | 1012 | { |
michael@0 | 1013 | #ifdef DEBUG_TABLE_CELLMAP |
michael@0 | 1014 | printf("=== insertRowGroupsBefore\n"); |
michael@0 | 1015 | Dump(true, false, true); |
michael@0 | 1016 | #endif |
michael@0 | 1017 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 1018 | if (cellMap) { |
michael@0 | 1019 | RowGroupArray orderedRowGroups; |
michael@0 | 1020 | OrderRowGroups(orderedRowGroups); |
michael@0 | 1021 | |
michael@0 | 1022 | nsAutoTArray<nsTableRowFrame*, 8> rows; |
michael@0 | 1023 | // Loop over the rowgroups and check if some of them are new, if they are |
michael@0 | 1024 | // insert cellmaps in the order that is predefined by OrderRowGroups, |
michael@0 | 1025 | // XXXbz this code is O(N*M) where N is number of new rowgroups |
michael@0 | 1026 | // and M is number of rowgroups we have! |
michael@0 | 1027 | uint32_t rgIndex; |
michael@0 | 1028 | for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { |
michael@0 | 1029 | for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd(); |
michael@0 | 1030 | rowgroups.Next()) { |
michael@0 | 1031 | if (orderedRowGroups[rgIndex] == rowgroups.get()) { |
michael@0 | 1032 | nsTableRowGroupFrame* priorRG = |
michael@0 | 1033 | (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1]; |
michael@0 | 1034 | // create and add the cell map for the row group |
michael@0 | 1035 | cellMap->InsertGroupCellMap(orderedRowGroups[rgIndex], priorRG); |
michael@0 | 1036 | |
michael@0 | 1037 | break; |
michael@0 | 1038 | } |
michael@0 | 1039 | } |
michael@0 | 1040 | } |
michael@0 | 1041 | cellMap->Synchronize(this); |
michael@0 | 1042 | ResetRowIndices(aRowGroups); |
michael@0 | 1043 | |
michael@0 | 1044 | //now that the cellmaps are reordered too insert the rows |
michael@0 | 1045 | for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { |
michael@0 | 1046 | for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd(); |
michael@0 | 1047 | rowgroups.Next()) { |
michael@0 | 1048 | if (orderedRowGroups[rgIndex] == rowgroups.get()) { |
michael@0 | 1049 | nsTableRowGroupFrame* priorRG = |
michael@0 | 1050 | (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1]; |
michael@0 | 1051 | // collect the new row frames in an array and add them to the table |
michael@0 | 1052 | int32_t numRows = CollectRows(rowgroups.get(), rows); |
michael@0 | 1053 | if (numRows > 0) { |
michael@0 | 1054 | int32_t rowIndex = 0; |
michael@0 | 1055 | if (priorRG) { |
michael@0 | 1056 | int32_t priorNumRows = priorRG->GetRowCount(); |
michael@0 | 1057 | rowIndex = priorRG->GetStartRowIndex() + priorNumRows; |
michael@0 | 1058 | } |
michael@0 | 1059 | InsertRows(orderedRowGroups[rgIndex], rows, rowIndex, true); |
michael@0 | 1060 | rows.Clear(); |
michael@0 | 1061 | } |
michael@0 | 1062 | break; |
michael@0 | 1063 | } |
michael@0 | 1064 | } |
michael@0 | 1065 | } |
michael@0 | 1066 | |
michael@0 | 1067 | } |
michael@0 | 1068 | #ifdef DEBUG_TABLE_CELLMAP |
michael@0 | 1069 | printf("=== insertRowGroupsAfter\n"); |
michael@0 | 1070 | Dump(true, true, true); |
michael@0 | 1071 | #endif |
michael@0 | 1072 | } |
michael@0 | 1073 | |
michael@0 | 1074 | |
michael@0 | 1075 | ///////////////////////////////////////////////////////////////////////////// |
michael@0 | 1076 | // Child frame enumeration |
michael@0 | 1077 | |
michael@0 | 1078 | const nsFrameList& |
michael@0 | 1079 | nsTableFrame::GetChildList(ChildListID aListID) const |
michael@0 | 1080 | { |
michael@0 | 1081 | if (aListID == kColGroupList) { |
michael@0 | 1082 | return mColGroups; |
michael@0 | 1083 | } |
michael@0 | 1084 | return nsContainerFrame::GetChildList(aListID); |
michael@0 | 1085 | } |
michael@0 | 1086 | |
michael@0 | 1087 | void |
michael@0 | 1088 | nsTableFrame::GetChildLists(nsTArray<ChildList>* aLists) const |
michael@0 | 1089 | { |
michael@0 | 1090 | nsContainerFrame::GetChildLists(aLists); |
michael@0 | 1091 | mColGroups.AppendIfNonempty(aLists, kColGroupList); |
michael@0 | 1092 | } |
michael@0 | 1093 | |
michael@0 | 1094 | nsRect |
michael@0 | 1095 | nsDisplayTableItem::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
michael@0 | 1096 | *aSnap = false; |
michael@0 | 1097 | return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); |
michael@0 | 1098 | } |
michael@0 | 1099 | |
michael@0 | 1100 | bool |
michael@0 | 1101 | nsDisplayTableItem::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder, |
michael@0 | 1102 | nsIFrame* aFrame) |
michael@0 | 1103 | { |
michael@0 | 1104 | if (!mPartHasFixedBackground) |
michael@0 | 1105 | return false; |
michael@0 | 1106 | |
michael@0 | 1107 | // If aFrame is mFrame or an ancestor in this document, and aFrame is |
michael@0 | 1108 | // not the viewport frame, then moving aFrame will move mFrame |
michael@0 | 1109 | // relative to the viewport, so our fixed-pos background will change. |
michael@0 | 1110 | return mFrame == aFrame || |
michael@0 | 1111 | nsLayoutUtils::IsProperAncestorFrame(aFrame, mFrame); |
michael@0 | 1112 | } |
michael@0 | 1113 | |
michael@0 | 1114 | /* static */ void |
michael@0 | 1115 | nsDisplayTableItem::UpdateForFrameBackground(nsIFrame* aFrame) |
michael@0 | 1116 | { |
michael@0 | 1117 | nsStyleContext *bgSC; |
michael@0 | 1118 | if (!nsCSSRendering::FindBackground(aFrame, &bgSC)) |
michael@0 | 1119 | return; |
michael@0 | 1120 | if (!bgSC->StyleBackground()->HasFixedBackground()) |
michael@0 | 1121 | return; |
michael@0 | 1122 | |
michael@0 | 1123 | mPartHasFixedBackground = true; |
michael@0 | 1124 | } |
michael@0 | 1125 | |
michael@0 | 1126 | class nsDisplayTableBorderBackground : public nsDisplayTableItem { |
michael@0 | 1127 | public: |
michael@0 | 1128 | nsDisplayTableBorderBackground(nsDisplayListBuilder* aBuilder, |
michael@0 | 1129 | nsTableFrame* aFrame) : |
michael@0 | 1130 | nsDisplayTableItem(aBuilder, aFrame) { |
michael@0 | 1131 | MOZ_COUNT_CTOR(nsDisplayTableBorderBackground); |
michael@0 | 1132 | } |
michael@0 | 1133 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 1134 | virtual ~nsDisplayTableBorderBackground() { |
michael@0 | 1135 | MOZ_COUNT_DTOR(nsDisplayTableBorderBackground); |
michael@0 | 1136 | } |
michael@0 | 1137 | #endif |
michael@0 | 1138 | |
michael@0 | 1139 | virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 1140 | const nsDisplayItemGeometry* aGeometry, |
michael@0 | 1141 | nsRegion *aInvalidRegion) MOZ_OVERRIDE; |
michael@0 | 1142 | virtual void Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 1143 | nsRenderingContext* aCtx) MOZ_OVERRIDE; |
michael@0 | 1144 | NS_DISPLAY_DECL_NAME("TableBorderBackground", TYPE_TABLE_BORDER_BACKGROUND) |
michael@0 | 1145 | }; |
michael@0 | 1146 | |
michael@0 | 1147 | #ifdef DEBUG |
michael@0 | 1148 | static bool |
michael@0 | 1149 | IsFrameAllowedInTable(nsIAtom* aType) |
michael@0 | 1150 | { |
michael@0 | 1151 | return IS_TABLE_CELL(aType) || |
michael@0 | 1152 | nsGkAtoms::tableRowFrame == aType || |
michael@0 | 1153 | nsGkAtoms::tableRowGroupFrame == aType || |
michael@0 | 1154 | nsGkAtoms::scrollFrame == aType || |
michael@0 | 1155 | nsGkAtoms::tableFrame == aType || |
michael@0 | 1156 | nsGkAtoms::tableColFrame == aType || |
michael@0 | 1157 | nsGkAtoms::tableColGroupFrame == aType; |
michael@0 | 1158 | } |
michael@0 | 1159 | #endif |
michael@0 | 1160 | |
michael@0 | 1161 | /* static */ bool |
michael@0 | 1162 | nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(nsIFrame* aStart, |
michael@0 | 1163 | nsIFrame* aEnd) |
michael@0 | 1164 | { |
michael@0 | 1165 | for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) { |
michael@0 | 1166 | NS_ASSERTION(IsFrameAllowedInTable(f->GetType()), "unexpected frame type"); |
michael@0 | 1167 | |
michael@0 | 1168 | if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(f)) |
michael@0 | 1169 | return true; |
michael@0 | 1170 | |
michael@0 | 1171 | nsTableCellFrame *cellFrame = do_QueryFrame(f); |
michael@0 | 1172 | if (cellFrame) |
michael@0 | 1173 | continue; |
michael@0 | 1174 | |
michael@0 | 1175 | if (AnyTablePartHasUndecodedBackgroundImage(f->PrincipalChildList().FirstChild(), nullptr)) |
michael@0 | 1176 | return true; |
michael@0 | 1177 | } |
michael@0 | 1178 | |
michael@0 | 1179 | return false; |
michael@0 | 1180 | } |
michael@0 | 1181 | |
michael@0 | 1182 | void |
michael@0 | 1183 | nsDisplayTableBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 1184 | const nsDisplayItemGeometry* aGeometry, |
michael@0 | 1185 | nsRegion *aInvalidRegion) |
michael@0 | 1186 | { |
michael@0 | 1187 | if (aBuilder->ShouldSyncDecodeImages()) { |
michael@0 | 1188 | if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling()) || |
michael@0 | 1189 | nsTableFrame::AnyTablePartHasUndecodedBackgroundImage( |
michael@0 | 1190 | mFrame->GetChildList(nsIFrame::kColGroupList).FirstChild(), nullptr)) { |
michael@0 | 1191 | bool snap; |
michael@0 | 1192 | aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); |
michael@0 | 1193 | } |
michael@0 | 1194 | } |
michael@0 | 1195 | |
michael@0 | 1196 | nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); |
michael@0 | 1197 | } |
michael@0 | 1198 | |
michael@0 | 1199 | void |
michael@0 | 1200 | nsDisplayTableBorderBackground::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 1201 | nsRenderingContext* aCtx) |
michael@0 | 1202 | { |
michael@0 | 1203 | static_cast<nsTableFrame*>(mFrame)-> |
michael@0 | 1204 | PaintTableBorderBackground(*aCtx, mVisibleRect, |
michael@0 | 1205 | ToReferenceFrame(), |
michael@0 | 1206 | aBuilder->GetBackgroundPaintFlags()); |
michael@0 | 1207 | } |
michael@0 | 1208 | |
michael@0 | 1209 | static int32_t GetTablePartRank(nsDisplayItem* aItem) |
michael@0 | 1210 | { |
michael@0 | 1211 | nsIAtom* type = aItem->Frame()->GetType(); |
michael@0 | 1212 | if (type == nsGkAtoms::tableFrame) |
michael@0 | 1213 | return 0; |
michael@0 | 1214 | if (type == nsGkAtoms::tableRowGroupFrame) |
michael@0 | 1215 | return 1; |
michael@0 | 1216 | if (type == nsGkAtoms::tableRowFrame) |
michael@0 | 1217 | return 2; |
michael@0 | 1218 | return 3; |
michael@0 | 1219 | } |
michael@0 | 1220 | |
michael@0 | 1221 | static bool CompareByTablePartRank(nsDisplayItem* aItem1, nsDisplayItem* aItem2, |
michael@0 | 1222 | void* aClosure) |
michael@0 | 1223 | { |
michael@0 | 1224 | return GetTablePartRank(aItem1) <= GetTablePartRank(aItem2); |
michael@0 | 1225 | } |
michael@0 | 1226 | |
michael@0 | 1227 | /* static */ void |
michael@0 | 1228 | nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, |
michael@0 | 1229 | const nsRect& aDirtyRect, const nsDisplayListSet& aLists) |
michael@0 | 1230 | { |
michael@0 | 1231 | // This is similar to what nsContainerFrame::BuildDisplayListForNonBlockChildren |
michael@0 | 1232 | // does, except that we allow the children's background and borders to go |
michael@0 | 1233 | // in our BorderBackground list. This doesn't really affect background |
michael@0 | 1234 | // painting --- the children won't actually draw their own backgrounds |
michael@0 | 1235 | // because the nsTableFrame already drew them, unless a child has its own |
michael@0 | 1236 | // stacking context, in which case the child won't use its passed-in |
michael@0 | 1237 | // BorderBackground list anyway. It does affect cell borders though; this |
michael@0 | 1238 | // lets us get cell borders into the nsTableFrame's BorderBackground list. |
michael@0 | 1239 | nsIFrame* kid = aFrame->GetFirstPrincipalChild(); |
michael@0 | 1240 | while (kid) { |
michael@0 | 1241 | aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); |
michael@0 | 1242 | kid = kid->GetNextSibling(); |
michael@0 | 1243 | } |
michael@0 | 1244 | } |
michael@0 | 1245 | |
michael@0 | 1246 | /* static */ void |
michael@0 | 1247 | nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder, |
michael@0 | 1248 | nsFrame* aFrame, |
michael@0 | 1249 | const nsRect& aDirtyRect, |
michael@0 | 1250 | const nsDisplayListSet& aLists, |
michael@0 | 1251 | nsDisplayTableItem* aDisplayItem, |
michael@0 | 1252 | DisplayGenericTablePartTraversal aTraversal) |
michael@0 | 1253 | { |
michael@0 | 1254 | nsDisplayList eventsBorderBackground; |
michael@0 | 1255 | // If we need to sort the event backgrounds, then we'll put descendants' |
michael@0 | 1256 | // display items into their own set of lists. |
michael@0 | 1257 | bool sortEventBackgrounds = aDisplayItem && aBuilder->IsForEventDelivery(); |
michael@0 | 1258 | nsDisplayListCollection separatedCollection; |
michael@0 | 1259 | const nsDisplayListSet* lists = sortEventBackgrounds ? &separatedCollection : &aLists; |
michael@0 | 1260 | |
michael@0 | 1261 | nsAutoPushCurrentTableItem pushTableItem; |
michael@0 | 1262 | if (aDisplayItem) { |
michael@0 | 1263 | pushTableItem.Push(aBuilder, aDisplayItem); |
michael@0 | 1264 | } |
michael@0 | 1265 | |
michael@0 | 1266 | if (aFrame->IsVisibleForPainting(aBuilder)) { |
michael@0 | 1267 | nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem(); |
michael@0 | 1268 | // currentItem may be null, when none of the table parts have a |
michael@0 | 1269 | // background or border |
michael@0 | 1270 | if (currentItem) { |
michael@0 | 1271 | currentItem->UpdateForFrameBackground(aFrame); |
michael@0 | 1272 | } |
michael@0 | 1273 | |
michael@0 | 1274 | // Paint the outset box-shadows for the table frames |
michael@0 | 1275 | bool hasBoxShadow = aFrame->StyleBorder()->mBoxShadow != nullptr; |
michael@0 | 1276 | if (hasBoxShadow) { |
michael@0 | 1277 | lists->BorderBackground()->AppendNewToTop( |
michael@0 | 1278 | new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, aFrame)); |
michael@0 | 1279 | } |
michael@0 | 1280 | |
michael@0 | 1281 | // Create dedicated background display items per-frame when we're |
michael@0 | 1282 | // handling events. |
michael@0 | 1283 | // XXX how to handle collapsed borders? |
michael@0 | 1284 | if (aBuilder->IsForEventDelivery()) { |
michael@0 | 1285 | nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, |
michael@0 | 1286 | lists->BorderBackground()); |
michael@0 | 1287 | } |
michael@0 | 1288 | |
michael@0 | 1289 | // Paint the inset box-shadows for the table frames |
michael@0 | 1290 | if (hasBoxShadow) { |
michael@0 | 1291 | lists->BorderBackground()->AppendNewToTop( |
michael@0 | 1292 | new (aBuilder) nsDisplayBoxShadowInner(aBuilder, aFrame)); |
michael@0 | 1293 | } |
michael@0 | 1294 | } |
michael@0 | 1295 | |
michael@0 | 1296 | aTraversal(aBuilder, aFrame, aDirtyRect, *lists); |
michael@0 | 1297 | |
michael@0 | 1298 | if (sortEventBackgrounds) { |
michael@0 | 1299 | // Ensure that the table frame event background goes before the |
michael@0 | 1300 | // table rowgroups event backgrounds, before the table row event backgrounds, |
michael@0 | 1301 | // before everything else (cells and their blocks) |
michael@0 | 1302 | separatedCollection.BorderBackground()->Sort(aBuilder, CompareByTablePartRank, nullptr); |
michael@0 | 1303 | separatedCollection.MoveTo(aLists); |
michael@0 | 1304 | } |
michael@0 | 1305 | |
michael@0 | 1306 | aFrame->DisplayOutline(aBuilder, aLists); |
michael@0 | 1307 | } |
michael@0 | 1308 | |
michael@0 | 1309 | static bool |
michael@0 | 1310 | AnyTablePartHasBorderOrBackground(nsIFrame* aStart, nsIFrame* aEnd) |
michael@0 | 1311 | { |
michael@0 | 1312 | for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) { |
michael@0 | 1313 | NS_ASSERTION(IsFrameAllowedInTable(f->GetType()), "unexpected frame type"); |
michael@0 | 1314 | |
michael@0 | 1315 | if (FrameHasBorderOrBackground(f)) |
michael@0 | 1316 | return true; |
michael@0 | 1317 | |
michael@0 | 1318 | nsTableCellFrame *cellFrame = do_QueryFrame(f); |
michael@0 | 1319 | if (cellFrame) |
michael@0 | 1320 | continue; |
michael@0 | 1321 | |
michael@0 | 1322 | if (AnyTablePartHasBorderOrBackground(f->PrincipalChildList().FirstChild(), nullptr)) |
michael@0 | 1323 | return true; |
michael@0 | 1324 | } |
michael@0 | 1325 | |
michael@0 | 1326 | return false; |
michael@0 | 1327 | } |
michael@0 | 1328 | |
michael@0 | 1329 | // table paint code is concerned primarily with borders and bg color |
michael@0 | 1330 | // SEC: TODO: adjust the rect for captions |
michael@0 | 1331 | void |
michael@0 | 1332 | nsTableFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
michael@0 | 1333 | const nsRect& aDirtyRect, |
michael@0 | 1334 | const nsDisplayListSet& aLists) |
michael@0 | 1335 | { |
michael@0 | 1336 | DO_GLOBAL_REFLOW_COUNT_DSP_COLOR("nsTableFrame", NS_RGB(255,128,255)); |
michael@0 | 1337 | |
michael@0 | 1338 | nsDisplayTableItem* item = nullptr; |
michael@0 | 1339 | if (IsVisibleInSelection(aBuilder)) { |
michael@0 | 1340 | if (StyleVisibility()->IsVisible()) { |
michael@0 | 1341 | nsMargin deflate = GetDeflationForBackground(PresContext()); |
michael@0 | 1342 | // If 'deflate' is (0,0,0,0) then we can paint the table background |
michael@0 | 1343 | // in its own display item, so do that to take advantage of |
michael@0 | 1344 | // opacity and visibility optimizations |
michael@0 | 1345 | if (deflate == nsMargin(0, 0, 0, 0)) { |
michael@0 | 1346 | DisplayBackgroundUnconditional(aBuilder, aLists, false); |
michael@0 | 1347 | } |
michael@0 | 1348 | } |
michael@0 | 1349 | |
michael@0 | 1350 | // This background is created if any of the table parts are visible, |
michael@0 | 1351 | // or if we're doing event handling (since DisplayGenericTablePart |
michael@0 | 1352 | // needs the item for the |sortEventBackgrounds|-dependent code). |
michael@0 | 1353 | // Specific visibility decisions are delegated to the table background |
michael@0 | 1354 | // painter, which handles borders and backgrounds for the table. |
michael@0 | 1355 | if (aBuilder->IsForEventDelivery() || |
michael@0 | 1356 | AnyTablePartHasBorderOrBackground(this, GetNextSibling()) || |
michael@0 | 1357 | AnyTablePartHasBorderOrBackground(mColGroups.FirstChild(), nullptr)) { |
michael@0 | 1358 | item = new (aBuilder) nsDisplayTableBorderBackground(aBuilder, this); |
michael@0 | 1359 | aLists.BorderBackground()->AppendNewToTop(item); |
michael@0 | 1360 | } |
michael@0 | 1361 | } |
michael@0 | 1362 | DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item); |
michael@0 | 1363 | } |
michael@0 | 1364 | |
michael@0 | 1365 | nsMargin |
michael@0 | 1366 | nsTableFrame::GetDeflationForBackground(nsPresContext* aPresContext) const |
michael@0 | 1367 | { |
michael@0 | 1368 | if (eCompatibility_NavQuirks != aPresContext->CompatibilityMode() || |
michael@0 | 1369 | !IsBorderCollapse()) |
michael@0 | 1370 | return nsMargin(0,0,0,0); |
michael@0 | 1371 | |
michael@0 | 1372 | return GetOuterBCBorder(); |
michael@0 | 1373 | } |
michael@0 | 1374 | |
michael@0 | 1375 | // XXX We don't put the borders and backgrounds in tree order like we should. |
michael@0 | 1376 | // That requires some major surgery which we aren't going to do right now. |
michael@0 | 1377 | void |
michael@0 | 1378 | nsTableFrame::PaintTableBorderBackground(nsRenderingContext& aRenderingContext, |
michael@0 | 1379 | const nsRect& aDirtyRect, |
michael@0 | 1380 | nsPoint aPt, uint32_t aBGPaintFlags) |
michael@0 | 1381 | { |
michael@0 | 1382 | nsPresContext* presContext = PresContext(); |
michael@0 | 1383 | |
michael@0 | 1384 | TableBackgroundPainter painter(this, TableBackgroundPainter::eOrigin_Table, |
michael@0 | 1385 | presContext, aRenderingContext, |
michael@0 | 1386 | aDirtyRect, aPt, aBGPaintFlags); |
michael@0 | 1387 | nsMargin deflate = GetDeflationForBackground(presContext); |
michael@0 | 1388 | // If 'deflate' is (0,0,0,0) then we'll paint the table background |
michael@0 | 1389 | // in a separate display item, so don't do it here. |
michael@0 | 1390 | nsresult rv = painter.PaintTable(this, deflate, deflate != nsMargin(0, 0, 0, 0)); |
michael@0 | 1391 | if (NS_FAILED(rv)) return; |
michael@0 | 1392 | |
michael@0 | 1393 | if (StyleVisibility()->IsVisible()) { |
michael@0 | 1394 | if (!IsBorderCollapse()) { |
michael@0 | 1395 | int skipSides = GetSkipSides(); |
michael@0 | 1396 | nsRect rect(aPt, mRect.Size()); |
michael@0 | 1397 | nsCSSRendering::PaintBorder(presContext, aRenderingContext, this, |
michael@0 | 1398 | aDirtyRect, rect, mStyleContext, skipSides); |
michael@0 | 1399 | } |
michael@0 | 1400 | else { |
michael@0 | 1401 | // XXX we should probably get rid of this translation at some stage |
michael@0 | 1402 | // But that would mean modifying PaintBCBorders, ugh |
michael@0 | 1403 | nsRenderingContext::AutoPushTranslation translate(&aRenderingContext, aPt); |
michael@0 | 1404 | PaintBCBorders(aRenderingContext, aDirtyRect - aPt); |
michael@0 | 1405 | } |
michael@0 | 1406 | } |
michael@0 | 1407 | } |
michael@0 | 1408 | |
michael@0 | 1409 | int |
michael@0 | 1410 | nsTableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const |
michael@0 | 1411 | { |
michael@0 | 1412 | int skip = 0; |
michael@0 | 1413 | // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto |
michael@0 | 1414 | // account for pagination |
michael@0 | 1415 | if (nullptr != GetPrevInFlow()) { |
michael@0 | 1416 | skip |= LOGICAL_SIDE_B_START; |
michael@0 | 1417 | } |
michael@0 | 1418 | if (nullptr != GetNextInFlow()) { |
michael@0 | 1419 | skip |= LOGICAL_SIDE_B_END; |
michael@0 | 1420 | } |
michael@0 | 1421 | return skip; |
michael@0 | 1422 | } |
michael@0 | 1423 | |
michael@0 | 1424 | void |
michael@0 | 1425 | nsTableFrame::SetColumnDimensions(nscoord aHeight, |
michael@0 | 1426 | const nsMargin& aBorderPadding) |
michael@0 | 1427 | { |
michael@0 | 1428 | nscoord cellSpacingX = GetCellSpacingX(); |
michael@0 | 1429 | nscoord cellSpacingY = GetCellSpacingY(); |
michael@0 | 1430 | nscoord colHeight = aHeight -= aBorderPadding.top + aBorderPadding.bottom + |
michael@0 | 1431 | 2* cellSpacingY; |
michael@0 | 1432 | |
michael@0 | 1433 | nsTableIterator iter(mColGroups); |
michael@0 | 1434 | nsIFrame* colGroupFrame = iter.First(); |
michael@0 | 1435 | bool tableIsLTR = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR; |
michael@0 | 1436 | int32_t colX =tableIsLTR ? 0 : std::max(0, GetColCount() - 1); |
michael@0 | 1437 | int32_t tableColIncr = tableIsLTR ? 1 : -1; |
michael@0 | 1438 | nsPoint colGroupOrigin(aBorderPadding.left + cellSpacingX, |
michael@0 | 1439 | aBorderPadding.top + cellSpacingY); |
michael@0 | 1440 | while (colGroupFrame) { |
michael@0 | 1441 | MOZ_ASSERT(colGroupFrame->GetType() == nsGkAtoms::tableColGroupFrame); |
michael@0 | 1442 | nscoord colGroupWidth = 0; |
michael@0 | 1443 | nsTableIterator iterCol(*colGroupFrame); |
michael@0 | 1444 | nsIFrame* colFrame = iterCol.First(); |
michael@0 | 1445 | nsPoint colOrigin(0,0); |
michael@0 | 1446 | while (colFrame) { |
michael@0 | 1447 | if (NS_STYLE_DISPLAY_TABLE_COLUMN == |
michael@0 | 1448 | colFrame->StyleDisplay()->mDisplay) { |
michael@0 | 1449 | NS_ASSERTION(colX < GetColCount(), "invalid number of columns"); |
michael@0 | 1450 | nscoord colWidth = GetColumnWidth(colX); |
michael@0 | 1451 | nsRect colRect(colOrigin.x, colOrigin.y, colWidth, colHeight); |
michael@0 | 1452 | colFrame->SetRect(colRect); |
michael@0 | 1453 | colOrigin.x += colWidth + cellSpacingX; |
michael@0 | 1454 | colGroupWidth += colWidth + cellSpacingX; |
michael@0 | 1455 | colX += tableColIncr; |
michael@0 | 1456 | } |
michael@0 | 1457 | colFrame = iterCol.Next(); |
michael@0 | 1458 | } |
michael@0 | 1459 | if (colGroupWidth) { |
michael@0 | 1460 | colGroupWidth -= cellSpacingX; |
michael@0 | 1461 | } |
michael@0 | 1462 | |
michael@0 | 1463 | nsRect colGroupRect(colGroupOrigin.x, colGroupOrigin.y, colGroupWidth, colHeight); |
michael@0 | 1464 | colGroupFrame->SetRect(colGroupRect); |
michael@0 | 1465 | colGroupFrame = iter.Next(); |
michael@0 | 1466 | colGroupOrigin.x += colGroupWidth + cellSpacingX; |
michael@0 | 1467 | } |
michael@0 | 1468 | } |
michael@0 | 1469 | |
michael@0 | 1470 | // SEC: TODO need to worry about continuing frames prev/next in flow for splitting across pages. |
michael@0 | 1471 | |
michael@0 | 1472 | // XXX this could be made more general to handle row modifications that change the |
michael@0 | 1473 | // table height, but first we need to scrutinize every Invalidate |
michael@0 | 1474 | void |
michael@0 | 1475 | nsTableFrame::ProcessRowInserted(nscoord aNewHeight) |
michael@0 | 1476 | { |
michael@0 | 1477 | SetRowInserted(false); // reset the bit that got us here |
michael@0 | 1478 | nsTableFrame::RowGroupArray rowGroups; |
michael@0 | 1479 | OrderRowGroups(rowGroups); |
michael@0 | 1480 | // find the row group containing the inserted row |
michael@0 | 1481 | for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { |
michael@0 | 1482 | nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
michael@0 | 1483 | NS_ASSERTION(rgFrame, "Must have rgFrame here"); |
michael@0 | 1484 | nsIFrame* childFrame = rgFrame->GetFirstPrincipalChild(); |
michael@0 | 1485 | // find the row that was inserted first |
michael@0 | 1486 | while (childFrame) { |
michael@0 | 1487 | nsTableRowFrame *rowFrame = do_QueryFrame(childFrame); |
michael@0 | 1488 | if (rowFrame) { |
michael@0 | 1489 | if (rowFrame->IsFirstInserted()) { |
michael@0 | 1490 | rowFrame->SetFirstInserted(false); |
michael@0 | 1491 | // damage the table from the 1st row inserted to the end of the table |
michael@0 | 1492 | nsIFrame::InvalidateFrame(); |
michael@0 | 1493 | // XXXbz didn't we do this up front? Why do we need to do it again? |
michael@0 | 1494 | SetRowInserted(false); |
michael@0 | 1495 | return; // found it, so leave |
michael@0 | 1496 | } |
michael@0 | 1497 | } |
michael@0 | 1498 | childFrame = childFrame->GetNextSibling(); |
michael@0 | 1499 | } |
michael@0 | 1500 | } |
michael@0 | 1501 | } |
michael@0 | 1502 | |
michael@0 | 1503 | /* virtual */ void |
michael@0 | 1504 | nsTableFrame::MarkIntrinsicWidthsDirty() |
michael@0 | 1505 | { |
michael@0 | 1506 | nsITableLayoutStrategy* tls = LayoutStrategy(); |
michael@0 | 1507 | if (MOZ_UNLIKELY(!tls)) { |
michael@0 | 1508 | // This is a FrameNeedsReflow() from nsBlockFrame::RemoveFrame() |
michael@0 | 1509 | // walking up the ancestor chain in a table next-in-flow. In this case |
michael@0 | 1510 | // our original first-in-flow (which owns the TableLayoutStrategy) has |
michael@0 | 1511 | // already been destroyed and unhooked from the flow chain and thusly |
michael@0 | 1512 | // LayoutStrategy() returns null. All the frames in the flow will be |
michael@0 | 1513 | // destroyed so no need to mark anything dirty here. See bug 595758. |
michael@0 | 1514 | return; |
michael@0 | 1515 | } |
michael@0 | 1516 | tls->MarkIntrinsicWidthsDirty(); |
michael@0 | 1517 | |
michael@0 | 1518 | // XXXldb Call SetBCDamageArea? |
michael@0 | 1519 | |
michael@0 | 1520 | nsContainerFrame::MarkIntrinsicWidthsDirty(); |
michael@0 | 1521 | } |
michael@0 | 1522 | |
michael@0 | 1523 | /* virtual */ nscoord |
michael@0 | 1524 | nsTableFrame::GetMinWidth(nsRenderingContext *aRenderingContext) |
michael@0 | 1525 | { |
michael@0 | 1526 | if (NeedToCalcBCBorders()) |
michael@0 | 1527 | CalcBCBorders(); |
michael@0 | 1528 | |
michael@0 | 1529 | ReflowColGroups(aRenderingContext); |
michael@0 | 1530 | |
michael@0 | 1531 | return LayoutStrategy()->GetMinWidth(aRenderingContext); |
michael@0 | 1532 | } |
michael@0 | 1533 | |
michael@0 | 1534 | /* virtual */ nscoord |
michael@0 | 1535 | nsTableFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) |
michael@0 | 1536 | { |
michael@0 | 1537 | if (NeedToCalcBCBorders()) |
michael@0 | 1538 | CalcBCBorders(); |
michael@0 | 1539 | |
michael@0 | 1540 | ReflowColGroups(aRenderingContext); |
michael@0 | 1541 | |
michael@0 | 1542 | return LayoutStrategy()->GetPrefWidth(aRenderingContext, false); |
michael@0 | 1543 | } |
michael@0 | 1544 | |
michael@0 | 1545 | /* virtual */ nsIFrame::IntrinsicWidthOffsetData |
michael@0 | 1546 | nsTableFrame::IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext) |
michael@0 | 1547 | { |
michael@0 | 1548 | IntrinsicWidthOffsetData result = |
michael@0 | 1549 | nsContainerFrame::IntrinsicWidthOffsets(aRenderingContext); |
michael@0 | 1550 | |
michael@0 | 1551 | result.hMargin = 0; |
michael@0 | 1552 | result.hPctMargin = 0; |
michael@0 | 1553 | |
michael@0 | 1554 | if (IsBorderCollapse()) { |
michael@0 | 1555 | result.hPadding = 0; |
michael@0 | 1556 | result.hPctPadding = 0; |
michael@0 | 1557 | |
michael@0 | 1558 | nsMargin outerBC = GetIncludedOuterBCBorder(); |
michael@0 | 1559 | result.hBorder = outerBC.LeftRight(); |
michael@0 | 1560 | } |
michael@0 | 1561 | |
michael@0 | 1562 | return result; |
michael@0 | 1563 | } |
michael@0 | 1564 | |
michael@0 | 1565 | /* virtual */ nsSize |
michael@0 | 1566 | nsTableFrame::ComputeSize(nsRenderingContext *aRenderingContext, |
michael@0 | 1567 | nsSize aCBSize, nscoord aAvailableWidth, |
michael@0 | 1568 | nsSize aMargin, nsSize aBorder, nsSize aPadding, |
michael@0 | 1569 | uint32_t aFlags) |
michael@0 | 1570 | { |
michael@0 | 1571 | nsSize result = |
michael@0 | 1572 | nsContainerFrame::ComputeSize(aRenderingContext, aCBSize, aAvailableWidth, |
michael@0 | 1573 | aMargin, aBorder, aPadding, aFlags); |
michael@0 | 1574 | |
michael@0 | 1575 | // If we're a container for font size inflation, then shrink |
michael@0 | 1576 | // wrapping inside of us should not apply font size inflation. |
michael@0 | 1577 | AutoMaybeDisableFontInflation an(this); |
michael@0 | 1578 | |
michael@0 | 1579 | // Tables never shrink below their min width. |
michael@0 | 1580 | nscoord minWidth = GetMinWidth(aRenderingContext); |
michael@0 | 1581 | if (minWidth > result.width) |
michael@0 | 1582 | result.width = minWidth; |
michael@0 | 1583 | |
michael@0 | 1584 | return result; |
michael@0 | 1585 | } |
michael@0 | 1586 | |
michael@0 | 1587 | nscoord |
michael@0 | 1588 | nsTableFrame::TableShrinkWidthToFit(nsRenderingContext *aRenderingContext, |
michael@0 | 1589 | nscoord aWidthInCB) |
michael@0 | 1590 | { |
michael@0 | 1591 | // If we're a container for font size inflation, then shrink |
michael@0 | 1592 | // wrapping inside of us should not apply font size inflation. |
michael@0 | 1593 | AutoMaybeDisableFontInflation an(this); |
michael@0 | 1594 | |
michael@0 | 1595 | nscoord result; |
michael@0 | 1596 | nscoord minWidth = GetMinWidth(aRenderingContext); |
michael@0 | 1597 | if (minWidth > aWidthInCB) { |
michael@0 | 1598 | result = minWidth; |
michael@0 | 1599 | } else { |
michael@0 | 1600 | // Tables shrink width to fit with a slightly different algorithm |
michael@0 | 1601 | // from the one they use for their intrinsic widths (the difference |
michael@0 | 1602 | // relates to handling of percentage widths on columns). So this |
michael@0 | 1603 | // function differs from nsFrame::ShrinkWidthToFit by only the |
michael@0 | 1604 | // following line. |
michael@0 | 1605 | // Since we've already called GetMinWidth, we don't need to do any |
michael@0 | 1606 | // of the other stuff GetPrefWidth does. |
michael@0 | 1607 | nscoord prefWidth = |
michael@0 | 1608 | LayoutStrategy()->GetPrefWidth(aRenderingContext, true); |
michael@0 | 1609 | if (prefWidth > aWidthInCB) { |
michael@0 | 1610 | result = aWidthInCB; |
michael@0 | 1611 | } else { |
michael@0 | 1612 | result = prefWidth; |
michael@0 | 1613 | } |
michael@0 | 1614 | } |
michael@0 | 1615 | return result; |
michael@0 | 1616 | } |
michael@0 | 1617 | |
michael@0 | 1618 | /* virtual */ nsSize |
michael@0 | 1619 | nsTableFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, |
michael@0 | 1620 | nsSize aCBSize, nscoord aAvailableWidth, |
michael@0 | 1621 | nsSize aMargin, nsSize aBorder, nsSize aPadding, |
michael@0 | 1622 | bool aShrinkWrap) |
michael@0 | 1623 | { |
michael@0 | 1624 | // Tables always shrink-wrap. |
michael@0 | 1625 | nscoord cbBased = aAvailableWidth - aMargin.width - aBorder.width - |
michael@0 | 1626 | aPadding.width; |
michael@0 | 1627 | return nsSize(TableShrinkWidthToFit(aRenderingContext, cbBased), |
michael@0 | 1628 | NS_UNCONSTRAINEDSIZE); |
michael@0 | 1629 | } |
michael@0 | 1630 | |
michael@0 | 1631 | // Return true if aParentReflowState.frame or any of its ancestors within |
michael@0 | 1632 | // the containing table have non-auto height. (e.g. pct or fixed height) |
michael@0 | 1633 | bool |
michael@0 | 1634 | nsTableFrame::AncestorsHaveStyleHeight(const nsHTMLReflowState& aParentReflowState) |
michael@0 | 1635 | { |
michael@0 | 1636 | for (const nsHTMLReflowState* rs = &aParentReflowState; |
michael@0 | 1637 | rs && rs->frame; rs = rs->parentReflowState) { |
michael@0 | 1638 | nsIAtom* frameType = rs->frame->GetType(); |
michael@0 | 1639 | if (IS_TABLE_CELL(frameType) || |
michael@0 | 1640 | (nsGkAtoms::tableRowFrame == frameType) || |
michael@0 | 1641 | (nsGkAtoms::tableRowGroupFrame == frameType)) { |
michael@0 | 1642 | const nsStyleCoord &height = rs->mStylePosition->mHeight; |
michael@0 | 1643 | // calc() with percentages treated like 'auto' on internal table elements |
michael@0 | 1644 | if (height.GetUnit() != eStyleUnit_Auto && |
michael@0 | 1645 | (!height.IsCalcUnit() || !height.HasPercent())) { |
michael@0 | 1646 | return true; |
michael@0 | 1647 | } |
michael@0 | 1648 | } |
michael@0 | 1649 | else if (nsGkAtoms::tableFrame == frameType) { |
michael@0 | 1650 | // we reached the containing table, so always return |
michael@0 | 1651 | return rs->mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto; |
michael@0 | 1652 | } |
michael@0 | 1653 | } |
michael@0 | 1654 | return false; |
michael@0 | 1655 | } |
michael@0 | 1656 | |
michael@0 | 1657 | // See if a special height reflow needs to occur and if so, call RequestSpecialHeightReflow |
michael@0 | 1658 | void |
michael@0 | 1659 | nsTableFrame::CheckRequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState) |
michael@0 | 1660 | { |
michael@0 | 1661 | NS_ASSERTION(IS_TABLE_CELL(aReflowState.frame->GetType()) || |
michael@0 | 1662 | aReflowState.frame->GetType() == nsGkAtoms::tableRowFrame || |
michael@0 | 1663 | aReflowState.frame->GetType() == nsGkAtoms::tableRowGroupFrame || |
michael@0 | 1664 | aReflowState.frame->GetType() == nsGkAtoms::tableFrame, |
michael@0 | 1665 | "unexpected frame type"); |
michael@0 | 1666 | if (!aReflowState.frame->GetPrevInFlow() && // 1st in flow |
michael@0 | 1667 | (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight() || // no computed height |
michael@0 | 1668 | 0 == aReflowState.ComputedHeight()) && |
michael@0 | 1669 | eStyleUnit_Percent == aReflowState.mStylePosition->mHeight.GetUnit() && // pct height |
michael@0 | 1670 | nsTableFrame::AncestorsHaveStyleHeight(*aReflowState.parentReflowState)) { |
michael@0 | 1671 | nsTableFrame::RequestSpecialHeightReflow(aReflowState); |
michael@0 | 1672 | } |
michael@0 | 1673 | } |
michael@0 | 1674 | |
michael@0 | 1675 | // Notify the frame and its ancestors (up to the containing table) that a special |
michael@0 | 1676 | // height reflow will occur. During a special height reflow, a table, row group, |
michael@0 | 1677 | // row, or cell returns the last size it was reflowed at. However, the table may |
michael@0 | 1678 | // change the height of row groups, rows, cells in DistributeHeightToRows after. |
michael@0 | 1679 | // And the row group can change the height of rows, cells in CalculateRowHeights. |
michael@0 | 1680 | void |
michael@0 | 1681 | nsTableFrame::RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState) |
michael@0 | 1682 | { |
michael@0 | 1683 | // notify the frame and its ancestors of the special reflow, stopping at the containing table |
michael@0 | 1684 | for (const nsHTMLReflowState* rs = &aReflowState; rs && rs->frame; rs = rs->parentReflowState) { |
michael@0 | 1685 | nsIAtom* frameType = rs->frame->GetType(); |
michael@0 | 1686 | NS_ASSERTION(IS_TABLE_CELL(frameType) || |
michael@0 | 1687 | nsGkAtoms::tableRowFrame == frameType || |
michael@0 | 1688 | nsGkAtoms::tableRowGroupFrame == frameType || |
michael@0 | 1689 | nsGkAtoms::tableFrame == frameType, |
michael@0 | 1690 | "unexpected frame type"); |
michael@0 | 1691 | |
michael@0 | 1692 | rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); |
michael@0 | 1693 | if (nsGkAtoms::tableFrame == frameType) { |
michael@0 | 1694 | NS_ASSERTION(rs != &aReflowState, |
michael@0 | 1695 | "should not request special height reflow for table"); |
michael@0 | 1696 | // always stop when we reach a table |
michael@0 | 1697 | break; |
michael@0 | 1698 | } |
michael@0 | 1699 | } |
michael@0 | 1700 | } |
michael@0 | 1701 | |
michael@0 | 1702 | /****************************************************************************************** |
michael@0 | 1703 | * Before reflow, intrinsic width calculation is done using GetMinWidth |
michael@0 | 1704 | * and GetPrefWidth. This used to be known as pass 1 reflow. |
michael@0 | 1705 | * |
michael@0 | 1706 | * After the intrinsic width calculation, the table determines the |
michael@0 | 1707 | * column widths using BalanceColumnWidths() and |
michael@0 | 1708 | * then reflows each child again with a constrained avail width. This reflow is referred to |
michael@0 | 1709 | * as the pass 2 reflow. |
michael@0 | 1710 | * |
michael@0 | 1711 | * A special height reflow (pass 3 reflow) can occur during an initial or resize reflow |
michael@0 | 1712 | * if (a) a row group, row, cell, or a frame inside a cell has a percent height but no computed |
michael@0 | 1713 | * height or (b) in paginated mode, a table has a height. (a) supports percent nested tables |
michael@0 | 1714 | * contained inside cells whose heights aren't known until after the pass 2 reflow. (b) is |
michael@0 | 1715 | * necessary because the table cannot split until after the pass 2 reflow. The mechanics of |
michael@0 | 1716 | * the special height reflow (variety a) are as follows: |
michael@0 | 1717 | * |
michael@0 | 1718 | * 1) Each table related frame (table, row group, row, cell) implements NeedsSpecialReflow() |
michael@0 | 1719 | * to indicate that it should get the reflow. It does this when it has a percent height but |
michael@0 | 1720 | * no computed height by calling CheckRequestSpecialHeightReflow(). This method calls |
michael@0 | 1721 | * RequestSpecialHeightReflow() which calls SetNeedSpecialReflow() on its ancestors until |
michael@0 | 1722 | * it reaches the containing table and calls SetNeedToInitiateSpecialReflow() on it. For |
michael@0 | 1723 | * percent height frames inside cells, during DidReflow(), the cell's NotifyPercentHeight() |
michael@0 | 1724 | * is called (the cell is the reflow state's mPercentHeightObserver in this case). |
michael@0 | 1725 | * NotifyPercentHeight() calls RequestSpecialHeightReflow(). |
michael@0 | 1726 | * |
michael@0 | 1727 | * 2) After the pass 2 reflow, if the table's NeedToInitiateSpecialReflow(true) was called, it |
michael@0 | 1728 | * will do the special height reflow, setting the reflow state's mFlags.mSpecialHeightReflow |
michael@0 | 1729 | * to true and mSpecialHeightInitiator to itself. It won't do this if IsPrematureSpecialHeightReflow() |
michael@0 | 1730 | * returns true because in that case another special height reflow will be coming along with the |
michael@0 | 1731 | * containing table as the mSpecialHeightInitiator. It is only relevant to do the reflow when |
michael@0 | 1732 | * the mSpecialHeightInitiator is the containing table, because if it is a remote ancestor, then |
michael@0 | 1733 | * appropriate heights will not be known. |
michael@0 | 1734 | * |
michael@0 | 1735 | * 3) Since the heights of the table, row groups, rows, and cells was determined during the pass 2 |
michael@0 | 1736 | * reflow, they return their last desired sizes during the special height reflow. The reflow only |
michael@0 | 1737 | * permits percent height frames inside the cells to resize based on the cells height and that height |
michael@0 | 1738 | * was determined during the pass 2 reflow. |
michael@0 | 1739 | * |
michael@0 | 1740 | * So, in the case of deeply nested tables, all of the tables that were told to initiate a special |
michael@0 | 1741 | * reflow will do so, but if a table is already in a special reflow, it won't inititate the reflow |
michael@0 | 1742 | * until the current initiator is its containing table. Since these reflows are only received by |
michael@0 | 1743 | * frames that need them and they don't cause any rebalancing of tables, the extra overhead is minimal. |
michael@0 | 1744 | * |
michael@0 | 1745 | * The type of special reflow that occurs during printing (variety b) follows the same mechanism except |
michael@0 | 1746 | * that all frames will receive the reflow even if they don't really need them. |
michael@0 | 1747 | * |
michael@0 | 1748 | * Open issues with the special height reflow: |
michael@0 | 1749 | * |
michael@0 | 1750 | * 1) At some point there should be 2 kinds of special height reflows because (a) and (b) above are |
michael@0 | 1751 | * really quite different. This would avoid unnecessary reflows during printing. |
michael@0 | 1752 | * 2) When a cell contains frames whose percent heights > 100%, there is data loss (see bug 115245). |
michael@0 | 1753 | * However, this can also occur if a cell has a fixed height and there is no special height reflow. |
michael@0 | 1754 | * |
michael@0 | 1755 | * XXXldb Special height reflow should really be its own method, not |
michael@0 | 1756 | * part of nsIFrame::Reflow. It should then call nsIFrame::Reflow on |
michael@0 | 1757 | * the contents of the cells to do the necessary vertical resizing. |
michael@0 | 1758 | * |
michael@0 | 1759 | ******************************************************************************************/ |
michael@0 | 1760 | |
michael@0 | 1761 | /* Layout the entire inner table. */ |
michael@0 | 1762 | nsresult nsTableFrame::Reflow(nsPresContext* aPresContext, |
michael@0 | 1763 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 1764 | const nsHTMLReflowState& aReflowState, |
michael@0 | 1765 | nsReflowStatus& aStatus) |
michael@0 | 1766 | { |
michael@0 | 1767 | DO_GLOBAL_REFLOW_COUNT("nsTableFrame"); |
michael@0 | 1768 | DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); |
michael@0 | 1769 | bool isPaginated = aPresContext->IsPaginated(); |
michael@0 | 1770 | |
michael@0 | 1771 | aStatus = NS_FRAME_COMPLETE; |
michael@0 | 1772 | if (!GetPrevInFlow() && !mTableLayoutStrategy) { |
michael@0 | 1773 | NS_ASSERTION(false, "strategy should have been created in Init"); |
michael@0 | 1774 | return NS_ERROR_NULL_POINTER; |
michael@0 | 1775 | } |
michael@0 | 1776 | nsresult rv = NS_OK; |
michael@0 | 1777 | |
michael@0 | 1778 | // see if collapsing borders need to be calculated |
michael@0 | 1779 | if (!GetPrevInFlow() && IsBorderCollapse() && NeedToCalcBCBorders()) { |
michael@0 | 1780 | CalcBCBorders(); |
michael@0 | 1781 | } |
michael@0 | 1782 | |
michael@0 | 1783 | aDesiredSize.Width() = aReflowState.AvailableWidth(); |
michael@0 | 1784 | |
michael@0 | 1785 | // Check for an overflow list, and append any row group frames being pushed |
michael@0 | 1786 | MoveOverflowToChildList(); |
michael@0 | 1787 | |
michael@0 | 1788 | bool haveDesiredHeight = false; |
michael@0 | 1789 | SetHaveReflowedColGroups(false); |
michael@0 | 1790 | |
michael@0 | 1791 | // Reflow the entire table (pass 2 and possibly pass 3). This phase is necessary during a |
michael@0 | 1792 | // constrained initial reflow and other reflows which require either a strategy init or balance. |
michael@0 | 1793 | // This isn't done during an unconstrained reflow, because it will occur later when the parent |
michael@0 | 1794 | // reflows with a constrained width. |
michael@0 | 1795 | if (NS_SUBTREE_DIRTY(this) || |
michael@0 | 1796 | aReflowState.ShouldReflowAllKids() || |
michael@0 | 1797 | IsGeometryDirty() || |
michael@0 | 1798 | aReflowState.mFlags.mVResize) { |
michael@0 | 1799 | |
michael@0 | 1800 | if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE || |
michael@0 | 1801 | // Also check mVResize, to handle the first Reflow preceding a |
michael@0 | 1802 | // special height Reflow, when we've already had a special height |
michael@0 | 1803 | // Reflow (where mComputedHeight would not be |
michael@0 | 1804 | // NS_UNCONSTRAINEDSIZE, but without a style change in between). |
michael@0 | 1805 | aReflowState.mFlags.mVResize) { |
michael@0 | 1806 | // XXX Eventually, we should modify DistributeHeightToRows to use |
michael@0 | 1807 | // nsTableRowFrame::GetHeight instead of nsIFrame::GetSize().height. |
michael@0 | 1808 | // That way, it will make its calculations based on internal table |
michael@0 | 1809 | // frame heights as they are before they ever had any extra height |
michael@0 | 1810 | // distributed to them. In the meantime, this reflows all the |
michael@0 | 1811 | // internal table frames, which restores them to their state before |
michael@0 | 1812 | // DistributeHeightToRows was called. |
michael@0 | 1813 | SetGeometryDirty(); |
michael@0 | 1814 | } |
michael@0 | 1815 | |
michael@0 | 1816 | bool needToInitiateSpecialReflow = |
michael@0 | 1817 | !!(GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT); |
michael@0 | 1818 | // see if an extra reflow will be necessary in pagination mode when there is a specified table height |
michael@0 | 1819 | if (isPaginated && !GetPrevInFlow() && (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight())) { |
michael@0 | 1820 | nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); |
michael@0 | 1821 | if ((tableSpecifiedHeight > 0) && |
michael@0 | 1822 | (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) { |
michael@0 | 1823 | needToInitiateSpecialReflow = true; |
michael@0 | 1824 | } |
michael@0 | 1825 | } |
michael@0 | 1826 | nsIFrame* lastChildReflowed = nullptr; |
michael@0 | 1827 | |
michael@0 | 1828 | NS_ASSERTION(!aReflowState.mFlags.mSpecialHeightReflow, |
michael@0 | 1829 | "Shouldn't be in special height reflow here!"); |
michael@0 | 1830 | |
michael@0 | 1831 | // do the pass 2 reflow unless this is a special height reflow and we will be |
michael@0 | 1832 | // initiating a special height reflow |
michael@0 | 1833 | // XXXldb I changed this. Should I change it back? |
michael@0 | 1834 | |
michael@0 | 1835 | // if we need to initiate a special height reflow, then don't constrain the |
michael@0 | 1836 | // height of the reflow before that |
michael@0 | 1837 | nscoord availHeight = needToInitiateSpecialReflow |
michael@0 | 1838 | ? NS_UNCONSTRAINEDSIZE : aReflowState.AvailableHeight(); |
michael@0 | 1839 | |
michael@0 | 1840 | ReflowTable(aDesiredSize, aReflowState, availHeight, |
michael@0 | 1841 | lastChildReflowed, aStatus); |
michael@0 | 1842 | |
michael@0 | 1843 | // reevaluate special height reflow conditions |
michael@0 | 1844 | if (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) |
michael@0 | 1845 | needToInitiateSpecialReflow = true; |
michael@0 | 1846 | |
michael@0 | 1847 | // XXXldb Are all these conditions correct? |
michael@0 | 1848 | if (needToInitiateSpecialReflow && NS_FRAME_IS_COMPLETE(aStatus)) { |
michael@0 | 1849 | // XXXldb Do we need to set the mVResize flag on any reflow states? |
michael@0 | 1850 | |
michael@0 | 1851 | nsHTMLReflowState &mutable_rs = |
michael@0 | 1852 | const_cast<nsHTMLReflowState&>(aReflowState); |
michael@0 | 1853 | |
michael@0 | 1854 | // distribute extra vertical space to rows |
michael@0 | 1855 | CalcDesiredHeight(aReflowState, aDesiredSize); |
michael@0 | 1856 | mutable_rs.mFlags.mSpecialHeightReflow = true; |
michael@0 | 1857 | |
michael@0 | 1858 | ReflowTable(aDesiredSize, aReflowState, aReflowState.AvailableHeight(), |
michael@0 | 1859 | lastChildReflowed, aStatus); |
michael@0 | 1860 | |
michael@0 | 1861 | if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) { |
michael@0 | 1862 | // if there is an incomplete child, then set the desired height to include it but not the next one |
michael@0 | 1863 | nsMargin borderPadding = GetChildAreaOffset(&aReflowState); |
michael@0 | 1864 | aDesiredSize.Height() = borderPadding.bottom + GetCellSpacingY() + |
michael@0 | 1865 | lastChildReflowed->GetRect().YMost(); |
michael@0 | 1866 | } |
michael@0 | 1867 | haveDesiredHeight = true; |
michael@0 | 1868 | |
michael@0 | 1869 | mutable_rs.mFlags.mSpecialHeightReflow = false; |
michael@0 | 1870 | } |
michael@0 | 1871 | } |
michael@0 | 1872 | else { |
michael@0 | 1873 | // Calculate the overflow area contribution from our children. |
michael@0 | 1874 | for (nsIFrame* kid = GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) { |
michael@0 | 1875 | ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kid); |
michael@0 | 1876 | } |
michael@0 | 1877 | } |
michael@0 | 1878 | |
michael@0 | 1879 | aDesiredSize.Width() = aReflowState.ComputedWidth() + |
michael@0 | 1880 | aReflowState.ComputedPhysicalBorderPadding().LeftRight(); |
michael@0 | 1881 | if (!haveDesiredHeight) { |
michael@0 | 1882 | CalcDesiredHeight(aReflowState, aDesiredSize); |
michael@0 | 1883 | } |
michael@0 | 1884 | if (IsRowInserted()) { |
michael@0 | 1885 | ProcessRowInserted(aDesiredSize.Height()); |
michael@0 | 1886 | } |
michael@0 | 1887 | |
michael@0 | 1888 | nsMargin borderPadding = GetChildAreaOffset(&aReflowState); |
michael@0 | 1889 | SetColumnDimensions(aDesiredSize.Height(), borderPadding); |
michael@0 | 1890 | if (NeedToCollapse() && |
michael@0 | 1891 | (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableWidth())) { |
michael@0 | 1892 | AdjustForCollapsingRowsCols(aDesiredSize, borderPadding); |
michael@0 | 1893 | } |
michael@0 | 1894 | |
michael@0 | 1895 | // If there are any relatively-positioned table parts, we need to reflow their |
michael@0 | 1896 | // absolutely-positioned descendants now that their dimensions are final. |
michael@0 | 1897 | FixupPositionedTableParts(aPresContext, aDesiredSize, aReflowState); |
michael@0 | 1898 | |
michael@0 | 1899 | // make sure the table overflow area does include the table rect. |
michael@0 | 1900 | nsRect tableRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()) ; |
michael@0 | 1901 | |
michael@0 | 1902 | if (!ShouldApplyOverflowClipping(this, aReflowState.mStyleDisplay)) { |
michael@0 | 1903 | // collapsed border may leak out |
michael@0 | 1904 | nsMargin bcMargin = GetExcludedOuterBCBorder(); |
michael@0 | 1905 | tableRect.Inflate(bcMargin); |
michael@0 | 1906 | } |
michael@0 | 1907 | aDesiredSize.mOverflowAreas.UnionAllWith(tableRect); |
michael@0 | 1908 | |
michael@0 | 1909 | if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) || |
michael@0 | 1910 | nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) { |
michael@0 | 1911 | nsIFrame::InvalidateFrame(); |
michael@0 | 1912 | } |
michael@0 | 1913 | |
michael@0 | 1914 | FinishAndStoreOverflow(&aDesiredSize); |
michael@0 | 1915 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
michael@0 | 1916 | return rv; |
michael@0 | 1917 | } |
michael@0 | 1918 | |
michael@0 | 1919 | void |
michael@0 | 1920 | nsTableFrame::FixupPositionedTableParts(nsPresContext* aPresContext, |
michael@0 | 1921 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 1922 | const nsHTMLReflowState& aReflowState) |
michael@0 | 1923 | { |
michael@0 | 1924 | auto positionedParts = |
michael@0 | 1925 | static_cast<FrameTArray*>(Properties().Get(PositionedTablePartArray())); |
michael@0 | 1926 | if (!positionedParts) { |
michael@0 | 1927 | return; |
michael@0 | 1928 | } |
michael@0 | 1929 | |
michael@0 | 1930 | OverflowChangedTracker overflowTracker; |
michael@0 | 1931 | overflowTracker.SetSubtreeRoot(this); |
michael@0 | 1932 | |
michael@0 | 1933 | for (size_t i = 0; i < positionedParts->Length(); ++i) { |
michael@0 | 1934 | nsIFrame* positionedPart = positionedParts->ElementAt(i); |
michael@0 | 1935 | |
michael@0 | 1936 | // As we've already finished reflow, positionedParts's size and overflow |
michael@0 | 1937 | // areas have already been assigned, so we just pull them back out. |
michael@0 | 1938 | nsSize size(positionedPart->GetSize()); |
michael@0 | 1939 | nsHTMLReflowMetrics desiredSize(aReflowState.GetWritingMode()); |
michael@0 | 1940 | desiredSize.Width() = size.width; |
michael@0 | 1941 | desiredSize.Height() = size.height; |
michael@0 | 1942 | desiredSize.mOverflowAreas = positionedPart->GetOverflowAreasRelativeToSelf(); |
michael@0 | 1943 | |
michael@0 | 1944 | // Construct a dummy reflow state and reflow status. |
michael@0 | 1945 | // XXX(seth): Note that the dummy reflow state doesn't have a correct |
michael@0 | 1946 | // chain of parent reflow states. It also doesn't necessarily have a |
michael@0 | 1947 | // correct containing block. |
michael@0 | 1948 | nsHTMLReflowState reflowState(aPresContext, positionedPart, |
michael@0 | 1949 | aReflowState.rendContext, |
michael@0 | 1950 | nsSize(size.width, NS_UNCONSTRAINEDSIZE), |
michael@0 | 1951 | nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE); |
michael@0 | 1952 | nsReflowStatus reflowStatus = NS_FRAME_COMPLETE; |
michael@0 | 1953 | |
michael@0 | 1954 | // Reflow absolutely-positioned descendants of the positioned part. |
michael@0 | 1955 | // FIXME: Unconditionally using NS_UNCONSTRAINEDSIZE for the height and |
michael@0 | 1956 | // ignoring any change to the reflow status aren't correct. We'll never |
michael@0 | 1957 | // paginate absolutely positioned frames. |
michael@0 | 1958 | overflowTracker.AddFrame(positionedPart, |
michael@0 | 1959 | OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED); |
michael@0 | 1960 | nsFrame* positionedFrame = static_cast<nsFrame*>(positionedPart); |
michael@0 | 1961 | positionedFrame->FinishReflowWithAbsoluteFrames(PresContext(), |
michael@0 | 1962 | desiredSize, |
michael@0 | 1963 | reflowState, |
michael@0 | 1964 | reflowStatus, |
michael@0 | 1965 | true); |
michael@0 | 1966 | } |
michael@0 | 1967 | |
michael@0 | 1968 | // Propagate updated overflow areas up the tree. |
michael@0 | 1969 | overflowTracker.Flush(); |
michael@0 | 1970 | |
michael@0 | 1971 | // Update our own overflow areas. (OverflowChangedTracker doesn't update the |
michael@0 | 1972 | // subtree root itself.) |
michael@0 | 1973 | aDesiredSize.SetOverflowAreasToDesiredBounds(); |
michael@0 | 1974 | nsLayoutUtils::UnionChildOverflow(this, aDesiredSize.mOverflowAreas); |
michael@0 | 1975 | } |
michael@0 | 1976 | |
michael@0 | 1977 | bool |
michael@0 | 1978 | nsTableFrame::UpdateOverflow() |
michael@0 | 1979 | { |
michael@0 | 1980 | nsRect bounds(nsPoint(0, 0), GetSize()); |
michael@0 | 1981 | |
michael@0 | 1982 | // As above in Reflow, make sure the table overflow area includes the table |
michael@0 | 1983 | // rect, and check for collapsed borders leaking out. |
michael@0 | 1984 | if (!ShouldApplyOverflowClipping(this, StyleDisplay())) { |
michael@0 | 1985 | nsMargin bcMargin = GetExcludedOuterBCBorder(); |
michael@0 | 1986 | bounds.Inflate(bcMargin); |
michael@0 | 1987 | } |
michael@0 | 1988 | |
michael@0 | 1989 | nsOverflowAreas overflowAreas(bounds, bounds); |
michael@0 | 1990 | nsLayoutUtils::UnionChildOverflow(this, overflowAreas); |
michael@0 | 1991 | |
michael@0 | 1992 | return FinishAndStoreOverflow(overflowAreas, GetSize()); |
michael@0 | 1993 | } |
michael@0 | 1994 | |
michael@0 | 1995 | nsresult |
michael@0 | 1996 | nsTableFrame::ReflowTable(nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 1997 | const nsHTMLReflowState& aReflowState, |
michael@0 | 1998 | nscoord aAvailHeight, |
michael@0 | 1999 | nsIFrame*& aLastChildReflowed, |
michael@0 | 2000 | nsReflowStatus& aStatus) |
michael@0 | 2001 | { |
michael@0 | 2002 | nsresult rv = NS_OK; |
michael@0 | 2003 | aLastChildReflowed = nullptr; |
michael@0 | 2004 | |
michael@0 | 2005 | if (!GetPrevInFlow()) { |
michael@0 | 2006 | mTableLayoutStrategy->ComputeColumnWidths(aReflowState); |
michael@0 | 2007 | } |
michael@0 | 2008 | // Constrain our reflow width to the computed table width (of the 1st in flow). |
michael@0 | 2009 | // and our reflow height to our avail height minus border, padding, cellspacing |
michael@0 | 2010 | aDesiredSize.Width() = aReflowState.ComputedWidth() + |
michael@0 | 2011 | aReflowState.ComputedPhysicalBorderPadding().LeftRight(); |
michael@0 | 2012 | nsTableReflowState reflowState(*PresContext(), aReflowState, *this, |
michael@0 | 2013 | aDesiredSize.Width(), aAvailHeight); |
michael@0 | 2014 | ReflowChildren(reflowState, aStatus, aLastChildReflowed, |
michael@0 | 2015 | aDesiredSize.mOverflowAreas); |
michael@0 | 2016 | |
michael@0 | 2017 | ReflowColGroups(aReflowState.rendContext); |
michael@0 | 2018 | return rv; |
michael@0 | 2019 | } |
michael@0 | 2020 | |
michael@0 | 2021 | nsIFrame* |
michael@0 | 2022 | nsTableFrame::GetFirstBodyRowGroupFrame() |
michael@0 | 2023 | { |
michael@0 | 2024 | nsIFrame* headerFrame = nullptr; |
michael@0 | 2025 | nsIFrame* footerFrame = nullptr; |
michael@0 | 2026 | |
michael@0 | 2027 | for (nsIFrame* kidFrame = mFrames.FirstChild(); nullptr != kidFrame; ) { |
michael@0 | 2028 | const nsStyleDisplay* childDisplay = kidFrame->StyleDisplay(); |
michael@0 | 2029 | |
michael@0 | 2030 | // We expect the header and footer row group frames to be first, and we only |
michael@0 | 2031 | // allow one header and one footer |
michael@0 | 2032 | if (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == childDisplay->mDisplay) { |
michael@0 | 2033 | if (headerFrame) { |
michael@0 | 2034 | // We already have a header frame and so this header frame is treated |
michael@0 | 2035 | // like an ordinary body row group frame |
michael@0 | 2036 | return kidFrame; |
michael@0 | 2037 | } |
michael@0 | 2038 | headerFrame = kidFrame; |
michael@0 | 2039 | |
michael@0 | 2040 | } else if (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == childDisplay->mDisplay) { |
michael@0 | 2041 | if (footerFrame) { |
michael@0 | 2042 | // We already have a footer frame and so this footer frame is treated |
michael@0 | 2043 | // like an ordinary body row group frame |
michael@0 | 2044 | return kidFrame; |
michael@0 | 2045 | } |
michael@0 | 2046 | footerFrame = kidFrame; |
michael@0 | 2047 | |
michael@0 | 2048 | } else if (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == childDisplay->mDisplay) { |
michael@0 | 2049 | return kidFrame; |
michael@0 | 2050 | } |
michael@0 | 2051 | |
michael@0 | 2052 | // Get the next child |
michael@0 | 2053 | kidFrame = kidFrame->GetNextSibling(); |
michael@0 | 2054 | } |
michael@0 | 2055 | |
michael@0 | 2056 | return nullptr; |
michael@0 | 2057 | } |
michael@0 | 2058 | |
michael@0 | 2059 | // Table specific version that takes into account repeated header and footer |
michael@0 | 2060 | // frames when continuing table frames |
michael@0 | 2061 | void |
michael@0 | 2062 | nsTableFrame::PushChildren(const RowGroupArray& aRowGroups, |
michael@0 | 2063 | int32_t aPushFrom) |
michael@0 | 2064 | { |
michael@0 | 2065 | NS_PRECONDITION(aPushFrom > 0, "pushing first child"); |
michael@0 | 2066 | |
michael@0 | 2067 | // extract the frames from the array into a sibling list |
michael@0 | 2068 | nsFrameList frames; |
michael@0 | 2069 | uint32_t childX; |
michael@0 | 2070 | for (childX = aPushFrom; childX < aRowGroups.Length(); ++childX) { |
michael@0 | 2071 | nsTableRowGroupFrame* rgFrame = aRowGroups[childX]; |
michael@0 | 2072 | if (!rgFrame->IsRepeatable()) { |
michael@0 | 2073 | mFrames.RemoveFrame(rgFrame); |
michael@0 | 2074 | frames.AppendFrame(nullptr, rgFrame); |
michael@0 | 2075 | } |
michael@0 | 2076 | } |
michael@0 | 2077 | |
michael@0 | 2078 | if (frames.IsEmpty()) { |
michael@0 | 2079 | return; |
michael@0 | 2080 | } |
michael@0 | 2081 | |
michael@0 | 2082 | nsTableFrame* nextInFlow = static_cast<nsTableFrame*>(GetNextInFlow()); |
michael@0 | 2083 | if (nextInFlow) { |
michael@0 | 2084 | // Insert the frames after any repeated header and footer frames. |
michael@0 | 2085 | nsIFrame* firstBodyFrame = nextInFlow->GetFirstBodyRowGroupFrame(); |
michael@0 | 2086 | nsIFrame* prevSibling = nullptr; |
michael@0 | 2087 | if (firstBodyFrame) { |
michael@0 | 2088 | prevSibling = firstBodyFrame->GetPrevSibling(); |
michael@0 | 2089 | } |
michael@0 | 2090 | // When pushing and pulling frames we need to check for whether any |
michael@0 | 2091 | // views need to be reparented. |
michael@0 | 2092 | ReparentFrameViewList(frames, this, nextInFlow); |
michael@0 | 2093 | nextInFlow->mFrames.InsertFrames(nextInFlow, prevSibling, |
michael@0 | 2094 | frames); |
michael@0 | 2095 | } |
michael@0 | 2096 | else { |
michael@0 | 2097 | // Add the frames to our overflow list. |
michael@0 | 2098 | SetOverflowFrames(frames); |
michael@0 | 2099 | } |
michael@0 | 2100 | } |
michael@0 | 2101 | |
michael@0 | 2102 | // collapsing row groups, rows, col groups and cols are accounted for after both passes of |
michael@0 | 2103 | // reflow so that it has no effect on the calculations of reflow. |
michael@0 | 2104 | void |
michael@0 | 2105 | nsTableFrame::AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 2106 | nsMargin aBorderPadding) |
michael@0 | 2107 | { |
michael@0 | 2108 | nscoord yTotalOffset = 0; // total offset among all rows in all row groups |
michael@0 | 2109 | |
michael@0 | 2110 | // reset the bit, it will be set again if row/rowgroup or col/colgroup are |
michael@0 | 2111 | // collapsed |
michael@0 | 2112 | SetNeedToCollapse(false); |
michael@0 | 2113 | |
michael@0 | 2114 | // collapse the rows and/or row groups as necessary |
michael@0 | 2115 | // Get the ordered children |
michael@0 | 2116 | RowGroupArray rowGroups; |
michael@0 | 2117 | OrderRowGroups(rowGroups); |
michael@0 | 2118 | |
michael@0 | 2119 | nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow()); |
michael@0 | 2120 | nscoord width = firstInFlow->GetCollapsedWidth(aBorderPadding); |
michael@0 | 2121 | nscoord rgWidth = width - 2 * GetCellSpacingX(); |
michael@0 | 2122 | nsOverflowAreas overflow; |
michael@0 | 2123 | // Walk the list of children |
michael@0 | 2124 | for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) { |
michael@0 | 2125 | nsTableRowGroupFrame* rgFrame = rowGroups[childX]; |
michael@0 | 2126 | NS_ASSERTION(rgFrame, "Must have row group frame here"); |
michael@0 | 2127 | yTotalOffset += rgFrame->CollapseRowGroupIfNecessary(yTotalOffset, rgWidth); |
michael@0 | 2128 | ConsiderChildOverflow(overflow, rgFrame); |
michael@0 | 2129 | } |
michael@0 | 2130 | |
michael@0 | 2131 | aDesiredSize.Height() -= yTotalOffset; |
michael@0 | 2132 | aDesiredSize.Width() = width; |
michael@0 | 2133 | overflow.UnionAllWith(nsRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height())); |
michael@0 | 2134 | FinishAndStoreOverflow(overflow, |
michael@0 | 2135 | nsSize(aDesiredSize.Width(), aDesiredSize.Height())); |
michael@0 | 2136 | } |
michael@0 | 2137 | |
michael@0 | 2138 | |
michael@0 | 2139 | nscoord |
michael@0 | 2140 | nsTableFrame::GetCollapsedWidth(nsMargin aBorderPadding) |
michael@0 | 2141 | { |
michael@0 | 2142 | NS_ASSERTION(!GetPrevInFlow(), "GetCollapsedWidth called on next in flow"); |
michael@0 | 2143 | nscoord cellSpacingX = GetCellSpacingX(); |
michael@0 | 2144 | nscoord width = cellSpacingX; |
michael@0 | 2145 | width += aBorderPadding.left + aBorderPadding.right; |
michael@0 | 2146 | for (nsIFrame* groupFrame = mColGroups.FirstChild(); groupFrame; |
michael@0 | 2147 | groupFrame = groupFrame->GetNextSibling()) { |
michael@0 | 2148 | const nsStyleVisibility* groupVis = groupFrame->StyleVisibility(); |
michael@0 | 2149 | bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible); |
michael@0 | 2150 | nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame; |
michael@0 | 2151 | for (nsTableColFrame* colFrame = cgFrame->GetFirstColumn(); colFrame; |
michael@0 | 2152 | colFrame = colFrame->GetNextCol()) { |
michael@0 | 2153 | const nsStyleDisplay* colDisplay = colFrame->StyleDisplay(); |
michael@0 | 2154 | int32_t colX = colFrame->GetColIndex(); |
michael@0 | 2155 | if (NS_STYLE_DISPLAY_TABLE_COLUMN == colDisplay->mDisplay) { |
michael@0 | 2156 | const nsStyleVisibility* colVis = colFrame->StyleVisibility(); |
michael@0 | 2157 | bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible); |
michael@0 | 2158 | int32_t colWidth = GetColumnWidth(colX); |
michael@0 | 2159 | if (!collapseGroup && !collapseCol) { |
michael@0 | 2160 | width += colWidth; |
michael@0 | 2161 | if (ColumnHasCellSpacingBefore(colX)) |
michael@0 | 2162 | width += cellSpacingX; |
michael@0 | 2163 | } |
michael@0 | 2164 | else { |
michael@0 | 2165 | SetNeedToCollapse(true); |
michael@0 | 2166 | } |
michael@0 | 2167 | } |
michael@0 | 2168 | } |
michael@0 | 2169 | } |
michael@0 | 2170 | return width; |
michael@0 | 2171 | } |
michael@0 | 2172 | |
michael@0 | 2173 | /* virtual */ void |
michael@0 | 2174 | nsTableFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) |
michael@0 | 2175 | { |
michael@0 | 2176 | nsContainerFrame::DidSetStyleContext(aOldStyleContext); |
michael@0 | 2177 | |
michael@0 | 2178 | if (!aOldStyleContext) //avoid this on init |
michael@0 | 2179 | return; |
michael@0 | 2180 | |
michael@0 | 2181 | if (IsBorderCollapse() && |
michael@0 | 2182 | BCRecalcNeeded(aOldStyleContext, StyleContext())) { |
michael@0 | 2183 | SetFullBCDamageArea(); |
michael@0 | 2184 | } |
michael@0 | 2185 | |
michael@0 | 2186 | //avoid this on init or nextinflow |
michael@0 | 2187 | if (!mTableLayoutStrategy || GetPrevInFlow()) |
michael@0 | 2188 | return; |
michael@0 | 2189 | |
michael@0 | 2190 | bool isAuto = IsAutoLayout(); |
michael@0 | 2191 | if (isAuto != (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto)) { |
michael@0 | 2192 | nsITableLayoutStrategy* temp; |
michael@0 | 2193 | if (isAuto) |
michael@0 | 2194 | temp = new BasicTableLayoutStrategy(this); |
michael@0 | 2195 | else |
michael@0 | 2196 | temp = new FixedTableLayoutStrategy(this); |
michael@0 | 2197 | |
michael@0 | 2198 | if (temp) { |
michael@0 | 2199 | delete mTableLayoutStrategy; |
michael@0 | 2200 | mTableLayoutStrategy = temp; |
michael@0 | 2201 | } |
michael@0 | 2202 | } |
michael@0 | 2203 | } |
michael@0 | 2204 | |
michael@0 | 2205 | |
michael@0 | 2206 | |
michael@0 | 2207 | nsresult |
michael@0 | 2208 | nsTableFrame::AppendFrames(ChildListID aListID, |
michael@0 | 2209 | nsFrameList& aFrameList) |
michael@0 | 2210 | { |
michael@0 | 2211 | NS_ASSERTION(aListID == kPrincipalList || aListID == kColGroupList, |
michael@0 | 2212 | "unexpected child list"); |
michael@0 | 2213 | |
michael@0 | 2214 | // Because we actually have two child lists, one for col group frames and one |
michael@0 | 2215 | // for everything else, we need to look at each frame individually |
michael@0 | 2216 | // XXX The frame construction code should be separating out child frames |
michael@0 | 2217 | // based on the type, bug 343048. |
michael@0 | 2218 | while (!aFrameList.IsEmpty()) { |
michael@0 | 2219 | nsIFrame* f = aFrameList.FirstChild(); |
michael@0 | 2220 | aFrameList.RemoveFrame(f); |
michael@0 | 2221 | |
michael@0 | 2222 | // See what kind of frame we have |
michael@0 | 2223 | const nsStyleDisplay* display = f->StyleDisplay(); |
michael@0 | 2224 | |
michael@0 | 2225 | if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) { |
michael@0 | 2226 | nsTableColGroupFrame* lastColGroup = |
michael@0 | 2227 | nsTableColGroupFrame::GetLastRealColGroup(this); |
michael@0 | 2228 | int32_t startColIndex = (lastColGroup) |
michael@0 | 2229 | ? lastColGroup->GetStartColumnIndex() + lastColGroup->GetColCount() : 0; |
michael@0 | 2230 | mColGroups.InsertFrame(nullptr, lastColGroup, f); |
michael@0 | 2231 | // Insert the colgroup and its cols into the table |
michael@0 | 2232 | InsertColGroups(startColIndex, |
michael@0 | 2233 | nsFrameList::Slice(mColGroups, f, f->GetNextSibling())); |
michael@0 | 2234 | } else if (IsRowGroup(display->mDisplay)) { |
michael@0 | 2235 | // Append the new row group frame to the sibling chain |
michael@0 | 2236 | mFrames.AppendFrame(nullptr, f); |
michael@0 | 2237 | |
michael@0 | 2238 | // insert the row group and its rows into the table |
michael@0 | 2239 | InsertRowGroups(nsFrameList::Slice(mFrames, f, nullptr)); |
michael@0 | 2240 | } else { |
michael@0 | 2241 | // Nothing special to do, just add the frame to our child list |
michael@0 | 2242 | NS_NOTREACHED("How did we get here? Frame construction screwed up"); |
michael@0 | 2243 | mFrames.AppendFrame(nullptr, f); |
michael@0 | 2244 | } |
michael@0 | 2245 | } |
michael@0 | 2246 | |
michael@0 | 2247 | #ifdef DEBUG_TABLE_CELLMAP |
michael@0 | 2248 | printf("=== TableFrame::AppendFrames\n"); |
michael@0 | 2249 | Dump(true, true, true); |
michael@0 | 2250 | #endif |
michael@0 | 2251 | PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
michael@0 | 2252 | NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 2253 | SetGeometryDirty(); |
michael@0 | 2254 | |
michael@0 | 2255 | return NS_OK; |
michael@0 | 2256 | } |
michael@0 | 2257 | |
michael@0 | 2258 | // Needs to be at file scope or ArrayLength fails to compile. |
michael@0 | 2259 | struct ChildListInsertions { |
michael@0 | 2260 | nsIFrame::ChildListID mID; |
michael@0 | 2261 | nsFrameList mList; |
michael@0 | 2262 | }; |
michael@0 | 2263 | |
michael@0 | 2264 | nsresult |
michael@0 | 2265 | nsTableFrame::InsertFrames(ChildListID aListID, |
michael@0 | 2266 | nsIFrame* aPrevFrame, |
michael@0 | 2267 | nsFrameList& aFrameList) |
michael@0 | 2268 | { |
michael@0 | 2269 | // The frames in aFrameList can be a mix of row group frames and col group |
michael@0 | 2270 | // frames. The problem is that they should go in separate child lists so |
michael@0 | 2271 | // we need to deal with that here... |
michael@0 | 2272 | // XXX The frame construction code should be separating out child frames |
michael@0 | 2273 | // based on the type, bug 343048. |
michael@0 | 2274 | |
michael@0 | 2275 | NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, |
michael@0 | 2276 | "inserting after sibling frame with different parent"); |
michael@0 | 2277 | |
michael@0 | 2278 | if ((aPrevFrame && !aPrevFrame->GetNextSibling()) || |
michael@0 | 2279 | (!aPrevFrame && GetChildList(aListID).IsEmpty())) { |
michael@0 | 2280 | // Treat this like an append; still a workaround for bug 343048. |
michael@0 | 2281 | return AppendFrames(aListID, aFrameList); |
michael@0 | 2282 | } |
michael@0 | 2283 | |
michael@0 | 2284 | // Collect ColGroupFrames into a separate list and insert those separately |
michael@0 | 2285 | // from the other frames (bug 759249). |
michael@0 | 2286 | ChildListInsertions insertions[2]; // ColGroup, other |
michael@0 | 2287 | const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay(); |
michael@0 | 2288 | nsFrameList::FrameLinkEnumerator e(aFrameList); |
michael@0 | 2289 | for (; !aFrameList.IsEmpty(); e.Next()) { |
michael@0 | 2290 | nsIFrame* next = e.NextFrame(); |
michael@0 | 2291 | if (!next || next->StyleDisplay()->mDisplay != display->mDisplay) { |
michael@0 | 2292 | nsFrameList head = aFrameList.ExtractHead(e); |
michael@0 | 2293 | if (display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) { |
michael@0 | 2294 | insertions[0].mID = kColGroupList; |
michael@0 | 2295 | insertions[0].mList.AppendFrames(nullptr, head); |
michael@0 | 2296 | } else { |
michael@0 | 2297 | insertions[1].mID = kPrincipalList; |
michael@0 | 2298 | insertions[1].mList.AppendFrames(nullptr, head); |
michael@0 | 2299 | } |
michael@0 | 2300 | if (!next) { |
michael@0 | 2301 | break; |
michael@0 | 2302 | } |
michael@0 | 2303 | display = next->StyleDisplay(); |
michael@0 | 2304 | } |
michael@0 | 2305 | } |
michael@0 | 2306 | for (uint32_t i = 0; i < ArrayLength(insertions); ++i) { |
michael@0 | 2307 | // We pass aPrevFrame for both ColGroup and other frames since |
michael@0 | 2308 | // HomogenousInsertFrames will only use it if it's a suitable |
michael@0 | 2309 | // prev-sibling for the frames in the frame list. |
michael@0 | 2310 | if (!insertions[i].mList.IsEmpty()) { |
michael@0 | 2311 | HomogenousInsertFrames(insertions[i].mID, aPrevFrame, |
michael@0 | 2312 | insertions[i].mList); |
michael@0 | 2313 | } |
michael@0 | 2314 | } |
michael@0 | 2315 | return NS_OK; |
michael@0 | 2316 | } |
michael@0 | 2317 | |
michael@0 | 2318 | void |
michael@0 | 2319 | nsTableFrame::HomogenousInsertFrames(ChildListID aListID, |
michael@0 | 2320 | nsIFrame* aPrevFrame, |
michael@0 | 2321 | nsFrameList& aFrameList) |
michael@0 | 2322 | { |
michael@0 | 2323 | // See what kind of frame we have |
michael@0 | 2324 | const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay(); |
michael@0 | 2325 | #ifdef DEBUG |
michael@0 | 2326 | // Verify that either all siblings have display:table-column-group, or they |
michael@0 | 2327 | // all have display values different from table-column-group. |
michael@0 | 2328 | for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { |
michael@0 | 2329 | const nsStyleDisplay* nextDisplay = e.get()->StyleDisplay(); |
michael@0 | 2330 | MOZ_ASSERT((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) == |
michael@0 | 2331 | (nextDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP), |
michael@0 | 2332 | "heterogenous childlist"); |
michael@0 | 2333 | } |
michael@0 | 2334 | #endif |
michael@0 | 2335 | if (aPrevFrame) { |
michael@0 | 2336 | const nsStyleDisplay* prevDisplay = aPrevFrame->StyleDisplay(); |
michael@0 | 2337 | // Make sure they belong on the same frame list |
michael@0 | 2338 | if ((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) != |
michael@0 | 2339 | (prevDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP)) { |
michael@0 | 2340 | // the previous frame is not valid, see comment at ::AppendFrames |
michael@0 | 2341 | // XXXbz Using content indices here means XBL will get screwed |
michael@0 | 2342 | // over... Oh, well. |
michael@0 | 2343 | nsIFrame* pseudoFrame = aFrameList.FirstChild(); |
michael@0 | 2344 | nsIContent* parentContent = GetContent(); |
michael@0 | 2345 | nsIContent* content; |
michael@0 | 2346 | aPrevFrame = nullptr; |
michael@0 | 2347 | while (pseudoFrame && (parentContent == |
michael@0 | 2348 | (content = pseudoFrame->GetContent()))) { |
michael@0 | 2349 | pseudoFrame = pseudoFrame->GetFirstPrincipalChild(); |
michael@0 | 2350 | } |
michael@0 | 2351 | nsCOMPtr<nsIContent> container = content->GetParent(); |
michael@0 | 2352 | if (MOZ_LIKELY(container)) { // XXX need this null-check, see bug 411823. |
michael@0 | 2353 | int32_t newIndex = container->IndexOf(content); |
michael@0 | 2354 | nsIFrame* kidFrame; |
michael@0 | 2355 | bool isColGroup = (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == |
michael@0 | 2356 | display->mDisplay); |
michael@0 | 2357 | nsTableColGroupFrame* lastColGroup; |
michael@0 | 2358 | if (isColGroup) { |
michael@0 | 2359 | kidFrame = mColGroups.FirstChild(); |
michael@0 | 2360 | lastColGroup = nsTableColGroupFrame::GetLastRealColGroup(this); |
michael@0 | 2361 | } |
michael@0 | 2362 | else { |
michael@0 | 2363 | kidFrame = mFrames.FirstChild(); |
michael@0 | 2364 | } |
michael@0 | 2365 | // Important: need to start at a value smaller than all valid indices |
michael@0 | 2366 | int32_t lastIndex = -1; |
michael@0 | 2367 | while (kidFrame) { |
michael@0 | 2368 | if (isColGroup) { |
michael@0 | 2369 | if (kidFrame == lastColGroup) { |
michael@0 | 2370 | aPrevFrame = kidFrame; // there is no real colgroup after this one |
michael@0 | 2371 | break; |
michael@0 | 2372 | } |
michael@0 | 2373 | } |
michael@0 | 2374 | pseudoFrame = kidFrame; |
michael@0 | 2375 | while (pseudoFrame && (parentContent == |
michael@0 | 2376 | (content = pseudoFrame->GetContent()))) { |
michael@0 | 2377 | pseudoFrame = pseudoFrame->GetFirstPrincipalChild(); |
michael@0 | 2378 | } |
michael@0 | 2379 | int32_t index = container->IndexOf(content); |
michael@0 | 2380 | if (index > lastIndex && index < newIndex) { |
michael@0 | 2381 | lastIndex = index; |
michael@0 | 2382 | aPrevFrame = kidFrame; |
michael@0 | 2383 | } |
michael@0 | 2384 | kidFrame = kidFrame->GetNextSibling(); |
michael@0 | 2385 | } |
michael@0 | 2386 | } |
michael@0 | 2387 | } |
michael@0 | 2388 | } |
michael@0 | 2389 | if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) { |
michael@0 | 2390 | NS_ASSERTION(aListID == kColGroupList, "unexpected child list"); |
michael@0 | 2391 | // Insert the column group frames |
michael@0 | 2392 | const nsFrameList::Slice& newColgroups = |
michael@0 | 2393 | mColGroups.InsertFrames(nullptr, aPrevFrame, aFrameList); |
michael@0 | 2394 | // find the starting col index for the first new col group |
michael@0 | 2395 | int32_t startColIndex = 0; |
michael@0 | 2396 | if (aPrevFrame) { |
michael@0 | 2397 | nsTableColGroupFrame* prevColGroup = |
michael@0 | 2398 | (nsTableColGroupFrame*)GetFrameAtOrBefore(this, aPrevFrame, |
michael@0 | 2399 | nsGkAtoms::tableColGroupFrame); |
michael@0 | 2400 | if (prevColGroup) { |
michael@0 | 2401 | startColIndex = prevColGroup->GetStartColumnIndex() + prevColGroup->GetColCount(); |
michael@0 | 2402 | } |
michael@0 | 2403 | } |
michael@0 | 2404 | InsertColGroups(startColIndex, newColgroups); |
michael@0 | 2405 | } else if (IsRowGroup(display->mDisplay)) { |
michael@0 | 2406 | NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
michael@0 | 2407 | // Insert the frames in the sibling chain |
michael@0 | 2408 | const nsFrameList::Slice& newRowGroups = |
michael@0 | 2409 | mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList); |
michael@0 | 2410 | |
michael@0 | 2411 | InsertRowGroups(newRowGroups); |
michael@0 | 2412 | } else { |
michael@0 | 2413 | NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
michael@0 | 2414 | NS_NOTREACHED("How did we even get here?"); |
michael@0 | 2415 | // Just insert the frame and don't worry about reflowing it |
michael@0 | 2416 | mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList); |
michael@0 | 2417 | return; |
michael@0 | 2418 | } |
michael@0 | 2419 | |
michael@0 | 2420 | PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
michael@0 | 2421 | NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 2422 | SetGeometryDirty(); |
michael@0 | 2423 | #ifdef DEBUG_TABLE_CELLMAP |
michael@0 | 2424 | printf("=== TableFrame::InsertFrames\n"); |
michael@0 | 2425 | Dump(true, true, true); |
michael@0 | 2426 | #endif |
michael@0 | 2427 | return; |
michael@0 | 2428 | } |
michael@0 | 2429 | |
michael@0 | 2430 | void |
michael@0 | 2431 | nsTableFrame::DoRemoveFrame(ChildListID aListID, |
michael@0 | 2432 | nsIFrame* aOldFrame) |
michael@0 | 2433 | { |
michael@0 | 2434 | if (aListID == kColGroupList) { |
michael@0 | 2435 | nsIFrame* nextColGroupFrame = aOldFrame->GetNextSibling(); |
michael@0 | 2436 | nsTableColGroupFrame* colGroup = (nsTableColGroupFrame*)aOldFrame; |
michael@0 | 2437 | int32_t firstColIndex = colGroup->GetStartColumnIndex(); |
michael@0 | 2438 | int32_t lastColIndex = firstColIndex + colGroup->GetColCount() - 1; |
michael@0 | 2439 | mColGroups.DestroyFrame(aOldFrame); |
michael@0 | 2440 | nsTableColGroupFrame::ResetColIndices(nextColGroupFrame, firstColIndex); |
michael@0 | 2441 | // remove the cols from the table |
michael@0 | 2442 | int32_t colX; |
michael@0 | 2443 | for (colX = lastColIndex; colX >= firstColIndex; colX--) { |
michael@0 | 2444 | nsTableColFrame* colFrame = mColFrames.SafeElementAt(colX); |
michael@0 | 2445 | if (colFrame) { |
michael@0 | 2446 | RemoveCol(colGroup, colX, true, false); |
michael@0 | 2447 | } |
michael@0 | 2448 | } |
michael@0 | 2449 | |
michael@0 | 2450 | int32_t numAnonymousColsToAdd = GetColCount() - mColFrames.Length(); |
michael@0 | 2451 | if (numAnonymousColsToAdd > 0) { |
michael@0 | 2452 | // this sets the child list, updates the col cache and cell map |
michael@0 | 2453 | AppendAnonymousColFrames(numAnonymousColsToAdd); |
michael@0 | 2454 | } |
michael@0 | 2455 | |
michael@0 | 2456 | } else { |
michael@0 | 2457 | NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
michael@0 | 2458 | nsTableRowGroupFrame* rgFrame = |
michael@0 | 2459 | static_cast<nsTableRowGroupFrame*>(aOldFrame); |
michael@0 | 2460 | // remove the row group from the cell map |
michael@0 | 2461 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 2462 | if (cellMap) { |
michael@0 | 2463 | cellMap->RemoveGroupCellMap(rgFrame); |
michael@0 | 2464 | } |
michael@0 | 2465 | |
michael@0 | 2466 | // remove the row group frame from the sibling chain |
michael@0 | 2467 | mFrames.DestroyFrame(aOldFrame); |
michael@0 | 2468 | |
michael@0 | 2469 | // the removal of a row group changes the cellmap, the columns might change |
michael@0 | 2470 | if (cellMap) { |
michael@0 | 2471 | cellMap->Synchronize(this); |
michael@0 | 2472 | // Create an empty slice |
michael@0 | 2473 | ResetRowIndices(nsFrameList::Slice(mFrames, nullptr, nullptr)); |
michael@0 | 2474 | nsIntRect damageArea; |
michael@0 | 2475 | cellMap->RebuildConsideringCells(nullptr, nullptr, 0, 0, false, damageArea); |
michael@0 | 2476 | |
michael@0 | 2477 | static_cast<nsTableFrame*>(FirstInFlow())->MatchCellMapToColCache(cellMap); |
michael@0 | 2478 | } |
michael@0 | 2479 | } |
michael@0 | 2480 | } |
michael@0 | 2481 | |
michael@0 | 2482 | nsresult |
michael@0 | 2483 | nsTableFrame::RemoveFrame(ChildListID aListID, |
michael@0 | 2484 | nsIFrame* aOldFrame) |
michael@0 | 2485 | { |
michael@0 | 2486 | NS_ASSERTION(aListID == kColGroupList || |
michael@0 | 2487 | NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP != |
michael@0 | 2488 | aOldFrame->StyleDisplay()->mDisplay, |
michael@0 | 2489 | "Wrong list name; use kColGroupList iff colgroup"); |
michael@0 | 2490 | nsIPresShell* shell = PresContext()->PresShell(); |
michael@0 | 2491 | nsTableFrame* lastParent = nullptr; |
michael@0 | 2492 | while (aOldFrame) { |
michael@0 | 2493 | nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation(); |
michael@0 | 2494 | nsTableFrame* parent = static_cast<nsTableFrame*>(aOldFrame->GetParent()); |
michael@0 | 2495 | if (parent != lastParent) { |
michael@0 | 2496 | parent->DrainSelfOverflowList(); |
michael@0 | 2497 | } |
michael@0 | 2498 | parent->DoRemoveFrame(aListID, aOldFrame); |
michael@0 | 2499 | aOldFrame = oldFrameNextContinuation; |
michael@0 | 2500 | if (parent != lastParent) { |
michael@0 | 2501 | // for now, just bail and recalc all of the collapsing borders |
michael@0 | 2502 | // as the cellmap changes we need to recalc |
michael@0 | 2503 | if (parent->IsBorderCollapse()) { |
michael@0 | 2504 | parent->SetFullBCDamageArea(); |
michael@0 | 2505 | } |
michael@0 | 2506 | parent->SetGeometryDirty(); |
michael@0 | 2507 | shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange, |
michael@0 | 2508 | NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 2509 | lastParent = parent; |
michael@0 | 2510 | } |
michael@0 | 2511 | } |
michael@0 | 2512 | #ifdef DEBUG_TABLE_CELLMAP |
michael@0 | 2513 | printf("=== TableFrame::RemoveFrame\n"); |
michael@0 | 2514 | Dump(true, true, true); |
michael@0 | 2515 | #endif |
michael@0 | 2516 | return NS_OK; |
michael@0 | 2517 | } |
michael@0 | 2518 | |
michael@0 | 2519 | /* virtual */ nsMargin |
michael@0 | 2520 | nsTableFrame::GetUsedBorder() const |
michael@0 | 2521 | { |
michael@0 | 2522 | if (!IsBorderCollapse()) |
michael@0 | 2523 | return nsContainerFrame::GetUsedBorder(); |
michael@0 | 2524 | |
michael@0 | 2525 | return GetIncludedOuterBCBorder(); |
michael@0 | 2526 | } |
michael@0 | 2527 | |
michael@0 | 2528 | /* virtual */ nsMargin |
michael@0 | 2529 | nsTableFrame::GetUsedPadding() const |
michael@0 | 2530 | { |
michael@0 | 2531 | if (!IsBorderCollapse()) |
michael@0 | 2532 | return nsContainerFrame::GetUsedPadding(); |
michael@0 | 2533 | |
michael@0 | 2534 | return nsMargin(0,0,0,0); |
michael@0 | 2535 | } |
michael@0 | 2536 | |
michael@0 | 2537 | /* virtual */ nsMargin |
michael@0 | 2538 | nsTableFrame::GetUsedMargin() const |
michael@0 | 2539 | { |
michael@0 | 2540 | // The margin is inherited to the outer table frame via |
michael@0 | 2541 | // the ::-moz-table-outer rule in ua.css. |
michael@0 | 2542 | return nsMargin(0, 0, 0, 0); |
michael@0 | 2543 | } |
michael@0 | 2544 | |
michael@0 | 2545 | // Destructor function for BCPropertyData properties |
michael@0 | 2546 | static void |
michael@0 | 2547 | DestroyBCProperty(void* aPropertyValue) |
michael@0 | 2548 | { |
michael@0 | 2549 | delete static_cast<BCPropertyData*>(aPropertyValue); |
michael@0 | 2550 | } |
michael@0 | 2551 | |
michael@0 | 2552 | NS_DECLARE_FRAME_PROPERTY(TableBCProperty, DestroyBCProperty) |
michael@0 | 2553 | |
michael@0 | 2554 | BCPropertyData* |
michael@0 | 2555 | nsTableFrame::GetBCProperty(bool aCreateIfNecessary) const |
michael@0 | 2556 | { |
michael@0 | 2557 | FrameProperties props = Properties(); |
michael@0 | 2558 | BCPropertyData* value = static_cast<BCPropertyData*> |
michael@0 | 2559 | (props.Get(TableBCProperty())); |
michael@0 | 2560 | if (!value && aCreateIfNecessary) { |
michael@0 | 2561 | value = new BCPropertyData(); |
michael@0 | 2562 | props.Set(TableBCProperty(), value); |
michael@0 | 2563 | } |
michael@0 | 2564 | |
michael@0 | 2565 | return value; |
michael@0 | 2566 | } |
michael@0 | 2567 | |
michael@0 | 2568 | static void |
michael@0 | 2569 | DivideBCBorderSize(BCPixelSize aPixelSize, |
michael@0 | 2570 | BCPixelSize& aSmallHalf, |
michael@0 | 2571 | BCPixelSize& aLargeHalf) |
michael@0 | 2572 | { |
michael@0 | 2573 | aSmallHalf = aPixelSize / 2; |
michael@0 | 2574 | aLargeHalf = aPixelSize - aSmallHalf; |
michael@0 | 2575 | } |
michael@0 | 2576 | |
michael@0 | 2577 | nsMargin |
michael@0 | 2578 | nsTableFrame::GetOuterBCBorder() const |
michael@0 | 2579 | { |
michael@0 | 2580 | if (NeedToCalcBCBorders()) |
michael@0 | 2581 | const_cast<nsTableFrame*>(this)->CalcBCBorders(); |
michael@0 | 2582 | |
michael@0 | 2583 | nsMargin border(0, 0, 0, 0); |
michael@0 | 2584 | int32_t p2t = nsPresContext::AppUnitsPerCSSPixel(); |
michael@0 | 2585 | BCPropertyData* propData = GetBCProperty(); |
michael@0 | 2586 | if (propData) { |
michael@0 | 2587 | border.top = BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth); |
michael@0 | 2588 | border.right = BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightBorderWidth); |
michael@0 | 2589 | border.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth); |
michael@0 | 2590 | border.left = BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftBorderWidth); |
michael@0 | 2591 | } |
michael@0 | 2592 | return border; |
michael@0 | 2593 | } |
michael@0 | 2594 | |
michael@0 | 2595 | nsMargin |
michael@0 | 2596 | nsTableFrame::GetIncludedOuterBCBorder() const |
michael@0 | 2597 | { |
michael@0 | 2598 | if (NeedToCalcBCBorders()) |
michael@0 | 2599 | const_cast<nsTableFrame*>(this)->CalcBCBorders(); |
michael@0 | 2600 | |
michael@0 | 2601 | nsMargin border(0, 0, 0, 0); |
michael@0 | 2602 | int32_t p2t = nsPresContext::AppUnitsPerCSSPixel(); |
michael@0 | 2603 | BCPropertyData* propData = GetBCProperty(); |
michael@0 | 2604 | if (propData) { |
michael@0 | 2605 | border.top += BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth); |
michael@0 | 2606 | border.right += BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightCellBorderWidth); |
michael@0 | 2607 | border.bottom += BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth); |
michael@0 | 2608 | border.left += BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftCellBorderWidth); |
michael@0 | 2609 | } |
michael@0 | 2610 | return border; |
michael@0 | 2611 | } |
michael@0 | 2612 | |
michael@0 | 2613 | nsMargin |
michael@0 | 2614 | nsTableFrame::GetExcludedOuterBCBorder() const |
michael@0 | 2615 | { |
michael@0 | 2616 | return GetOuterBCBorder() - GetIncludedOuterBCBorder(); |
michael@0 | 2617 | } |
michael@0 | 2618 | |
michael@0 | 2619 | static |
michael@0 | 2620 | void GetSeparateModelBorderPadding(const nsHTMLReflowState* aReflowState, |
michael@0 | 2621 | nsStyleContext& aStyleContext, |
michael@0 | 2622 | nsMargin& aBorderPadding) |
michael@0 | 2623 | { |
michael@0 | 2624 | // XXXbz Either we _do_ have a reflow state and then we can use its |
michael@0 | 2625 | // mComputedBorderPadding or we don't and then we get the padding |
michael@0 | 2626 | // wrong! |
michael@0 | 2627 | const nsStyleBorder* border = aStyleContext.StyleBorder(); |
michael@0 | 2628 | aBorderPadding = border->GetComputedBorder(); |
michael@0 | 2629 | if (aReflowState) { |
michael@0 | 2630 | aBorderPadding += aReflowState->ComputedPhysicalPadding(); |
michael@0 | 2631 | } |
michael@0 | 2632 | } |
michael@0 | 2633 | |
michael@0 | 2634 | nsMargin |
michael@0 | 2635 | nsTableFrame::GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const |
michael@0 | 2636 | { |
michael@0 | 2637 | nsMargin offset(0,0,0,0); |
michael@0 | 2638 | if (IsBorderCollapse()) { |
michael@0 | 2639 | offset = GetIncludedOuterBCBorder(); |
michael@0 | 2640 | } |
michael@0 | 2641 | else { |
michael@0 | 2642 | GetSeparateModelBorderPadding(aReflowState, *mStyleContext, offset); |
michael@0 | 2643 | } |
michael@0 | 2644 | return offset; |
michael@0 | 2645 | } |
michael@0 | 2646 | |
michael@0 | 2647 | void |
michael@0 | 2648 | nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState) |
michael@0 | 2649 | { |
michael@0 | 2650 | nsMargin collapseBorder; |
michael@0 | 2651 | nsMargin padding(0,0,0,0); |
michael@0 | 2652 | nsMargin* pCollapseBorder = nullptr; |
michael@0 | 2653 | nsPresContext* presContext = PresContext(); |
michael@0 | 2654 | if (IsBorderCollapse()) { |
michael@0 | 2655 | nsTableRowGroupFrame* rgFrame = |
michael@0 | 2656 | static_cast<nsTableRowGroupFrame*>(aReflowState.frame); |
michael@0 | 2657 | pCollapseBorder = rgFrame->GetBCBorderWidth(collapseBorder); |
michael@0 | 2658 | } |
michael@0 | 2659 | aReflowState.Init(presContext, -1, -1, pCollapseBorder, &padding); |
michael@0 | 2660 | |
michael@0 | 2661 | NS_ASSERTION(!mBits.mResizedColumns || |
michael@0 | 2662 | !aReflowState.parentReflowState->mFlags.mSpecialHeightReflow, |
michael@0 | 2663 | "should not resize columns on special height reflow"); |
michael@0 | 2664 | if (mBits.mResizedColumns) { |
michael@0 | 2665 | aReflowState.mFlags.mHResize = true; |
michael@0 | 2666 | } |
michael@0 | 2667 | } |
michael@0 | 2668 | |
michael@0 | 2669 | // Position and size aKidFrame and update our reflow state. The origin of |
michael@0 | 2670 | // aKidRect is relative to the upper-left origin of our frame |
michael@0 | 2671 | void nsTableFrame::PlaceChild(nsTableReflowState& aReflowState, |
michael@0 | 2672 | nsIFrame* aKidFrame, |
michael@0 | 2673 | nsHTMLReflowMetrics& aKidDesiredSize, |
michael@0 | 2674 | const nsRect& aOriginalKidRect, |
michael@0 | 2675 | const nsRect& aOriginalKidVisualOverflow) |
michael@0 | 2676 | { |
michael@0 | 2677 | bool isFirstReflow = |
michael@0 | 2678 | (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; |
michael@0 | 2679 | |
michael@0 | 2680 | // Place and size the child |
michael@0 | 2681 | FinishReflowChild(aKidFrame, PresContext(), aKidDesiredSize, nullptr, |
michael@0 | 2682 | aReflowState.x, aReflowState.y, 0); |
michael@0 | 2683 | |
michael@0 | 2684 | InvalidateTableFrame(aKidFrame, aOriginalKidRect, aOriginalKidVisualOverflow, |
michael@0 | 2685 | isFirstReflow); |
michael@0 | 2686 | |
michael@0 | 2687 | // Adjust the running y-offset |
michael@0 | 2688 | aReflowState.y += aKidDesiredSize.Height(); |
michael@0 | 2689 | |
michael@0 | 2690 | // If our height is constrained, then update the available height |
michael@0 | 2691 | if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { |
michael@0 | 2692 | aReflowState.availSize.height -= aKidDesiredSize.Height(); |
michael@0 | 2693 | } |
michael@0 | 2694 | } |
michael@0 | 2695 | |
michael@0 | 2696 | void |
michael@0 | 2697 | nsTableFrame::OrderRowGroups(RowGroupArray& aChildren, |
michael@0 | 2698 | nsTableRowGroupFrame** aHead, |
michael@0 | 2699 | nsTableRowGroupFrame** aFoot) const |
michael@0 | 2700 | { |
michael@0 | 2701 | aChildren.Clear(); |
michael@0 | 2702 | nsTableRowGroupFrame* head = nullptr; |
michael@0 | 2703 | nsTableRowGroupFrame* foot = nullptr; |
michael@0 | 2704 | |
michael@0 | 2705 | nsIFrame* kidFrame = mFrames.FirstChild(); |
michael@0 | 2706 | while (kidFrame) { |
michael@0 | 2707 | const nsStyleDisplay* kidDisplay = kidFrame->StyleDisplay(); |
michael@0 | 2708 | nsTableRowGroupFrame* rowGroup = |
michael@0 | 2709 | static_cast<nsTableRowGroupFrame*>(kidFrame); |
michael@0 | 2710 | |
michael@0 | 2711 | switch (kidDisplay->mDisplay) { |
michael@0 | 2712 | case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP: |
michael@0 | 2713 | if (head) { // treat additional thead like tbody |
michael@0 | 2714 | aChildren.AppendElement(rowGroup); |
michael@0 | 2715 | } |
michael@0 | 2716 | else { |
michael@0 | 2717 | head = rowGroup; |
michael@0 | 2718 | } |
michael@0 | 2719 | break; |
michael@0 | 2720 | case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP: |
michael@0 | 2721 | if (foot) { // treat additional tfoot like tbody |
michael@0 | 2722 | aChildren.AppendElement(rowGroup); |
michael@0 | 2723 | } |
michael@0 | 2724 | else { |
michael@0 | 2725 | foot = rowGroup; |
michael@0 | 2726 | } |
michael@0 | 2727 | break; |
michael@0 | 2728 | case NS_STYLE_DISPLAY_TABLE_ROW_GROUP: |
michael@0 | 2729 | aChildren.AppendElement(rowGroup); |
michael@0 | 2730 | break; |
michael@0 | 2731 | default: |
michael@0 | 2732 | NS_NOTREACHED("How did this produce an nsTableRowGroupFrame?"); |
michael@0 | 2733 | // Just ignore it |
michael@0 | 2734 | break; |
michael@0 | 2735 | } |
michael@0 | 2736 | // Get the next sibling but skip it if it's also the next-in-flow, since |
michael@0 | 2737 | // a next-in-flow will not be part of the current table. |
michael@0 | 2738 | while (kidFrame) { |
michael@0 | 2739 | nsIFrame* nif = kidFrame->GetNextInFlow(); |
michael@0 | 2740 | kidFrame = kidFrame->GetNextSibling(); |
michael@0 | 2741 | if (kidFrame != nif) |
michael@0 | 2742 | break; |
michael@0 | 2743 | } |
michael@0 | 2744 | } |
michael@0 | 2745 | |
michael@0 | 2746 | // put the thead first |
michael@0 | 2747 | if (head) { |
michael@0 | 2748 | aChildren.InsertElementAt(0, head); |
michael@0 | 2749 | } |
michael@0 | 2750 | if (aHead) |
michael@0 | 2751 | *aHead = head; |
michael@0 | 2752 | // put the tfoot after the last tbody |
michael@0 | 2753 | if (foot) { |
michael@0 | 2754 | aChildren.AppendElement(foot); |
michael@0 | 2755 | } |
michael@0 | 2756 | if (aFoot) |
michael@0 | 2757 | *aFoot = foot; |
michael@0 | 2758 | } |
michael@0 | 2759 | |
michael@0 | 2760 | nsTableRowGroupFrame* |
michael@0 | 2761 | nsTableFrame::GetTHead() const |
michael@0 | 2762 | { |
michael@0 | 2763 | nsIFrame* kidFrame = mFrames.FirstChild(); |
michael@0 | 2764 | while (kidFrame) { |
michael@0 | 2765 | if (kidFrame->StyleDisplay()->mDisplay == |
michael@0 | 2766 | NS_STYLE_DISPLAY_TABLE_HEADER_GROUP) { |
michael@0 | 2767 | return static_cast<nsTableRowGroupFrame*>(kidFrame); |
michael@0 | 2768 | } |
michael@0 | 2769 | |
michael@0 | 2770 | // Get the next sibling but skip it if it's also the next-in-flow, since |
michael@0 | 2771 | // a next-in-flow will not be part of the current table. |
michael@0 | 2772 | while (kidFrame) { |
michael@0 | 2773 | nsIFrame* nif = kidFrame->GetNextInFlow(); |
michael@0 | 2774 | kidFrame = kidFrame->GetNextSibling(); |
michael@0 | 2775 | if (kidFrame != nif) |
michael@0 | 2776 | break; |
michael@0 | 2777 | } |
michael@0 | 2778 | } |
michael@0 | 2779 | |
michael@0 | 2780 | return nullptr; |
michael@0 | 2781 | } |
michael@0 | 2782 | |
michael@0 | 2783 | nsTableRowGroupFrame* |
michael@0 | 2784 | nsTableFrame::GetTFoot() const |
michael@0 | 2785 | { |
michael@0 | 2786 | nsIFrame* kidFrame = mFrames.FirstChild(); |
michael@0 | 2787 | while (kidFrame) { |
michael@0 | 2788 | if (kidFrame->StyleDisplay()->mDisplay == |
michael@0 | 2789 | NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP) { |
michael@0 | 2790 | return static_cast<nsTableRowGroupFrame*>(kidFrame); |
michael@0 | 2791 | } |
michael@0 | 2792 | |
michael@0 | 2793 | // Get the next sibling but skip it if it's also the next-in-flow, since |
michael@0 | 2794 | // a next-in-flow will not be part of the current table. |
michael@0 | 2795 | while (kidFrame) { |
michael@0 | 2796 | nsIFrame* nif = kidFrame->GetNextInFlow(); |
michael@0 | 2797 | kidFrame = kidFrame->GetNextSibling(); |
michael@0 | 2798 | if (kidFrame != nif) |
michael@0 | 2799 | break; |
michael@0 | 2800 | } |
michael@0 | 2801 | } |
michael@0 | 2802 | |
michael@0 | 2803 | return nullptr; |
michael@0 | 2804 | } |
michael@0 | 2805 | |
michael@0 | 2806 | static bool |
michael@0 | 2807 | IsRepeatable(nscoord aFrameHeight, nscoord aPageHeight) |
michael@0 | 2808 | { |
michael@0 | 2809 | return aFrameHeight < (aPageHeight / 4); |
michael@0 | 2810 | } |
michael@0 | 2811 | |
michael@0 | 2812 | nsresult |
michael@0 | 2813 | nsTableFrame::SetupHeaderFooterChild(const nsTableReflowState& aReflowState, |
michael@0 | 2814 | nsTableRowGroupFrame* aFrame, |
michael@0 | 2815 | nscoord* aDesiredHeight) |
michael@0 | 2816 | { |
michael@0 | 2817 | nsPresContext* presContext = PresContext(); |
michael@0 | 2818 | nscoord pageHeight = presContext->GetPageSize().height; |
michael@0 | 2819 | |
michael@0 | 2820 | // Reflow the child with unconstrainted height |
michael@0 | 2821 | nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState, |
michael@0 | 2822 | aFrame, |
michael@0 | 2823 | nsSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE), |
michael@0 | 2824 | -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); |
michael@0 | 2825 | InitChildReflowState(kidReflowState); |
michael@0 | 2826 | kidReflowState.mFlags.mIsTopOfPage = true; |
michael@0 | 2827 | nsHTMLReflowMetrics desiredSize(aReflowState.reflowState); |
michael@0 | 2828 | desiredSize.Width() = desiredSize.Height() = 0; |
michael@0 | 2829 | nsReflowStatus status; |
michael@0 | 2830 | nsresult rv = ReflowChild(aFrame, presContext, desiredSize, kidReflowState, |
michael@0 | 2831 | aReflowState.x, aReflowState.y, 0, status); |
michael@0 | 2832 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2833 | // The child will be reflowed again "for real" so no need to place it now |
michael@0 | 2834 | |
michael@0 | 2835 | aFrame->SetRepeatable(IsRepeatable(desiredSize.Height(), pageHeight)); |
michael@0 | 2836 | *aDesiredHeight = desiredSize.Height(); |
michael@0 | 2837 | return NS_OK; |
michael@0 | 2838 | } |
michael@0 | 2839 | |
michael@0 | 2840 | void |
michael@0 | 2841 | nsTableFrame::PlaceRepeatedFooter(nsTableReflowState& aReflowState, |
michael@0 | 2842 | nsTableRowGroupFrame *aTfoot, |
michael@0 | 2843 | nscoord aFooterHeight) |
michael@0 | 2844 | { |
michael@0 | 2845 | nsPresContext* presContext = PresContext(); |
michael@0 | 2846 | nsSize kidAvailSize(aReflowState.availSize); |
michael@0 | 2847 | kidAvailSize.height = aFooterHeight; |
michael@0 | 2848 | nsHTMLReflowState footerReflowState(presContext, |
michael@0 | 2849 | aReflowState.reflowState, |
michael@0 | 2850 | aTfoot, kidAvailSize, |
michael@0 | 2851 | -1, -1, |
michael@0 | 2852 | nsHTMLReflowState::CALLER_WILL_INIT); |
michael@0 | 2853 | InitChildReflowState(footerReflowState); |
michael@0 | 2854 | aReflowState.y += GetCellSpacingY(); |
michael@0 | 2855 | |
michael@0 | 2856 | nsRect origTfootRect = aTfoot->GetRect(); |
michael@0 | 2857 | nsRect origTfootVisualOverflow = aTfoot->GetVisualOverflowRect(); |
michael@0 | 2858 | |
michael@0 | 2859 | nsReflowStatus footerStatus; |
michael@0 | 2860 | nsHTMLReflowMetrics desiredSize(aReflowState.reflowState); |
michael@0 | 2861 | desiredSize.Width() = desiredSize.Height() = 0; |
michael@0 | 2862 | ReflowChild(aTfoot, presContext, desiredSize, footerReflowState, |
michael@0 | 2863 | aReflowState.x, aReflowState.y, 0, footerStatus); |
michael@0 | 2864 | PlaceChild(aReflowState, aTfoot, desiredSize, origTfootRect, |
michael@0 | 2865 | origTfootVisualOverflow); |
michael@0 | 2866 | } |
michael@0 | 2867 | |
michael@0 | 2868 | // Reflow the children based on the avail size and reason in aReflowState |
michael@0 | 2869 | // update aReflowMetrics a aStatus |
michael@0 | 2870 | nsresult |
michael@0 | 2871 | nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, |
michael@0 | 2872 | nsReflowStatus& aStatus, |
michael@0 | 2873 | nsIFrame*& aLastChildReflowed, |
michael@0 | 2874 | nsOverflowAreas& aOverflowAreas) |
michael@0 | 2875 | { |
michael@0 | 2876 | aStatus = NS_FRAME_COMPLETE; |
michael@0 | 2877 | aLastChildReflowed = nullptr; |
michael@0 | 2878 | |
michael@0 | 2879 | nsIFrame* prevKidFrame = nullptr; |
michael@0 | 2880 | nsresult rv = NS_OK; |
michael@0 | 2881 | nscoord cellSpacingY = GetCellSpacingY(); |
michael@0 | 2882 | |
michael@0 | 2883 | nsPresContext* presContext = PresContext(); |
michael@0 | 2884 | // XXXldb Should we be checking constrained height instead? |
michael@0 | 2885 | // tables are not able to pull back children from its next inflow, so even |
michael@0 | 2886 | // under paginated contexts tables are should not paginate if they are inside |
michael@0 | 2887 | // column set |
michael@0 | 2888 | bool isPaginated = presContext->IsPaginated() && |
michael@0 | 2889 | NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height && |
michael@0 | 2890 | aReflowState.reflowState.mFlags.mTableIsSplittable; |
michael@0 | 2891 | |
michael@0 | 2892 | aOverflowAreas.Clear(); |
michael@0 | 2893 | |
michael@0 | 2894 | bool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() || |
michael@0 | 2895 | mBits.mResizedColumns || |
michael@0 | 2896 | IsGeometryDirty(); |
michael@0 | 2897 | |
michael@0 | 2898 | RowGroupArray rowGroups; |
michael@0 | 2899 | nsTableRowGroupFrame *thead, *tfoot; |
michael@0 | 2900 | OrderRowGroups(rowGroups, &thead, &tfoot); |
michael@0 | 2901 | bool pageBreak = false; |
michael@0 | 2902 | nscoord footerHeight = 0; |
michael@0 | 2903 | |
michael@0 | 2904 | // Determine the repeatablility of headers and footers, and also the desired |
michael@0 | 2905 | // height of any repeatable footer. |
michael@0 | 2906 | // The repeatability of headers on continued tables is handled |
michael@0 | 2907 | // when they are created in nsCSSFrameConstructor::CreateContinuingTableFrame. |
michael@0 | 2908 | // We handle the repeatability of footers again here because we need to |
michael@0 | 2909 | // determine the footer's height anyway. We could perhaps optimize by |
michael@0 | 2910 | // using the footer's prev-in-flow's height instead of reflowing it again, |
michael@0 | 2911 | // but there's no real need. |
michael@0 | 2912 | if (isPaginated) { |
michael@0 | 2913 | if (thead && !GetPrevInFlow()) { |
michael@0 | 2914 | nscoord desiredHeight; |
michael@0 | 2915 | rv = SetupHeaderFooterChild(aReflowState, thead, &desiredHeight); |
michael@0 | 2916 | if (NS_FAILED(rv)) |
michael@0 | 2917 | return rv; |
michael@0 | 2918 | } |
michael@0 | 2919 | if (tfoot) { |
michael@0 | 2920 | rv = SetupHeaderFooterChild(aReflowState, tfoot, &footerHeight); |
michael@0 | 2921 | if (NS_FAILED(rv)) |
michael@0 | 2922 | return rv; |
michael@0 | 2923 | } |
michael@0 | 2924 | } |
michael@0 | 2925 | // if the child is a tbody in paginated mode reduce the height by a repeated footer |
michael@0 | 2926 | bool allowRepeatedFooter = false; |
michael@0 | 2927 | for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) { |
michael@0 | 2928 | nsIFrame* kidFrame = rowGroups[childX]; |
michael@0 | 2929 | // Get the frame state bits |
michael@0 | 2930 | // See if we should only reflow the dirty child frames |
michael@0 | 2931 | if (reflowAllKids || |
michael@0 | 2932 | NS_SUBTREE_DIRTY(kidFrame) || |
michael@0 | 2933 | (aReflowState.reflowState.mFlags.mSpecialHeightReflow && |
michael@0 | 2934 | (isPaginated || (kidFrame->GetStateBits() & |
michael@0 | 2935 | NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) { |
michael@0 | 2936 | if (pageBreak) { |
michael@0 | 2937 | if (allowRepeatedFooter) { |
michael@0 | 2938 | PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); |
michael@0 | 2939 | } |
michael@0 | 2940 | else if (tfoot && tfoot->IsRepeatable()) { |
michael@0 | 2941 | tfoot->SetRepeatable(false); |
michael@0 | 2942 | } |
michael@0 | 2943 | PushChildren(rowGroups, childX); |
michael@0 | 2944 | aStatus = NS_FRAME_NOT_COMPLETE; |
michael@0 | 2945 | break; |
michael@0 | 2946 | } |
michael@0 | 2947 | |
michael@0 | 2948 | nsSize kidAvailSize(aReflowState.availSize); |
michael@0 | 2949 | allowRepeatedFooter = false; |
michael@0 | 2950 | if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.height)) { |
michael@0 | 2951 | nsTableRowGroupFrame* kidRG = |
michael@0 | 2952 | static_cast<nsTableRowGroupFrame*>(kidFrame); |
michael@0 | 2953 | if (kidRG != thead && kidRG != tfoot && tfoot && tfoot->IsRepeatable()) { |
michael@0 | 2954 | // the child is a tbody and there is a repeatable footer |
michael@0 | 2955 | NS_ASSERTION(tfoot == rowGroups[rowGroups.Length() - 1], "Missing footer!"); |
michael@0 | 2956 | if (footerHeight + cellSpacingY < kidAvailSize.height) { |
michael@0 | 2957 | allowRepeatedFooter = true; |
michael@0 | 2958 | kidAvailSize.height -= footerHeight + cellSpacingY; |
michael@0 | 2959 | } |
michael@0 | 2960 | } |
michael@0 | 2961 | } |
michael@0 | 2962 | |
michael@0 | 2963 | nsRect oldKidRect = kidFrame->GetRect(); |
michael@0 | 2964 | nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect(); |
michael@0 | 2965 | |
michael@0 | 2966 | nsHTMLReflowMetrics desiredSize(aReflowState.reflowState); |
michael@0 | 2967 | desiredSize.Width() = desiredSize.Height() = 0; |
michael@0 | 2968 | |
michael@0 | 2969 | // Reflow the child into the available space |
michael@0 | 2970 | nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState, |
michael@0 | 2971 | kidFrame, kidAvailSize, |
michael@0 | 2972 | -1, -1, |
michael@0 | 2973 | nsHTMLReflowState::CALLER_WILL_INIT); |
michael@0 | 2974 | InitChildReflowState(kidReflowState); |
michael@0 | 2975 | |
michael@0 | 2976 | // If this isn't the first row group, and the previous row group has a |
michael@0 | 2977 | // nonzero YMost, then we can't be at the top of the page. |
michael@0 | 2978 | // We ignore a repeated head row group in this check to avoid causing |
michael@0 | 2979 | // infinite loops in some circumstances - see bug 344883. |
michael@0 | 2980 | if (childX > ((thead && IsRepeatedFrame(thead)) ? 1u : 0u) && |
michael@0 | 2981 | (rowGroups[childX - 1]->GetRect().YMost() > 0)) { |
michael@0 | 2982 | kidReflowState.mFlags.mIsTopOfPage = false; |
michael@0 | 2983 | } |
michael@0 | 2984 | aReflowState.y += cellSpacingY; |
michael@0 | 2985 | if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { |
michael@0 | 2986 | aReflowState.availSize.height -= cellSpacingY; |
michael@0 | 2987 | } |
michael@0 | 2988 | // record the presence of a next in flow, it might get destroyed so we |
michael@0 | 2989 | // need to reorder the row group array |
michael@0 | 2990 | bool reorder = false; |
michael@0 | 2991 | if (kidFrame->GetNextInFlow()) |
michael@0 | 2992 | reorder = true; |
michael@0 | 2993 | |
michael@0 | 2994 | rv = ReflowChild(kidFrame, presContext, desiredSize, kidReflowState, |
michael@0 | 2995 | aReflowState.x, aReflowState.y, 0, aStatus); |
michael@0 | 2996 | |
michael@0 | 2997 | if (reorder) { |
michael@0 | 2998 | // reorder row groups the reflow may have changed the nextinflows |
michael@0 | 2999 | OrderRowGroups(rowGroups, &thead, &tfoot); |
michael@0 | 3000 | childX = rowGroups.IndexOf(kidFrame); |
michael@0 | 3001 | if (childX == RowGroupArray::NoIndex) { |
michael@0 | 3002 | // XXXbz can this happen? |
michael@0 | 3003 | childX = rowGroups.Length(); |
michael@0 | 3004 | } |
michael@0 | 3005 | } |
michael@0 | 3006 | if (isPaginated && !NS_FRAME_IS_FULLY_COMPLETE(aStatus) && |
michael@0 | 3007 | ShouldAvoidBreakInside(aReflowState.reflowState)) { |
michael@0 | 3008 | aStatus = NS_INLINE_LINE_BREAK_BEFORE(); |
michael@0 | 3009 | break; |
michael@0 | 3010 | } |
michael@0 | 3011 | // see if the rowgroup did not fit on this page might be pushed on |
michael@0 | 3012 | // the next page |
michael@0 | 3013 | if (isPaginated && |
michael@0 | 3014 | (NS_INLINE_IS_BREAK_BEFORE(aStatus) || |
michael@0 | 3015 | (NS_FRAME_IS_COMPLETE(aStatus) && |
michael@0 | 3016 | (NS_UNCONSTRAINEDSIZE != kidReflowState.AvailableHeight()) && |
michael@0 | 3017 | kidReflowState.AvailableHeight() < desiredSize.Height()))) { |
michael@0 | 3018 | if (ShouldAvoidBreakInside(aReflowState.reflowState)) { |
michael@0 | 3019 | aStatus = NS_INLINE_LINE_BREAK_BEFORE(); |
michael@0 | 3020 | break; |
michael@0 | 3021 | } |
michael@0 | 3022 | // if we are on top of the page place with dataloss |
michael@0 | 3023 | if (kidReflowState.mFlags.mIsTopOfPage) { |
michael@0 | 3024 | if (childX+1 < rowGroups.Length()) { |
michael@0 | 3025 | nsIFrame* nextRowGroupFrame = rowGroups[childX + 1]; |
michael@0 | 3026 | if (nextRowGroupFrame) { |
michael@0 | 3027 | PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect, |
michael@0 | 3028 | oldKidVisualOverflow); |
michael@0 | 3029 | if (allowRepeatedFooter) { |
michael@0 | 3030 | PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); |
michael@0 | 3031 | } |
michael@0 | 3032 | else if (tfoot && tfoot->IsRepeatable()) { |
michael@0 | 3033 | tfoot->SetRepeatable(false); |
michael@0 | 3034 | } |
michael@0 | 3035 | aStatus = NS_FRAME_NOT_COMPLETE; |
michael@0 | 3036 | PushChildren(rowGroups, childX + 1); |
michael@0 | 3037 | aLastChildReflowed = kidFrame; |
michael@0 | 3038 | break; |
michael@0 | 3039 | } |
michael@0 | 3040 | } |
michael@0 | 3041 | } |
michael@0 | 3042 | else { // we are not on top, push this rowgroup onto the next page |
michael@0 | 3043 | if (prevKidFrame) { // we had a rowgroup before so push this |
michael@0 | 3044 | if (allowRepeatedFooter) { |
michael@0 | 3045 | PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); |
michael@0 | 3046 | } |
michael@0 | 3047 | else if (tfoot && tfoot->IsRepeatable()) { |
michael@0 | 3048 | tfoot->SetRepeatable(false); |
michael@0 | 3049 | } |
michael@0 | 3050 | aStatus = NS_FRAME_NOT_COMPLETE; |
michael@0 | 3051 | PushChildren(rowGroups, childX); |
michael@0 | 3052 | aLastChildReflowed = prevKidFrame; |
michael@0 | 3053 | break; |
michael@0 | 3054 | } |
michael@0 | 3055 | else { // we can't push so lets make clear how much space we need |
michael@0 | 3056 | PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect, |
michael@0 | 3057 | oldKidVisualOverflow); |
michael@0 | 3058 | aLastChildReflowed = kidFrame; |
michael@0 | 3059 | if (allowRepeatedFooter) { |
michael@0 | 3060 | PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); |
michael@0 | 3061 | aLastChildReflowed = tfoot; |
michael@0 | 3062 | } |
michael@0 | 3063 | break; |
michael@0 | 3064 | } |
michael@0 | 3065 | } |
michael@0 | 3066 | } |
michael@0 | 3067 | |
michael@0 | 3068 | aLastChildReflowed = kidFrame; |
michael@0 | 3069 | |
michael@0 | 3070 | pageBreak = false; |
michael@0 | 3071 | // see if there is a page break after this row group or before the next one |
michael@0 | 3072 | if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated && |
michael@0 | 3073 | (NS_UNCONSTRAINEDSIZE != kidReflowState.AvailableHeight())) { |
michael@0 | 3074 | nsIFrame* nextKid = |
michael@0 | 3075 | (childX + 1 < rowGroups.Length()) ? rowGroups[childX + 1] : nullptr; |
michael@0 | 3076 | pageBreak = PageBreakAfter(kidFrame, nextKid); |
michael@0 | 3077 | } |
michael@0 | 3078 | |
michael@0 | 3079 | // Place the child |
michael@0 | 3080 | PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect, |
michael@0 | 3081 | oldKidVisualOverflow); |
michael@0 | 3082 | |
michael@0 | 3083 | // Remember where we just were in case we end up pushing children |
michael@0 | 3084 | prevKidFrame = kidFrame; |
michael@0 | 3085 | |
michael@0 | 3086 | // Special handling for incomplete children |
michael@0 | 3087 | if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { |
michael@0 | 3088 | nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow(); |
michael@0 | 3089 | if (!kidNextInFlow) { |
michael@0 | 3090 | // The child doesn't have a next-in-flow so create a continuing |
michael@0 | 3091 | // frame. This hooks the child into the flow |
michael@0 | 3092 | kidNextInFlow = presContext->PresShell()->FrameConstructor()-> |
michael@0 | 3093 | CreateContinuingFrame(presContext, kidFrame, this); |
michael@0 | 3094 | |
michael@0 | 3095 | // Insert the kid's new next-in-flow into our sibling list... |
michael@0 | 3096 | mFrames.InsertFrame(nullptr, kidFrame, kidNextInFlow); |
michael@0 | 3097 | // and in rowGroups after childX so that it will get pushed below. |
michael@0 | 3098 | rowGroups.InsertElementAt(childX + 1, |
michael@0 | 3099 | static_cast<nsTableRowGroupFrame*>(kidNextInFlow)); |
michael@0 | 3100 | } else if (kidNextInFlow == kidFrame->GetNextSibling()) { |
michael@0 | 3101 | // OrderRowGroups excludes NIFs in the child list from 'rowGroups' |
michael@0 | 3102 | // so we deal with that here to make sure they get pushed. |
michael@0 | 3103 | MOZ_ASSERT(!rowGroups.Contains(kidNextInFlow), |
michael@0 | 3104 | "OrderRowGroups must not put our NIF in 'rowGroups'"); |
michael@0 | 3105 | rowGroups.InsertElementAt(childX + 1, |
michael@0 | 3106 | static_cast<nsTableRowGroupFrame*>(kidNextInFlow)); |
michael@0 | 3107 | } |
michael@0 | 3108 | |
michael@0 | 3109 | // We've used up all of our available space so push the remaining |
michael@0 | 3110 | // children. |
michael@0 | 3111 | if (allowRepeatedFooter) { |
michael@0 | 3112 | PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); |
michael@0 | 3113 | } |
michael@0 | 3114 | else if (tfoot && tfoot->IsRepeatable()) { |
michael@0 | 3115 | tfoot->SetRepeatable(false); |
michael@0 | 3116 | } |
michael@0 | 3117 | |
michael@0 | 3118 | nsIFrame* nextSibling = kidFrame->GetNextSibling(); |
michael@0 | 3119 | if (nextSibling) { |
michael@0 | 3120 | PushChildren(rowGroups, childX + 1); |
michael@0 | 3121 | } |
michael@0 | 3122 | break; |
michael@0 | 3123 | } |
michael@0 | 3124 | } |
michael@0 | 3125 | else { // it isn't being reflowed |
michael@0 | 3126 | aReflowState.y += cellSpacingY; |
michael@0 | 3127 | nsRect kidRect = kidFrame->GetRect(); |
michael@0 | 3128 | if (kidRect.y != aReflowState.y) { |
michael@0 | 3129 | // invalidate the old position |
michael@0 | 3130 | kidFrame->InvalidateFrameSubtree(); |
michael@0 | 3131 | kidRect.y = aReflowState.y; |
michael@0 | 3132 | kidFrame->SetRect(kidRect); // move to the new position |
michael@0 | 3133 | RePositionViews(kidFrame); |
michael@0 | 3134 | // invalidate the new position |
michael@0 | 3135 | kidFrame->InvalidateFrameSubtree(); |
michael@0 | 3136 | } |
michael@0 | 3137 | aReflowState.y += kidRect.height; |
michael@0 | 3138 | |
michael@0 | 3139 | // If our height is constrained then update the available height. |
michael@0 | 3140 | if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { |
michael@0 | 3141 | aReflowState.availSize.height -= cellSpacingY + kidRect.height; |
michael@0 | 3142 | } |
michael@0 | 3143 | } |
michael@0 | 3144 | ConsiderChildOverflow(aOverflowAreas, kidFrame); |
michael@0 | 3145 | } |
michael@0 | 3146 | |
michael@0 | 3147 | // We've now propagated the column resizes and geometry changes to all |
michael@0 | 3148 | // the children. |
michael@0 | 3149 | mBits.mResizedColumns = false; |
michael@0 | 3150 | ClearGeometryDirty(); |
michael@0 | 3151 | |
michael@0 | 3152 | return rv; |
michael@0 | 3153 | } |
michael@0 | 3154 | |
michael@0 | 3155 | void |
michael@0 | 3156 | nsTableFrame::ReflowColGroups(nsRenderingContext *aRenderingContext) |
michael@0 | 3157 | { |
michael@0 | 3158 | if (!GetPrevInFlow() && !HaveReflowedColGroups()) { |
michael@0 | 3159 | nsHTMLReflowMetrics kidMet(GetWritingMode()); |
michael@0 | 3160 | nsPresContext *presContext = PresContext(); |
michael@0 | 3161 | for (nsIFrame* kidFrame = mColGroups.FirstChild(); kidFrame; |
michael@0 | 3162 | kidFrame = kidFrame->GetNextSibling()) { |
michael@0 | 3163 | if (NS_SUBTREE_DIRTY(kidFrame)) { |
michael@0 | 3164 | // The column groups don't care about dimensions or reflow states. |
michael@0 | 3165 | nsHTMLReflowState kidReflowState(presContext, kidFrame, |
michael@0 | 3166 | aRenderingContext, nsSize(0,0)); |
michael@0 | 3167 | nsReflowStatus cgStatus; |
michael@0 | 3168 | ReflowChild(kidFrame, presContext, kidMet, kidReflowState, 0, 0, 0, |
michael@0 | 3169 | cgStatus); |
michael@0 | 3170 | FinishReflowChild(kidFrame, presContext, kidMet, nullptr, 0, 0, 0); |
michael@0 | 3171 | } |
michael@0 | 3172 | } |
michael@0 | 3173 | SetHaveReflowedColGroups(true); |
michael@0 | 3174 | } |
michael@0 | 3175 | } |
michael@0 | 3176 | |
michael@0 | 3177 | void |
michael@0 | 3178 | nsTableFrame::CalcDesiredHeight(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize) |
michael@0 | 3179 | { |
michael@0 | 3180 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 3181 | if (!cellMap) { |
michael@0 | 3182 | NS_ASSERTION(false, "never ever call me until the cell map is built!"); |
michael@0 | 3183 | aDesiredSize.Height() = 0; |
michael@0 | 3184 | return; |
michael@0 | 3185 | } |
michael@0 | 3186 | nscoord cellSpacingY = GetCellSpacingY(); |
michael@0 | 3187 | nsMargin borderPadding = GetChildAreaOffset(&aReflowState); |
michael@0 | 3188 | |
michael@0 | 3189 | // get the natural height based on the last child's (row group) rect |
michael@0 | 3190 | RowGroupArray rowGroups; |
michael@0 | 3191 | OrderRowGroups(rowGroups); |
michael@0 | 3192 | if (rowGroups.IsEmpty()) { |
michael@0 | 3193 | // tables can be used as rectangular items without content |
michael@0 | 3194 | nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); |
michael@0 | 3195 | if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedHeight) && |
michael@0 | 3196 | (tableSpecifiedHeight > 0) && |
michael@0 | 3197 | eCompatibility_NavQuirks != PresContext()->CompatibilityMode()) { |
michael@0 | 3198 | // empty tables should not have a size in quirks mode |
michael@0 | 3199 | aDesiredSize.Height() = tableSpecifiedHeight; |
michael@0 | 3200 | } |
michael@0 | 3201 | else |
michael@0 | 3202 | aDesiredSize.Height() = 0; |
michael@0 | 3203 | return; |
michael@0 | 3204 | } |
michael@0 | 3205 | int32_t rowCount = cellMap->GetRowCount(); |
michael@0 | 3206 | int32_t colCount = cellMap->GetColCount(); |
michael@0 | 3207 | nscoord desiredHeight = borderPadding.top + borderPadding.bottom; |
michael@0 | 3208 | if (rowCount > 0 && colCount > 0) { |
michael@0 | 3209 | desiredHeight += cellSpacingY; |
michael@0 | 3210 | for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { |
michael@0 | 3211 | desiredHeight += rowGroups[rgX]->GetSize().height + cellSpacingY; |
michael@0 | 3212 | } |
michael@0 | 3213 | } |
michael@0 | 3214 | |
michael@0 | 3215 | // see if a specified table height requires dividing additional space to rows |
michael@0 | 3216 | if (!GetPrevInFlow()) { |
michael@0 | 3217 | nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); |
michael@0 | 3218 | if ((tableSpecifiedHeight > 0) && |
michael@0 | 3219 | (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) && |
michael@0 | 3220 | (tableSpecifiedHeight > desiredHeight)) { |
michael@0 | 3221 | // proportionately distribute the excess height to unconstrained rows in each |
michael@0 | 3222 | // unconstrained row group. |
michael@0 | 3223 | DistributeHeightToRows(aReflowState, tableSpecifiedHeight - desiredHeight); |
michael@0 | 3224 | // this might have changed the overflow area incorporate the childframe overflow area. |
michael@0 | 3225 | for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) { |
michael@0 | 3226 | ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame); |
michael@0 | 3227 | } |
michael@0 | 3228 | desiredHeight = tableSpecifiedHeight; |
michael@0 | 3229 | } |
michael@0 | 3230 | } |
michael@0 | 3231 | aDesiredSize.Height() = desiredHeight; |
michael@0 | 3232 | } |
michael@0 | 3233 | |
michael@0 | 3234 | static |
michael@0 | 3235 | void ResizeCells(nsTableFrame& aTableFrame) |
michael@0 | 3236 | { |
michael@0 | 3237 | nsTableFrame::RowGroupArray rowGroups; |
michael@0 | 3238 | aTableFrame.OrderRowGroups(rowGroups); |
michael@0 | 3239 | nsHTMLReflowMetrics tableDesiredSize(aTableFrame.GetWritingMode()); // ??? |
michael@0 | 3240 | nsRect tableRect = aTableFrame.GetRect(); |
michael@0 | 3241 | tableDesiredSize.Width() = tableRect.width; |
michael@0 | 3242 | tableDesiredSize.Height() = tableRect.height; |
michael@0 | 3243 | tableDesiredSize.SetOverflowAreasToDesiredBounds(); |
michael@0 | 3244 | |
michael@0 | 3245 | for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { |
michael@0 | 3246 | nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
michael@0 | 3247 | |
michael@0 | 3248 | nsRect rowGroupRect = rgFrame->GetRect(); |
michael@0 | 3249 | nsHTMLReflowMetrics groupDesiredSize(tableDesiredSize.GetWritingMode()); |
michael@0 | 3250 | groupDesiredSize.Width() = rowGroupRect.width; |
michael@0 | 3251 | groupDesiredSize.Height() = rowGroupRect.height; |
michael@0 | 3252 | groupDesiredSize.SetOverflowAreasToDesiredBounds(); |
michael@0 | 3253 | |
michael@0 | 3254 | nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); |
michael@0 | 3255 | while (rowFrame) { |
michael@0 | 3256 | rowFrame->DidResize(); |
michael@0 | 3257 | rgFrame->ConsiderChildOverflow(groupDesiredSize.mOverflowAreas, rowFrame); |
michael@0 | 3258 | rowFrame = rowFrame->GetNextRow(); |
michael@0 | 3259 | } |
michael@0 | 3260 | rgFrame->FinishAndStoreOverflow(&groupDesiredSize); |
michael@0 | 3261 | tableDesiredSize.mOverflowAreas.UnionWith(groupDesiredSize.mOverflowAreas + |
michael@0 | 3262 | rgFrame->GetPosition()); |
michael@0 | 3263 | } |
michael@0 | 3264 | aTableFrame.FinishAndStoreOverflow(&tableDesiredSize); |
michael@0 | 3265 | } |
michael@0 | 3266 | |
michael@0 | 3267 | void |
michael@0 | 3268 | nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, |
michael@0 | 3269 | nscoord aAmount) |
michael@0 | 3270 | { |
michael@0 | 3271 | nscoord cellSpacingY = GetCellSpacingY(); |
michael@0 | 3272 | |
michael@0 | 3273 | nsMargin borderPadding = GetChildAreaOffset(&aReflowState); |
michael@0 | 3274 | |
michael@0 | 3275 | RowGroupArray rowGroups; |
michael@0 | 3276 | OrderRowGroups(rowGroups); |
michael@0 | 3277 | |
michael@0 | 3278 | nscoord amountUsed = 0; |
michael@0 | 3279 | // distribute space to each pct height row whose row group doesn't have a computed |
michael@0 | 3280 | // height, and base the pct on the table height. If the row group had a computed |
michael@0 | 3281 | // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights |
michael@0 | 3282 | nscoord pctBasis = aReflowState.ComputedHeight() - (GetCellSpacingY() * (GetRowCount() + 1)); |
michael@0 | 3283 | nscoord yOriginRG = borderPadding.top + GetCellSpacingY(); |
michael@0 | 3284 | nscoord yEndRG = yOriginRG; |
michael@0 | 3285 | uint32_t rgX; |
michael@0 | 3286 | for (rgX = 0; rgX < rowGroups.Length(); rgX++) { |
michael@0 | 3287 | nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
michael@0 | 3288 | nscoord amountUsedByRG = 0; |
michael@0 | 3289 | nscoord yOriginRow = 0; |
michael@0 | 3290 | nsRect rgRect = rgFrame->GetRect(); |
michael@0 | 3291 | if (!rgFrame->HasStyleHeight()) { |
michael@0 | 3292 | nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); |
michael@0 | 3293 | while (rowFrame) { |
michael@0 | 3294 | nsRect rowRect = rowFrame->GetRect(); |
michael@0 | 3295 | if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) { |
michael@0 | 3296 | nscoord pctHeight = rowFrame->GetHeight(pctBasis); |
michael@0 | 3297 | nscoord amountForRow = std::min(aAmount - amountUsed, pctHeight - rowRect.height); |
michael@0 | 3298 | if (amountForRow > 0) { |
michael@0 | 3299 | nsRect oldRowRect = rowRect; |
michael@0 | 3300 | rowRect.height += amountForRow; |
michael@0 | 3301 | // XXXbz we don't need to change rowRect.y to be yOriginRow? |
michael@0 | 3302 | rowFrame->SetRect(rowRect); |
michael@0 | 3303 | yOriginRow += rowRect.height + cellSpacingY; |
michael@0 | 3304 | yEndRG += rowRect.height + cellSpacingY; |
michael@0 | 3305 | amountUsed += amountForRow; |
michael@0 | 3306 | amountUsedByRG += amountForRow; |
michael@0 | 3307 | //rowFrame->DidResize(); |
michael@0 | 3308 | nsTableFrame::RePositionViews(rowFrame); |
michael@0 | 3309 | |
michael@0 | 3310 | rgFrame->InvalidateFrameWithRect(oldRowRect); |
michael@0 | 3311 | rgFrame->InvalidateFrame(); |
michael@0 | 3312 | } |
michael@0 | 3313 | } |
michael@0 | 3314 | else { |
michael@0 | 3315 | if (amountUsed > 0 && yOriginRow != rowRect.y && |
michael@0 | 3316 | !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { |
michael@0 | 3317 | rowFrame->InvalidateFrameSubtree(); |
michael@0 | 3318 | rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow)); |
michael@0 | 3319 | nsTableFrame::RePositionViews(rowFrame); |
michael@0 | 3320 | rowFrame->InvalidateFrameSubtree(); |
michael@0 | 3321 | } |
michael@0 | 3322 | yOriginRow += rowRect.height + cellSpacingY; |
michael@0 | 3323 | yEndRG += rowRect.height + cellSpacingY; |
michael@0 | 3324 | } |
michael@0 | 3325 | rowFrame = rowFrame->GetNextRow(); |
michael@0 | 3326 | } |
michael@0 | 3327 | if (amountUsed > 0) { |
michael@0 | 3328 | if (rgRect.y != yOriginRG) { |
michael@0 | 3329 | rgFrame->InvalidateFrameSubtree(); |
michael@0 | 3330 | } |
michael@0 | 3331 | |
michael@0 | 3332 | nsRect origRgRect = rgRect; |
michael@0 | 3333 | nsRect origRgVisualOverflow = rgFrame->GetVisualOverflowRect(); |
michael@0 | 3334 | |
michael@0 | 3335 | rgRect.y = yOriginRG; |
michael@0 | 3336 | rgRect.height += amountUsedByRG; |
michael@0 | 3337 | |
michael@0 | 3338 | rgFrame->SetRect(rgRect); |
michael@0 | 3339 | |
michael@0 | 3340 | nsTableFrame::InvalidateTableFrame(rgFrame, origRgRect, |
michael@0 | 3341 | origRgVisualOverflow, false); |
michael@0 | 3342 | } |
michael@0 | 3343 | } |
michael@0 | 3344 | else if (amountUsed > 0 && yOriginRG != rgRect.y) { |
michael@0 | 3345 | rgFrame->InvalidateFrameSubtree(); |
michael@0 | 3346 | rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG)); |
michael@0 | 3347 | // Make sure child views are properly positioned |
michael@0 | 3348 | nsTableFrame::RePositionViews(rgFrame); |
michael@0 | 3349 | rgFrame->InvalidateFrameSubtree(); |
michael@0 | 3350 | } |
michael@0 | 3351 | yOriginRG = yEndRG; |
michael@0 | 3352 | } |
michael@0 | 3353 | |
michael@0 | 3354 | if (amountUsed >= aAmount) { |
michael@0 | 3355 | ResizeCells(*this); |
michael@0 | 3356 | return; |
michael@0 | 3357 | } |
michael@0 | 3358 | |
michael@0 | 3359 | // get the first row without a style height where its row group has an |
michael@0 | 3360 | // unconstrained height |
michael@0 | 3361 | nsTableRowGroupFrame* firstUnStyledRG = nullptr; |
michael@0 | 3362 | nsTableRowFrame* firstUnStyledRow = nullptr; |
michael@0 | 3363 | for (rgX = 0; rgX < rowGroups.Length() && !firstUnStyledRG; rgX++) { |
michael@0 | 3364 | nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
michael@0 | 3365 | if (!rgFrame->HasStyleHeight()) { |
michael@0 | 3366 | nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); |
michael@0 | 3367 | while (rowFrame) { |
michael@0 | 3368 | if (!rowFrame->HasStyleHeight()) { |
michael@0 | 3369 | firstUnStyledRG = rgFrame; |
michael@0 | 3370 | firstUnStyledRow = rowFrame; |
michael@0 | 3371 | break; |
michael@0 | 3372 | } |
michael@0 | 3373 | rowFrame = rowFrame->GetNextRow(); |
michael@0 | 3374 | } |
michael@0 | 3375 | } |
michael@0 | 3376 | } |
michael@0 | 3377 | |
michael@0 | 3378 | nsTableRowFrame* lastEligibleRow = nullptr; |
michael@0 | 3379 | // Accumulate the correct divisor. This will be the total total height of all |
michael@0 | 3380 | // unstyled rows inside unstyled row groups, unless there are none, in which |
michael@0 | 3381 | // case, it will be number of all rows. If the unstyled rows don't have a |
michael@0 | 3382 | // height, divide the space equally among them. |
michael@0 | 3383 | nscoord divisor = 0; |
michael@0 | 3384 | int32_t eligibleRows = 0; |
michael@0 | 3385 | bool expandEmptyRows = false; |
michael@0 | 3386 | |
michael@0 | 3387 | if (!firstUnStyledRow) { |
michael@0 | 3388 | // there is no unstyled row |
michael@0 | 3389 | divisor = GetRowCount(); |
michael@0 | 3390 | } |
michael@0 | 3391 | else { |
michael@0 | 3392 | for (rgX = 0; rgX < rowGroups.Length(); rgX++) { |
michael@0 | 3393 | nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
michael@0 | 3394 | if (!firstUnStyledRG || !rgFrame->HasStyleHeight()) { |
michael@0 | 3395 | nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); |
michael@0 | 3396 | while (rowFrame) { |
michael@0 | 3397 | if (!firstUnStyledRG || !rowFrame->HasStyleHeight()) { |
michael@0 | 3398 | NS_ASSERTION(rowFrame->GetSize().height >= 0, |
michael@0 | 3399 | "negative row frame height"); |
michael@0 | 3400 | divisor += rowFrame->GetSize().height; |
michael@0 | 3401 | eligibleRows++; |
michael@0 | 3402 | lastEligibleRow = rowFrame; |
michael@0 | 3403 | } |
michael@0 | 3404 | rowFrame = rowFrame->GetNextRow(); |
michael@0 | 3405 | } |
michael@0 | 3406 | } |
michael@0 | 3407 | } |
michael@0 | 3408 | if (divisor <= 0) { |
michael@0 | 3409 | if (eligibleRows > 0) { |
michael@0 | 3410 | expandEmptyRows = true; |
michael@0 | 3411 | } |
michael@0 | 3412 | else { |
michael@0 | 3413 | NS_ERROR("invalid divisor"); |
michael@0 | 3414 | return; |
michael@0 | 3415 | } |
michael@0 | 3416 | } |
michael@0 | 3417 | } |
michael@0 | 3418 | // allocate the extra height to the unstyled row groups and rows |
michael@0 | 3419 | nscoord heightToDistribute = aAmount - amountUsed; |
michael@0 | 3420 | yOriginRG = borderPadding.top + cellSpacingY; |
michael@0 | 3421 | yEndRG = yOriginRG; |
michael@0 | 3422 | for (rgX = 0; rgX < rowGroups.Length(); rgX++) { |
michael@0 | 3423 | nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
michael@0 | 3424 | nscoord amountUsedByRG = 0; |
michael@0 | 3425 | nscoord yOriginRow = 0; |
michael@0 | 3426 | nsRect rgRect = rgFrame->GetRect(); |
michael@0 | 3427 | nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect(); |
michael@0 | 3428 | // see if there is an eligible row group or we distribute to all rows |
michael@0 | 3429 | if (!firstUnStyledRG || !rgFrame->HasStyleHeight() || !eligibleRows) { |
michael@0 | 3430 | nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); |
michael@0 | 3431 | while (rowFrame) { |
michael@0 | 3432 | nsRect rowRect = rowFrame->GetRect(); |
michael@0 | 3433 | nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect(); |
michael@0 | 3434 | // see if there is an eligible row or we distribute to all rows |
michael@0 | 3435 | if (!firstUnStyledRow || !rowFrame->HasStyleHeight() || !eligibleRows) { |
michael@0 | 3436 | float ratio; |
michael@0 | 3437 | if (eligibleRows) { |
michael@0 | 3438 | if (!expandEmptyRows) { |
michael@0 | 3439 | // The amount of additional space each row gets is proportional to |
michael@0 | 3440 | // its height |
michael@0 | 3441 | ratio = float(rowRect.height) / float(divisor); |
michael@0 | 3442 | } else { |
michael@0 | 3443 | // empty rows get all the same additional space |
michael@0 | 3444 | ratio = 1.0f / float(eligibleRows); |
michael@0 | 3445 | } |
michael@0 | 3446 | } |
michael@0 | 3447 | else { |
michael@0 | 3448 | // all rows get the same additional space |
michael@0 | 3449 | ratio = 1.0f / float(divisor); |
michael@0 | 3450 | } |
michael@0 | 3451 | // give rows their additional space, except for the last row which |
michael@0 | 3452 | // gets the remainder |
michael@0 | 3453 | nscoord amountForRow = (rowFrame == lastEligibleRow) |
michael@0 | 3454 | ? aAmount - amountUsed : NSToCoordRound(((float)(heightToDistribute)) * ratio); |
michael@0 | 3455 | amountForRow = std::min(amountForRow, aAmount - amountUsed); |
michael@0 | 3456 | |
michael@0 | 3457 | if (yOriginRow != rowRect.y) { |
michael@0 | 3458 | rowFrame->InvalidateFrameSubtree(); |
michael@0 | 3459 | } |
michael@0 | 3460 | |
michael@0 | 3461 | // update the row height |
michael@0 | 3462 | nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width, |
michael@0 | 3463 | rowRect.height + amountForRow); |
michael@0 | 3464 | rowFrame->SetRect(newRowRect); |
michael@0 | 3465 | |
michael@0 | 3466 | yOriginRow += newRowRect.height + cellSpacingY; |
michael@0 | 3467 | yEndRG += newRowRect.height + cellSpacingY; |
michael@0 | 3468 | |
michael@0 | 3469 | amountUsed += amountForRow; |
michael@0 | 3470 | amountUsedByRG += amountForRow; |
michael@0 | 3471 | NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation"); |
michael@0 | 3472 | //rowFrame->DidResize(); |
michael@0 | 3473 | nsTableFrame::RePositionViews(rowFrame); |
michael@0 | 3474 | |
michael@0 | 3475 | nsTableFrame::InvalidateTableFrame(rowFrame, rowRect, rowVisualOverflow, |
michael@0 | 3476 | false); |
michael@0 | 3477 | } |
michael@0 | 3478 | else { |
michael@0 | 3479 | if (amountUsed > 0 && yOriginRow != rowRect.y) { |
michael@0 | 3480 | rowFrame->InvalidateFrameSubtree(); |
michael@0 | 3481 | rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow)); |
michael@0 | 3482 | nsTableFrame::RePositionViews(rowFrame); |
michael@0 | 3483 | rowFrame->InvalidateFrameSubtree(); |
michael@0 | 3484 | } |
michael@0 | 3485 | yOriginRow += rowRect.height + cellSpacingY; |
michael@0 | 3486 | yEndRG += rowRect.height + cellSpacingY; |
michael@0 | 3487 | } |
michael@0 | 3488 | rowFrame = rowFrame->GetNextRow(); |
michael@0 | 3489 | } |
michael@0 | 3490 | if (amountUsed > 0) { |
michael@0 | 3491 | if (rgRect.y != yOriginRG) { |
michael@0 | 3492 | rgFrame->InvalidateFrameSubtree(); |
michael@0 | 3493 | } |
michael@0 | 3494 | |
michael@0 | 3495 | rgFrame->SetRect(nsRect(rgRect.x, yOriginRG, rgRect.width, |
michael@0 | 3496 | rgRect.height + amountUsedByRG)); |
michael@0 | 3497 | |
michael@0 | 3498 | nsTableFrame::InvalidateTableFrame(rgFrame, rgRect, rgVisualOverflow, |
michael@0 | 3499 | false); |
michael@0 | 3500 | } |
michael@0 | 3501 | // Make sure child views are properly positioned |
michael@0 | 3502 | } |
michael@0 | 3503 | else if (amountUsed > 0 && yOriginRG != rgRect.y) { |
michael@0 | 3504 | rgFrame->InvalidateFrameSubtree(); |
michael@0 | 3505 | rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG)); |
michael@0 | 3506 | // Make sure child views are properly positioned |
michael@0 | 3507 | nsTableFrame::RePositionViews(rgFrame); |
michael@0 | 3508 | rgFrame->InvalidateFrameSubtree(); |
michael@0 | 3509 | } |
michael@0 | 3510 | yOriginRG = yEndRG; |
michael@0 | 3511 | } |
michael@0 | 3512 | |
michael@0 | 3513 | ResizeCells(*this); |
michael@0 | 3514 | } |
michael@0 | 3515 | |
michael@0 | 3516 | int32_t nsTableFrame::GetColumnWidth(int32_t aColIndex) |
michael@0 | 3517 | { |
michael@0 | 3518 | nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow()); |
michael@0 | 3519 | if (this == firstInFlow) { |
michael@0 | 3520 | nsTableColFrame* colFrame = GetColFrame(aColIndex); |
michael@0 | 3521 | return colFrame ? colFrame->GetFinalWidth() : 0; |
michael@0 | 3522 | } |
michael@0 | 3523 | return firstInFlow->GetColumnWidth(aColIndex); |
michael@0 | 3524 | } |
michael@0 | 3525 | |
michael@0 | 3526 | // XXX: could cache this. But be sure to check style changes if you do! |
michael@0 | 3527 | nscoord nsTableFrame::GetCellSpacingX() |
michael@0 | 3528 | { |
michael@0 | 3529 | if (IsBorderCollapse()) |
michael@0 | 3530 | return 0; |
michael@0 | 3531 | |
michael@0 | 3532 | return StyleTableBorder()->mBorderSpacingX; |
michael@0 | 3533 | } |
michael@0 | 3534 | |
michael@0 | 3535 | // XXX: could cache this. But be sure to check style changes if you do! |
michael@0 | 3536 | nscoord nsTableFrame::GetCellSpacingY() |
michael@0 | 3537 | { |
michael@0 | 3538 | if (IsBorderCollapse()) |
michael@0 | 3539 | return 0; |
michael@0 | 3540 | |
michael@0 | 3541 | return StyleTableBorder()->mBorderSpacingY; |
michael@0 | 3542 | } |
michael@0 | 3543 | |
michael@0 | 3544 | |
michael@0 | 3545 | /* virtual */ nscoord |
michael@0 | 3546 | nsTableFrame::GetBaseline() const |
michael@0 | 3547 | { |
michael@0 | 3548 | nscoord ascent = 0; |
michael@0 | 3549 | RowGroupArray orderedRowGroups; |
michael@0 | 3550 | OrderRowGroups(orderedRowGroups); |
michael@0 | 3551 | nsTableRowFrame* firstRow = nullptr; |
michael@0 | 3552 | for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { |
michael@0 | 3553 | nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex]; |
michael@0 | 3554 | if (rgFrame->GetRowCount()) { |
michael@0 | 3555 | firstRow = rgFrame->GetFirstRow(); |
michael@0 | 3556 | ascent = rgFrame->GetRect().y + firstRow->GetRect().y + firstRow->GetRowBaseline(); |
michael@0 | 3557 | break; |
michael@0 | 3558 | } |
michael@0 | 3559 | } |
michael@0 | 3560 | if (!firstRow) |
michael@0 | 3561 | ascent = GetRect().height; |
michael@0 | 3562 | return ascent; |
michael@0 | 3563 | } |
michael@0 | 3564 | /* ----- global methods ----- */ |
michael@0 | 3565 | |
michael@0 | 3566 | nsIFrame* |
michael@0 | 3567 | NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
michael@0 | 3568 | { |
michael@0 | 3569 | return new (aPresShell) nsTableFrame(aContext); |
michael@0 | 3570 | } |
michael@0 | 3571 | |
michael@0 | 3572 | NS_IMPL_FRAMEARENA_HELPERS(nsTableFrame) |
michael@0 | 3573 | |
michael@0 | 3574 | nsTableFrame* |
michael@0 | 3575 | nsTableFrame::GetTableFrame(nsIFrame* aFrame) |
michael@0 | 3576 | { |
michael@0 | 3577 | for (nsIFrame* ancestor = aFrame->GetParent(); ancestor; |
michael@0 | 3578 | ancestor = ancestor->GetParent()) { |
michael@0 | 3579 | if (nsGkAtoms::tableFrame == ancestor->GetType()) { |
michael@0 | 3580 | return static_cast<nsTableFrame*>(ancestor); |
michael@0 | 3581 | } |
michael@0 | 3582 | } |
michael@0 | 3583 | NS_RUNTIMEABORT("unable to find table parent"); |
michael@0 | 3584 | return nullptr; |
michael@0 | 3585 | } |
michael@0 | 3586 | |
michael@0 | 3587 | nsTableFrame* |
michael@0 | 3588 | nsTableFrame::GetTableFramePassingThrough(nsIFrame* aMustPassThrough, |
michael@0 | 3589 | nsIFrame* aFrame) |
michael@0 | 3590 | { |
michael@0 | 3591 | MOZ_ASSERT(aMustPassThrough == aFrame || |
michael@0 | 3592 | nsLayoutUtils::IsProperAncestorFrame(aMustPassThrough, aFrame), |
michael@0 | 3593 | "aMustPassThrough should be an ancestor"); |
michael@0 | 3594 | |
michael@0 | 3595 | // Retrieve the table frame, and ensure that we hit aMustPassThrough on the |
michael@0 | 3596 | // way. If we don't, just return null. |
michael@0 | 3597 | bool hitPassThroughFrame = false; |
michael@0 | 3598 | nsTableFrame* tableFrame = nullptr; |
michael@0 | 3599 | for (nsIFrame* ancestor = aFrame; ancestor; ancestor = ancestor->GetParent()) { |
michael@0 | 3600 | if (ancestor == aMustPassThrough) { |
michael@0 | 3601 | hitPassThroughFrame = true; |
michael@0 | 3602 | } |
michael@0 | 3603 | if (nsGkAtoms::tableFrame == ancestor->GetType()) { |
michael@0 | 3604 | tableFrame = static_cast<nsTableFrame*>(ancestor); |
michael@0 | 3605 | break; |
michael@0 | 3606 | } |
michael@0 | 3607 | } |
michael@0 | 3608 | |
michael@0 | 3609 | MOZ_ASSERT(tableFrame, "Should have a table frame here"); |
michael@0 | 3610 | return hitPassThroughFrame ? tableFrame : nullptr; |
michael@0 | 3611 | } |
michael@0 | 3612 | |
michael@0 | 3613 | bool |
michael@0 | 3614 | nsTableFrame::IsAutoHeight() |
michael@0 | 3615 | { |
michael@0 | 3616 | const nsStyleCoord &height = StylePosition()->mHeight; |
michael@0 | 3617 | // Don't consider calc() here like this quirk for percent. |
michael@0 | 3618 | return height.GetUnit() == eStyleUnit_Auto || |
michael@0 | 3619 | (height.GetUnit() == eStyleUnit_Percent && |
michael@0 | 3620 | height.GetPercentValue() <= 0.0f); |
michael@0 | 3621 | } |
michael@0 | 3622 | |
michael@0 | 3623 | nscoord |
michael@0 | 3624 | nsTableFrame::CalcBorderBoxHeight(const nsHTMLReflowState& aState) |
michael@0 | 3625 | { |
michael@0 | 3626 | nscoord height = aState.ComputedHeight(); |
michael@0 | 3627 | if (NS_AUTOHEIGHT != height) { |
michael@0 | 3628 | nsMargin borderPadding = GetChildAreaOffset(&aState); |
michael@0 | 3629 | height += borderPadding.top + borderPadding.bottom; |
michael@0 | 3630 | } |
michael@0 | 3631 | height = std::max(0, height); |
michael@0 | 3632 | |
michael@0 | 3633 | return height; |
michael@0 | 3634 | } |
michael@0 | 3635 | |
michael@0 | 3636 | bool |
michael@0 | 3637 | nsTableFrame::IsAutoLayout() |
michael@0 | 3638 | { |
michael@0 | 3639 | if (StyleTable()->mLayoutStrategy == NS_STYLE_TABLE_LAYOUT_AUTO) |
michael@0 | 3640 | return true; |
michael@0 | 3641 | // a fixed-layout inline-table must have a width |
michael@0 | 3642 | // and tables with 'width: -moz-max-content' must be auto-layout |
michael@0 | 3643 | // (at least as long as FixedTableLayoutStrategy::GetPrefWidth returns |
michael@0 | 3644 | // nscoord_MAX) |
michael@0 | 3645 | const nsStyleCoord &width = StylePosition()->mWidth; |
michael@0 | 3646 | return (width.GetUnit() == eStyleUnit_Auto) || |
michael@0 | 3647 | (width.GetUnit() == eStyleUnit_Enumerated && |
michael@0 | 3648 | width.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT); |
michael@0 | 3649 | } |
michael@0 | 3650 | |
michael@0 | 3651 | #ifdef DEBUG_FRAME_DUMP |
michael@0 | 3652 | nsresult |
michael@0 | 3653 | nsTableFrame::GetFrameName(nsAString& aResult) const |
michael@0 | 3654 | { |
michael@0 | 3655 | return MakeFrameName(NS_LITERAL_STRING("Table"), aResult); |
michael@0 | 3656 | } |
michael@0 | 3657 | #endif |
michael@0 | 3658 | |
michael@0 | 3659 | // Find the closet sibling before aPriorChildFrame (including aPriorChildFrame) that |
michael@0 | 3660 | // is of type aChildType |
michael@0 | 3661 | nsIFrame* |
michael@0 | 3662 | nsTableFrame::GetFrameAtOrBefore(nsIFrame* aParentFrame, |
michael@0 | 3663 | nsIFrame* aPriorChildFrame, |
michael@0 | 3664 | nsIAtom* aChildType) |
michael@0 | 3665 | { |
michael@0 | 3666 | nsIFrame* result = nullptr; |
michael@0 | 3667 | if (!aPriorChildFrame) { |
michael@0 | 3668 | return result; |
michael@0 | 3669 | } |
michael@0 | 3670 | if (aChildType == aPriorChildFrame->GetType()) { |
michael@0 | 3671 | return aPriorChildFrame; |
michael@0 | 3672 | } |
michael@0 | 3673 | |
michael@0 | 3674 | // aPriorChildFrame is not of type aChildType, so we need start from |
michael@0 | 3675 | // the beginnng and find the closest one |
michael@0 | 3676 | nsIFrame* lastMatchingFrame = nullptr; |
michael@0 | 3677 | nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild(); |
michael@0 | 3678 | while (childFrame && (childFrame != aPriorChildFrame)) { |
michael@0 | 3679 | if (aChildType == childFrame->GetType()) { |
michael@0 | 3680 | lastMatchingFrame = childFrame; |
michael@0 | 3681 | } |
michael@0 | 3682 | childFrame = childFrame->GetNextSibling(); |
michael@0 | 3683 | } |
michael@0 | 3684 | return lastMatchingFrame; |
michael@0 | 3685 | } |
michael@0 | 3686 | |
michael@0 | 3687 | #ifdef DEBUG |
michael@0 | 3688 | void |
michael@0 | 3689 | nsTableFrame::DumpRowGroup(nsIFrame* aKidFrame) |
michael@0 | 3690 | { |
michael@0 | 3691 | if (!aKidFrame) |
michael@0 | 3692 | return; |
michael@0 | 3693 | |
michael@0 | 3694 | nsIFrame* cFrame = aKidFrame->GetFirstPrincipalChild(); |
michael@0 | 3695 | while (cFrame) { |
michael@0 | 3696 | nsTableRowFrame *rowFrame = do_QueryFrame(cFrame); |
michael@0 | 3697 | if (rowFrame) { |
michael@0 | 3698 | printf("row(%d)=%p ", rowFrame->GetRowIndex(), |
michael@0 | 3699 | static_cast<void*>(rowFrame)); |
michael@0 | 3700 | nsIFrame* childFrame = cFrame->GetFirstPrincipalChild(); |
michael@0 | 3701 | while (childFrame) { |
michael@0 | 3702 | nsTableCellFrame *cellFrame = do_QueryFrame(childFrame); |
michael@0 | 3703 | if (cellFrame) { |
michael@0 | 3704 | int32_t colIndex; |
michael@0 | 3705 | cellFrame->GetColIndex(colIndex); |
michael@0 | 3706 | printf("cell(%d)=%p ", colIndex, static_cast<void*>(childFrame)); |
michael@0 | 3707 | } |
michael@0 | 3708 | childFrame = childFrame->GetNextSibling(); |
michael@0 | 3709 | } |
michael@0 | 3710 | printf("\n"); |
michael@0 | 3711 | } |
michael@0 | 3712 | else { |
michael@0 | 3713 | DumpRowGroup(rowFrame); |
michael@0 | 3714 | } |
michael@0 | 3715 | cFrame = cFrame->GetNextSibling(); |
michael@0 | 3716 | } |
michael@0 | 3717 | } |
michael@0 | 3718 | |
michael@0 | 3719 | void |
michael@0 | 3720 | nsTableFrame::Dump(bool aDumpRows, |
michael@0 | 3721 | bool aDumpCols, |
michael@0 | 3722 | bool aDumpCellMap) |
michael@0 | 3723 | { |
michael@0 | 3724 | printf("***START TABLE DUMP*** \n"); |
michael@0 | 3725 | // dump the columns widths array |
michael@0 | 3726 | printf("mColWidths="); |
michael@0 | 3727 | int32_t numCols = GetColCount(); |
michael@0 | 3728 | int32_t colX; |
michael@0 | 3729 | for (colX = 0; colX < numCols; colX++) { |
michael@0 | 3730 | printf("%d ", GetColumnWidth(colX)); |
michael@0 | 3731 | } |
michael@0 | 3732 | printf("\n"); |
michael@0 | 3733 | |
michael@0 | 3734 | if (aDumpRows) { |
michael@0 | 3735 | nsIFrame* kidFrame = mFrames.FirstChild(); |
michael@0 | 3736 | while (kidFrame) { |
michael@0 | 3737 | DumpRowGroup(kidFrame); |
michael@0 | 3738 | kidFrame = kidFrame->GetNextSibling(); |
michael@0 | 3739 | } |
michael@0 | 3740 | } |
michael@0 | 3741 | |
michael@0 | 3742 | if (aDumpCols) { |
michael@0 | 3743 | // output col frame cache |
michael@0 | 3744 | printf("\n col frame cache ->"); |
michael@0 | 3745 | for (colX = 0; colX < numCols; colX++) { |
michael@0 | 3746 | nsTableColFrame* colFrame = mColFrames.ElementAt(colX); |
michael@0 | 3747 | if (0 == (colX % 8)) { |
michael@0 | 3748 | printf("\n"); |
michael@0 | 3749 | } |
michael@0 | 3750 | printf ("%d=%p ", colX, static_cast<void*>(colFrame)); |
michael@0 | 3751 | nsTableColType colType = colFrame->GetColType(); |
michael@0 | 3752 | switch (colType) { |
michael@0 | 3753 | case eColContent: |
michael@0 | 3754 | printf(" content "); |
michael@0 | 3755 | break; |
michael@0 | 3756 | case eColAnonymousCol: |
michael@0 | 3757 | printf(" anonymous-column "); |
michael@0 | 3758 | break; |
michael@0 | 3759 | case eColAnonymousColGroup: |
michael@0 | 3760 | printf(" anonymous-colgroup "); |
michael@0 | 3761 | break; |
michael@0 | 3762 | case eColAnonymousCell: |
michael@0 | 3763 | printf(" anonymous-cell "); |
michael@0 | 3764 | break; |
michael@0 | 3765 | } |
michael@0 | 3766 | } |
michael@0 | 3767 | printf("\n colgroups->"); |
michael@0 | 3768 | for (nsIFrame* childFrame = mColGroups.FirstChild(); childFrame; |
michael@0 | 3769 | childFrame = childFrame->GetNextSibling()) { |
michael@0 | 3770 | if (nsGkAtoms::tableColGroupFrame == childFrame->GetType()) { |
michael@0 | 3771 | nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame *)childFrame; |
michael@0 | 3772 | colGroupFrame->Dump(1); |
michael@0 | 3773 | } |
michael@0 | 3774 | } |
michael@0 | 3775 | for (colX = 0; colX < numCols; colX++) { |
michael@0 | 3776 | printf("\n"); |
michael@0 | 3777 | nsTableColFrame* colFrame = GetColFrame(colX); |
michael@0 | 3778 | colFrame->Dump(1); |
michael@0 | 3779 | } |
michael@0 | 3780 | } |
michael@0 | 3781 | if (aDumpCellMap) { |
michael@0 | 3782 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 3783 | cellMap->Dump(); |
michael@0 | 3784 | } |
michael@0 | 3785 | printf(" ***END TABLE DUMP*** \n"); |
michael@0 | 3786 | } |
michael@0 | 3787 | #endif |
michael@0 | 3788 | |
michael@0 | 3789 | // nsTableIterator |
michael@0 | 3790 | nsTableIterator::nsTableIterator(nsIFrame& aSource) |
michael@0 | 3791 | { |
michael@0 | 3792 | nsIFrame* firstChild = aSource.GetFirstPrincipalChild(); |
michael@0 | 3793 | Init(firstChild); |
michael@0 | 3794 | } |
michael@0 | 3795 | |
michael@0 | 3796 | nsTableIterator::nsTableIterator(nsFrameList& aSource) |
michael@0 | 3797 | { |
michael@0 | 3798 | nsIFrame* firstChild = aSource.FirstChild(); |
michael@0 | 3799 | Init(firstChild); |
michael@0 | 3800 | } |
michael@0 | 3801 | |
michael@0 | 3802 | void nsTableIterator::Init(nsIFrame* aFirstChild) |
michael@0 | 3803 | { |
michael@0 | 3804 | mFirstListChild = aFirstChild; |
michael@0 | 3805 | mFirstChild = aFirstChild; |
michael@0 | 3806 | mCurrentChild = nullptr; |
michael@0 | 3807 | mLeftToRight = true; |
michael@0 | 3808 | mCount = -1; |
michael@0 | 3809 | |
michael@0 | 3810 | if (!mFirstChild) { |
michael@0 | 3811 | return; |
michael@0 | 3812 | } |
michael@0 | 3813 | |
michael@0 | 3814 | nsTableFrame* table = nsTableFrame::GetTableFrame(mFirstChild); |
michael@0 | 3815 | mLeftToRight = (NS_STYLE_DIRECTION_LTR == |
michael@0 | 3816 | table->StyleVisibility()->mDirection); |
michael@0 | 3817 | |
michael@0 | 3818 | if (!mLeftToRight) { |
michael@0 | 3819 | mCount = 0; |
michael@0 | 3820 | nsIFrame* nextChild = mFirstChild->GetNextSibling(); |
michael@0 | 3821 | while (nullptr != nextChild) { |
michael@0 | 3822 | mCount++; |
michael@0 | 3823 | mFirstChild = nextChild; |
michael@0 | 3824 | nextChild = nextChild->GetNextSibling(); |
michael@0 | 3825 | } |
michael@0 | 3826 | } |
michael@0 | 3827 | } |
michael@0 | 3828 | |
michael@0 | 3829 | nsIFrame* nsTableIterator::First() |
michael@0 | 3830 | { |
michael@0 | 3831 | mCurrentChild = mFirstChild; |
michael@0 | 3832 | return mCurrentChild; |
michael@0 | 3833 | } |
michael@0 | 3834 | |
michael@0 | 3835 | nsIFrame* nsTableIterator::Next() |
michael@0 | 3836 | { |
michael@0 | 3837 | if (!mCurrentChild) { |
michael@0 | 3838 | return nullptr; |
michael@0 | 3839 | } |
michael@0 | 3840 | |
michael@0 | 3841 | if (mLeftToRight) { |
michael@0 | 3842 | mCurrentChild = mCurrentChild->GetNextSibling(); |
michael@0 | 3843 | return mCurrentChild; |
michael@0 | 3844 | } |
michael@0 | 3845 | else { |
michael@0 | 3846 | nsIFrame* targetChild = mCurrentChild; |
michael@0 | 3847 | mCurrentChild = nullptr; |
michael@0 | 3848 | nsIFrame* child = mFirstListChild; |
michael@0 | 3849 | while (child && (child != targetChild)) { |
michael@0 | 3850 | mCurrentChild = child; |
michael@0 | 3851 | child = child->GetNextSibling(); |
michael@0 | 3852 | } |
michael@0 | 3853 | return mCurrentChild; |
michael@0 | 3854 | } |
michael@0 | 3855 | } |
michael@0 | 3856 | |
michael@0 | 3857 | bool nsTableIterator::IsLeftToRight() |
michael@0 | 3858 | { |
michael@0 | 3859 | return mLeftToRight; |
michael@0 | 3860 | } |
michael@0 | 3861 | |
michael@0 | 3862 | int32_t nsTableIterator::Count() |
michael@0 | 3863 | { |
michael@0 | 3864 | if (-1 == mCount) { |
michael@0 | 3865 | mCount = 0; |
michael@0 | 3866 | nsIFrame* child = mFirstListChild; |
michael@0 | 3867 | while (nullptr != child) { |
michael@0 | 3868 | mCount++; |
michael@0 | 3869 | child = child->GetNextSibling(); |
michael@0 | 3870 | } |
michael@0 | 3871 | } |
michael@0 | 3872 | return mCount; |
michael@0 | 3873 | } |
michael@0 | 3874 | |
michael@0 | 3875 | bool |
michael@0 | 3876 | nsTableFrame::ColumnHasCellSpacingBefore(int32_t aColIndex) const |
michael@0 | 3877 | { |
michael@0 | 3878 | // Since fixed-layout tables should not have their column sizes change |
michael@0 | 3879 | // as they load, we assume that all columns are significant. |
michael@0 | 3880 | if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Fixed) |
michael@0 | 3881 | return true; |
michael@0 | 3882 | // the first column is always significant |
michael@0 | 3883 | if (aColIndex == 0) |
michael@0 | 3884 | return true; |
michael@0 | 3885 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 3886 | if (!cellMap) |
michael@0 | 3887 | return false; |
michael@0 | 3888 | return cellMap->GetNumCellsOriginatingInCol(aColIndex) > 0; |
michael@0 | 3889 | } |
michael@0 | 3890 | |
michael@0 | 3891 | /******************************************************************************** |
michael@0 | 3892 | * Collapsing Borders |
michael@0 | 3893 | * |
michael@0 | 3894 | * The CSS spec says to resolve border conflicts in this order: |
michael@0 | 3895 | * 1) any border with the style HIDDEN wins |
michael@0 | 3896 | * 2) the widest border with a style that is not NONE wins |
michael@0 | 3897 | * 3) the border styles are ranked in this order, highest to lowest precedence: |
michael@0 | 3898 | * double, solid, dashed, dotted, ridge, outset, groove, inset |
michael@0 | 3899 | * 4) borders that are of equal width and style (differ only in color) have this precedence: |
michael@0 | 3900 | * cell, row, rowgroup, col, colgroup, table |
michael@0 | 3901 | * 5) if all border styles are NONE, then that's the computed border style. |
michael@0 | 3902 | *******************************************************************************/ |
michael@0 | 3903 | |
michael@0 | 3904 | #ifdef DEBUG |
michael@0 | 3905 | #define VerifyNonNegativeDamageRect(r) \ |
michael@0 | 3906 | NS_ASSERTION((r).x >= 0, "negative col index"); \ |
michael@0 | 3907 | NS_ASSERTION((r).y >= 0, "negative row index"); \ |
michael@0 | 3908 | NS_ASSERTION((r).width >= 0, "negative horizontal damage"); \ |
michael@0 | 3909 | NS_ASSERTION((r).height >= 0, "negative vertical damage"); |
michael@0 | 3910 | #define VerifyDamageRect(r) \ |
michael@0 | 3911 | VerifyNonNegativeDamageRect(r); \ |
michael@0 | 3912 | NS_ASSERTION((r).XMost() <= GetColCount(), \ |
michael@0 | 3913 | "horizontal damage extends outside table"); \ |
michael@0 | 3914 | NS_ASSERTION((r).YMost() <= GetRowCount(), \ |
michael@0 | 3915 | "vertical damage extends outside table"); |
michael@0 | 3916 | #endif |
michael@0 | 3917 | |
michael@0 | 3918 | void |
michael@0 | 3919 | nsTableFrame::AddBCDamageArea(const nsIntRect& aValue) |
michael@0 | 3920 | { |
michael@0 | 3921 | NS_ASSERTION(IsBorderCollapse(), "invalid AddBCDamageArea call"); |
michael@0 | 3922 | #ifdef DEBUG |
michael@0 | 3923 | VerifyDamageRect(aValue); |
michael@0 | 3924 | #endif |
michael@0 | 3925 | |
michael@0 | 3926 | SetNeedToCalcBCBorders(true); |
michael@0 | 3927 | // Get the property |
michael@0 | 3928 | BCPropertyData* value = GetBCProperty(true); |
michael@0 | 3929 | if (value) { |
michael@0 | 3930 | #ifdef DEBUG |
michael@0 | 3931 | VerifyNonNegativeDamageRect(value->mDamageArea); |
michael@0 | 3932 | #endif |
michael@0 | 3933 | // Clamp the old damage area to the current table area in case it shrunk. |
michael@0 | 3934 | int32_t cols = GetColCount(); |
michael@0 | 3935 | if (value->mDamageArea.XMost() > cols) { |
michael@0 | 3936 | if (value->mDamageArea.x > cols) { |
michael@0 | 3937 | value->mDamageArea.x = cols; |
michael@0 | 3938 | value->mDamageArea.width = 0; |
michael@0 | 3939 | } |
michael@0 | 3940 | else { |
michael@0 | 3941 | value->mDamageArea.width = cols - value->mDamageArea.x; |
michael@0 | 3942 | } |
michael@0 | 3943 | } |
michael@0 | 3944 | int32_t rows = GetRowCount(); |
michael@0 | 3945 | if (value->mDamageArea.YMost() > rows) { |
michael@0 | 3946 | if (value->mDamageArea.y > rows) { |
michael@0 | 3947 | value->mDamageArea.y = rows; |
michael@0 | 3948 | value->mDamageArea.height = 0; |
michael@0 | 3949 | } |
michael@0 | 3950 | else { |
michael@0 | 3951 | value->mDamageArea.height = rows - value->mDamageArea.y; |
michael@0 | 3952 | } |
michael@0 | 3953 | } |
michael@0 | 3954 | |
michael@0 | 3955 | // Construct a union of the new and old damage areas. |
michael@0 | 3956 | value->mDamageArea.UnionRect(value->mDamageArea, aValue); |
michael@0 | 3957 | } |
michael@0 | 3958 | } |
michael@0 | 3959 | |
michael@0 | 3960 | |
michael@0 | 3961 | void |
michael@0 | 3962 | nsTableFrame::SetFullBCDamageArea() |
michael@0 | 3963 | { |
michael@0 | 3964 | NS_ASSERTION(IsBorderCollapse(), "invalid SetFullBCDamageArea call"); |
michael@0 | 3965 | |
michael@0 | 3966 | SetNeedToCalcBCBorders(true); |
michael@0 | 3967 | |
michael@0 | 3968 | BCPropertyData* value = GetBCProperty(true); |
michael@0 | 3969 | if (value) { |
michael@0 | 3970 | value->mDamageArea = nsIntRect(0, 0, GetColCount(), GetRowCount()); |
michael@0 | 3971 | } |
michael@0 | 3972 | } |
michael@0 | 3973 | |
michael@0 | 3974 | |
michael@0 | 3975 | /* BCCellBorder represents a border segment which can be either a horizontal |
michael@0 | 3976 | * or a vertical segment. For each segment we need to know the color, width, |
michael@0 | 3977 | * style, who owns it and how long it is in cellmap coordinates. |
michael@0 | 3978 | * Ownership of these segments is important to calculate which corners should |
michael@0 | 3979 | * be bevelled. This structure has dual use, its used first to compute the |
michael@0 | 3980 | * dominant border for horizontal and vertical segments and to store the |
michael@0 | 3981 | * preliminary computed border results in the BCCellBorders structure. |
michael@0 | 3982 | * This temporary storage is not symmetric with respect to horizontal and |
michael@0 | 3983 | * vertical border segments, its always column oriented. For each column in |
michael@0 | 3984 | * the cellmap there is a temporary stored vertical and horizontal segment. |
michael@0 | 3985 | * XXX_Bernd this asymmetry is the root of those rowspan bc border errors |
michael@0 | 3986 | */ |
michael@0 | 3987 | struct BCCellBorder |
michael@0 | 3988 | { |
michael@0 | 3989 | BCCellBorder() { Reset(0, 1); } |
michael@0 | 3990 | void Reset(uint32_t aRowIndex, uint32_t aRowSpan); |
michael@0 | 3991 | nscolor color; // border segment color |
michael@0 | 3992 | BCPixelSize width; // border segment width in pixel coordinates !! |
michael@0 | 3993 | uint8_t style; // border segment style, possible values are defined |
michael@0 | 3994 | // in nsStyleConsts.h as NS_STYLE_BORDER_STYLE_* |
michael@0 | 3995 | BCBorderOwner owner; // border segment owner, possible values are defined |
michael@0 | 3996 | // in celldata.h. In the cellmap for each border |
michael@0 | 3997 | // segment we store the owner and later when |
michael@0 | 3998 | // painting we know the owner and can retrieve the |
michael@0 | 3999 | // style info from the corresponding frame |
michael@0 | 4000 | int32_t rowIndex; // rowIndex of temporary stored horizontal border |
michael@0 | 4001 | // segments relative to the table |
michael@0 | 4002 | int32_t rowSpan; // row span of temporary stored horizontal border |
michael@0 | 4003 | // segments |
michael@0 | 4004 | }; |
michael@0 | 4005 | |
michael@0 | 4006 | void |
michael@0 | 4007 | BCCellBorder::Reset(uint32_t aRowIndex, |
michael@0 | 4008 | uint32_t aRowSpan) |
michael@0 | 4009 | { |
michael@0 | 4010 | style = NS_STYLE_BORDER_STYLE_NONE; |
michael@0 | 4011 | color = 0; |
michael@0 | 4012 | width = 0; |
michael@0 | 4013 | owner = eTableOwner; |
michael@0 | 4014 | rowIndex = aRowIndex; |
michael@0 | 4015 | rowSpan = aRowSpan; |
michael@0 | 4016 | } |
michael@0 | 4017 | |
michael@0 | 4018 | class BCMapCellIterator; |
michael@0 | 4019 | |
michael@0 | 4020 | /***************************************************************** |
michael@0 | 4021 | * BCMapCellInfo |
michael@0 | 4022 | * This structure stores information about the cellmap and all involved |
michael@0 | 4023 | * table related frames that are used during the computation of winning borders |
michael@0 | 4024 | * in CalcBCBorders so that they do need to be looked up again and again when |
michael@0 | 4025 | * iterating over the cells. |
michael@0 | 4026 | ****************************************************************/ |
michael@0 | 4027 | struct BCMapCellInfo |
michael@0 | 4028 | { |
michael@0 | 4029 | BCMapCellInfo(nsTableFrame* aTableFrame); |
michael@0 | 4030 | void ResetCellInfo(); |
michael@0 | 4031 | void SetInfo(nsTableRowFrame* aNewRow, |
michael@0 | 4032 | int32_t aColIndex, |
michael@0 | 4033 | BCCellData* aCellData, |
michael@0 | 4034 | BCMapCellIterator* aIter, |
michael@0 | 4035 | nsCellMap* aCellMap = nullptr); |
michael@0 | 4036 | // The BCMapCellInfo has functions to set the continous |
michael@0 | 4037 | // border widths (see nsTablePainter.cpp for a description of the continous |
michael@0 | 4038 | // borders concept). The widths are computed inside these functions based on |
michael@0 | 4039 | // the current position inside the table and the cached frames that correspond |
michael@0 | 4040 | // to this position. The widths are stored in member variables of the internal |
michael@0 | 4041 | // table frames. |
michael@0 | 4042 | void SetTableTopLeftContBCBorder(); |
michael@0 | 4043 | void SetRowGroupLeftContBCBorder(); |
michael@0 | 4044 | void SetRowGroupRightContBCBorder(); |
michael@0 | 4045 | void SetRowGroupBottomContBCBorder(); |
michael@0 | 4046 | void SetRowLeftContBCBorder(); |
michael@0 | 4047 | void SetRowRightContBCBorder(); |
michael@0 | 4048 | void SetColumnTopRightContBCBorder(); |
michael@0 | 4049 | void SetColumnBottomContBCBorder(); |
michael@0 | 4050 | void SetColGroupBottomContBCBorder(); |
michael@0 | 4051 | void SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup, |
michael@0 | 4052 | nsTableRowFrame* aNextRow); |
michael@0 | 4053 | |
michael@0 | 4054 | // functions to set the border widths on the table related frames, where the |
michael@0 | 4055 | // knowledge about the current position in the table is used. |
michael@0 | 4056 | void SetTableTopBorderWidth(BCPixelSize aWidth); |
michael@0 | 4057 | void SetTableLeftBorderWidth(int32_t aRowY, BCPixelSize aWidth); |
michael@0 | 4058 | void SetTableRightBorderWidth(int32_t aRowY, BCPixelSize aWidth); |
michael@0 | 4059 | void SetTableBottomBorderWidth(BCPixelSize aWidth); |
michael@0 | 4060 | void SetLeftBorderWidths(BCPixelSize aWidth); |
michael@0 | 4061 | void SetRightBorderWidths(BCPixelSize aWidth); |
michael@0 | 4062 | void SetTopBorderWidths(BCPixelSize aWidth); |
michael@0 | 4063 | void SetBottomBorderWidths(BCPixelSize aWidth); |
michael@0 | 4064 | |
michael@0 | 4065 | // functions to compute the borders; they depend on the |
michael@0 | 4066 | // knowledge about the current position in the table. The edge functions |
michael@0 | 4067 | // should be called if a table edge is involved, otherwise the internal |
michael@0 | 4068 | // functions should be called. |
michael@0 | 4069 | BCCellBorder GetTopEdgeBorder(); |
michael@0 | 4070 | BCCellBorder GetBottomEdgeBorder(); |
michael@0 | 4071 | BCCellBorder GetLeftEdgeBorder(); |
michael@0 | 4072 | BCCellBorder GetRightEdgeBorder(); |
michael@0 | 4073 | BCCellBorder GetRightInternalBorder(); |
michael@0 | 4074 | BCCellBorder GetLeftInternalBorder(); |
michael@0 | 4075 | BCCellBorder GetTopInternalBorder(); |
michael@0 | 4076 | BCCellBorder GetBottomInternalBorder(); |
michael@0 | 4077 | |
michael@0 | 4078 | // functions to set the interal position information |
michael@0 | 4079 | void SetColumn(int32_t aColX); |
michael@0 | 4080 | // Increment the row as we loop over the rows of a rowspan |
michael@0 | 4081 | void IncrementRow(bool aResetToTopRowOfCell = false); |
michael@0 | 4082 | |
michael@0 | 4083 | // Helper functions to get extent of the cell |
michael@0 | 4084 | int32_t GetCellEndRowIndex() const; |
michael@0 | 4085 | int32_t GetCellEndColIndex() const; |
michael@0 | 4086 | |
michael@0 | 4087 | // storage of table information |
michael@0 | 4088 | nsTableFrame* mTableFrame; |
michael@0 | 4089 | int32_t mNumTableRows; |
michael@0 | 4090 | int32_t mNumTableCols; |
michael@0 | 4091 | BCPropertyData* mTableBCData; |
michael@0 | 4092 | |
michael@0 | 4093 | // storage of table ltr information, the border collapse code swaps the sides |
michael@0 | 4094 | // to account for rtl tables, this is done through mStartSide and mEndSide |
michael@0 | 4095 | bool mTableIsLTR; |
michael@0 | 4096 | mozilla::css::Side mStartSide; |
michael@0 | 4097 | mozilla::css::Side mEndSide; |
michael@0 | 4098 | |
michael@0 | 4099 | // a cell can only belong to one rowgroup |
michael@0 | 4100 | nsTableRowGroupFrame* mRowGroup; |
michael@0 | 4101 | |
michael@0 | 4102 | // a cell with a rowspan has a top and a bottom row, and rows in between |
michael@0 | 4103 | nsTableRowFrame* mTopRow; |
michael@0 | 4104 | nsTableRowFrame* mBottomRow; |
michael@0 | 4105 | nsTableRowFrame* mCurrentRowFrame; |
michael@0 | 4106 | |
michael@0 | 4107 | // a cell with a colspan has a left and right column and columns in between |
michael@0 | 4108 | // they can belong to different colgroups |
michael@0 | 4109 | nsTableColGroupFrame* mColGroup; |
michael@0 | 4110 | nsTableColGroupFrame* mCurrentColGroupFrame; |
michael@0 | 4111 | |
michael@0 | 4112 | nsTableColFrame* mLeftCol; |
michael@0 | 4113 | nsTableColFrame* mRightCol; |
michael@0 | 4114 | nsTableColFrame* mCurrentColFrame; |
michael@0 | 4115 | |
michael@0 | 4116 | // cell information |
michael@0 | 4117 | BCCellData* mCellData; |
michael@0 | 4118 | nsBCTableCellFrame* mCell; |
michael@0 | 4119 | |
michael@0 | 4120 | int32_t mRowIndex; |
michael@0 | 4121 | int32_t mRowSpan; |
michael@0 | 4122 | int32_t mColIndex; |
michael@0 | 4123 | int32_t mColSpan; |
michael@0 | 4124 | |
michael@0 | 4125 | // flags to describe the position of the cell with respect to the row- and |
michael@0 | 4126 | // colgroups, for instance mRgAtTop documents that the top cell border hits |
michael@0 | 4127 | // a rowgroup border |
michael@0 | 4128 | bool mRgAtTop; |
michael@0 | 4129 | bool mRgAtBottom; |
michael@0 | 4130 | bool mCgAtLeft; |
michael@0 | 4131 | bool mCgAtRight; |
michael@0 | 4132 | |
michael@0 | 4133 | }; |
michael@0 | 4134 | |
michael@0 | 4135 | |
michael@0 | 4136 | BCMapCellInfo::BCMapCellInfo(nsTableFrame* aTableFrame) |
michael@0 | 4137 | { |
michael@0 | 4138 | mTableFrame = aTableFrame; |
michael@0 | 4139 | mTableIsLTR = |
michael@0 | 4140 | aTableFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR; |
michael@0 | 4141 | if (mTableIsLTR) { |
michael@0 | 4142 | mStartSide = NS_SIDE_LEFT; |
michael@0 | 4143 | mEndSide = NS_SIDE_RIGHT; |
michael@0 | 4144 | } |
michael@0 | 4145 | else { |
michael@0 | 4146 | mStartSide = NS_SIDE_RIGHT; |
michael@0 | 4147 | mEndSide = NS_SIDE_LEFT; |
michael@0 | 4148 | } |
michael@0 | 4149 | mNumTableRows = mTableFrame->GetRowCount(); |
michael@0 | 4150 | mNumTableCols = mTableFrame->GetColCount(); |
michael@0 | 4151 | mTableBCData = static_cast<BCPropertyData*> |
michael@0 | 4152 | (mTableFrame->Properties().Get(TableBCProperty())); |
michael@0 | 4153 | |
michael@0 | 4154 | ResetCellInfo(); |
michael@0 | 4155 | } |
michael@0 | 4156 | |
michael@0 | 4157 | void BCMapCellInfo::ResetCellInfo() |
michael@0 | 4158 | { |
michael@0 | 4159 | mCellData = nullptr; |
michael@0 | 4160 | mRowGroup = nullptr; |
michael@0 | 4161 | mTopRow = nullptr; |
michael@0 | 4162 | mBottomRow = nullptr; |
michael@0 | 4163 | mColGroup = nullptr; |
michael@0 | 4164 | mLeftCol = nullptr; |
michael@0 | 4165 | mRightCol = nullptr; |
michael@0 | 4166 | mCell = nullptr; |
michael@0 | 4167 | mRowIndex = mRowSpan = mColIndex = mColSpan = 0; |
michael@0 | 4168 | mRgAtTop = mRgAtBottom = mCgAtLeft = mCgAtRight = false; |
michael@0 | 4169 | } |
michael@0 | 4170 | |
michael@0 | 4171 | inline int32_t BCMapCellInfo::GetCellEndRowIndex() const |
michael@0 | 4172 | { |
michael@0 | 4173 | return mRowIndex + mRowSpan - 1; |
michael@0 | 4174 | } |
michael@0 | 4175 | |
michael@0 | 4176 | inline int32_t BCMapCellInfo::GetCellEndColIndex() const |
michael@0 | 4177 | { |
michael@0 | 4178 | return mColIndex + mColSpan - 1; |
michael@0 | 4179 | } |
michael@0 | 4180 | |
michael@0 | 4181 | |
michael@0 | 4182 | class BCMapCellIterator |
michael@0 | 4183 | { |
michael@0 | 4184 | public: |
michael@0 | 4185 | BCMapCellIterator(nsTableFrame* aTableFrame, |
michael@0 | 4186 | const nsIntRect& aDamageArea); |
michael@0 | 4187 | |
michael@0 | 4188 | void First(BCMapCellInfo& aMapCellInfo); |
michael@0 | 4189 | |
michael@0 | 4190 | void Next(BCMapCellInfo& aMapCellInfo); |
michael@0 | 4191 | |
michael@0 | 4192 | void PeekRight(BCMapCellInfo& aRefInfo, |
michael@0 | 4193 | uint32_t aRowIndex, |
michael@0 | 4194 | BCMapCellInfo& aAjaInfo); |
michael@0 | 4195 | |
michael@0 | 4196 | void PeekBottom(BCMapCellInfo& aRefInfo, |
michael@0 | 4197 | uint32_t aColIndex, |
michael@0 | 4198 | BCMapCellInfo& aAjaInfo); |
michael@0 | 4199 | |
michael@0 | 4200 | bool IsNewRow() { return mIsNewRow; } |
michael@0 | 4201 | |
michael@0 | 4202 | nsTableRowFrame* GetPrevRow() const { return mPrevRow; } |
michael@0 | 4203 | nsTableRowFrame* GetCurrentRow() const { return mRow; } |
michael@0 | 4204 | nsTableRowGroupFrame* GetCurrentRowGroup() const { return mRowGroup;} |
michael@0 | 4205 | |
michael@0 | 4206 | int32_t mRowGroupStart; |
michael@0 | 4207 | int32_t mRowGroupEnd; |
michael@0 | 4208 | bool mAtEnd; |
michael@0 | 4209 | nsCellMap* mCellMap; |
michael@0 | 4210 | |
michael@0 | 4211 | private: |
michael@0 | 4212 | bool SetNewRow(nsTableRowFrame* row = nullptr); |
michael@0 | 4213 | bool SetNewRowGroup(bool aFindFirstDamagedRow); |
michael@0 | 4214 | |
michael@0 | 4215 | nsTableFrame* mTableFrame; |
michael@0 | 4216 | nsTableCellMap* mTableCellMap; |
michael@0 | 4217 | nsTableFrame::RowGroupArray mRowGroups; |
michael@0 | 4218 | nsTableRowGroupFrame* mRowGroup; |
michael@0 | 4219 | int32_t mRowGroupIndex; |
michael@0 | 4220 | uint32_t mNumTableRows; |
michael@0 | 4221 | nsTableRowFrame* mRow; |
michael@0 | 4222 | nsTableRowFrame* mPrevRow; |
michael@0 | 4223 | bool mIsNewRow; |
michael@0 | 4224 | int32_t mRowIndex; |
michael@0 | 4225 | uint32_t mNumTableCols; |
michael@0 | 4226 | int32_t mColIndex; |
michael@0 | 4227 | nsPoint mAreaStart; |
michael@0 | 4228 | nsPoint mAreaEnd; |
michael@0 | 4229 | }; |
michael@0 | 4230 | |
michael@0 | 4231 | BCMapCellIterator::BCMapCellIterator(nsTableFrame* aTableFrame, |
michael@0 | 4232 | const nsIntRect& aDamageArea) |
michael@0 | 4233 | :mTableFrame(aTableFrame) |
michael@0 | 4234 | { |
michael@0 | 4235 | mTableCellMap = aTableFrame->GetCellMap(); |
michael@0 | 4236 | |
michael@0 | 4237 | mAreaStart.x = aDamageArea.x; |
michael@0 | 4238 | mAreaStart.y = aDamageArea.y; |
michael@0 | 4239 | mAreaEnd.y = aDamageArea.y + aDamageArea.height - 1; |
michael@0 | 4240 | mAreaEnd.x = aDamageArea.x + aDamageArea.width - 1; |
michael@0 | 4241 | |
michael@0 | 4242 | mNumTableRows = mTableFrame->GetRowCount(); |
michael@0 | 4243 | mRow = nullptr; |
michael@0 | 4244 | mRowIndex = 0; |
michael@0 | 4245 | mNumTableCols = mTableFrame->GetColCount(); |
michael@0 | 4246 | mColIndex = 0; |
michael@0 | 4247 | mRowGroupIndex = -1; |
michael@0 | 4248 | |
michael@0 | 4249 | // Get the ordered row groups |
michael@0 | 4250 | aTableFrame->OrderRowGroups(mRowGroups); |
michael@0 | 4251 | |
michael@0 | 4252 | mAtEnd = true; // gets reset when First() is called |
michael@0 | 4253 | } |
michael@0 | 4254 | |
michael@0 | 4255 | // fill fields that we need for border collapse computation on a given cell |
michael@0 | 4256 | void |
michael@0 | 4257 | BCMapCellInfo::SetInfo(nsTableRowFrame* aNewRow, |
michael@0 | 4258 | int32_t aColIndex, |
michael@0 | 4259 | BCCellData* aCellData, |
michael@0 | 4260 | BCMapCellIterator* aIter, |
michael@0 | 4261 | nsCellMap* aCellMap) |
michael@0 | 4262 | { |
michael@0 | 4263 | // fill the cell information |
michael@0 | 4264 | mCellData = aCellData; |
michael@0 | 4265 | mColIndex = aColIndex; |
michael@0 | 4266 | |
michael@0 | 4267 | // initialize the row information if it was not previously set for cells in |
michael@0 | 4268 | // this row |
michael@0 | 4269 | mRowIndex = 0; |
michael@0 | 4270 | if (aNewRow) { |
michael@0 | 4271 | mTopRow = aNewRow; |
michael@0 | 4272 | mRowIndex = aNewRow->GetRowIndex(); |
michael@0 | 4273 | } |
michael@0 | 4274 | |
michael@0 | 4275 | // fill cell frame info and row information |
michael@0 | 4276 | mCell = nullptr; |
michael@0 | 4277 | mRowSpan = 1; |
michael@0 | 4278 | mColSpan = 1; |
michael@0 | 4279 | if (aCellData) { |
michael@0 | 4280 | mCell = static_cast<nsBCTableCellFrame*>(aCellData->GetCellFrame()); |
michael@0 | 4281 | if (mCell) { |
michael@0 | 4282 | if (!mTopRow) { |
michael@0 | 4283 | mTopRow = static_cast<nsTableRowFrame*>(mCell->GetParent()); |
michael@0 | 4284 | if (!mTopRow) ABORT0(); |
michael@0 | 4285 | mRowIndex = mTopRow->GetRowIndex(); |
michael@0 | 4286 | } |
michael@0 | 4287 | mColSpan = mTableFrame->GetEffectiveColSpan(*mCell, aCellMap); |
michael@0 | 4288 | mRowSpan = mTableFrame->GetEffectiveRowSpan(*mCell, aCellMap); |
michael@0 | 4289 | } |
michael@0 | 4290 | } |
michael@0 | 4291 | |
michael@0 | 4292 | if (!mTopRow) { |
michael@0 | 4293 | mTopRow = aIter->GetCurrentRow(); |
michael@0 | 4294 | } |
michael@0 | 4295 | if (1 == mRowSpan) { |
michael@0 | 4296 | mBottomRow = mTopRow; |
michael@0 | 4297 | } |
michael@0 | 4298 | else { |
michael@0 | 4299 | mBottomRow = mTopRow->GetNextRow(); |
michael@0 | 4300 | if (mBottomRow) { |
michael@0 | 4301 | for (int32_t spanY = 2; mBottomRow && (spanY < mRowSpan); spanY++) { |
michael@0 | 4302 | mBottomRow = mBottomRow->GetNextRow(); |
michael@0 | 4303 | } |
michael@0 | 4304 | NS_ASSERTION(mBottomRow, "spanned row not found"); |
michael@0 | 4305 | } |
michael@0 | 4306 | else { |
michael@0 | 4307 | NS_ASSERTION(false, "error in cell map"); |
michael@0 | 4308 | mRowSpan = 1; |
michael@0 | 4309 | mBottomRow = mTopRow; |
michael@0 | 4310 | } |
michael@0 | 4311 | } |
michael@0 | 4312 | // row group frame info |
michael@0 | 4313 | // try to reuse the rgStart and rgEnd from the iterator as calls to |
michael@0 | 4314 | // GetRowCount() are computationally expensive and should be avoided if |
michael@0 | 4315 | // possible |
michael@0 | 4316 | uint32_t rgStart = aIter->mRowGroupStart; |
michael@0 | 4317 | uint32_t rgEnd = aIter->mRowGroupEnd; |
michael@0 | 4318 | mRowGroup = static_cast<nsTableRowGroupFrame*>(mTopRow->GetParent()); |
michael@0 | 4319 | if (mRowGroup != aIter->GetCurrentRowGroup()) { |
michael@0 | 4320 | rgStart = mRowGroup->GetStartRowIndex(); |
michael@0 | 4321 | rgEnd = rgStart + mRowGroup->GetRowCount() - 1; |
michael@0 | 4322 | } |
michael@0 | 4323 | uint32_t rowIndex = mTopRow->GetRowIndex(); |
michael@0 | 4324 | mRgAtTop = (rgStart == rowIndex); |
michael@0 | 4325 | mRgAtBottom = (rgEnd == rowIndex + mRowSpan - 1); |
michael@0 | 4326 | |
michael@0 | 4327 | // col frame info |
michael@0 | 4328 | mLeftCol = mTableFrame->GetColFrame(aColIndex); |
michael@0 | 4329 | if (!mLeftCol) ABORT0(); |
michael@0 | 4330 | |
michael@0 | 4331 | mRightCol = mLeftCol; |
michael@0 | 4332 | if (mColSpan > 1) { |
michael@0 | 4333 | nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + |
michael@0 | 4334 | mColSpan -1); |
michael@0 | 4335 | if (!colFrame) ABORT0(); |
michael@0 | 4336 | mRightCol = colFrame; |
michael@0 | 4337 | } |
michael@0 | 4338 | |
michael@0 | 4339 | // col group frame info |
michael@0 | 4340 | mColGroup = static_cast<nsTableColGroupFrame*>(mLeftCol->GetParent()); |
michael@0 | 4341 | int32_t cgStart = mColGroup->GetStartColumnIndex(); |
michael@0 | 4342 | int32_t cgEnd = std::max(0, cgStart + mColGroup->GetColCount() - 1); |
michael@0 | 4343 | mCgAtLeft = (cgStart == aColIndex); |
michael@0 | 4344 | mCgAtRight = (cgEnd == aColIndex + mColSpan - 1); |
michael@0 | 4345 | } |
michael@0 | 4346 | |
michael@0 | 4347 | bool |
michael@0 | 4348 | BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow) |
michael@0 | 4349 | { |
michael@0 | 4350 | mAtEnd = true; |
michael@0 | 4351 | mPrevRow = mRow; |
michael@0 | 4352 | if (aRow) { |
michael@0 | 4353 | mRow = aRow; |
michael@0 | 4354 | } |
michael@0 | 4355 | else if (mRow) { |
michael@0 | 4356 | mRow = mRow->GetNextRow(); |
michael@0 | 4357 | } |
michael@0 | 4358 | if (mRow) { |
michael@0 | 4359 | mRowIndex = mRow->GetRowIndex(); |
michael@0 | 4360 | // get to the first entry with an originating cell |
michael@0 | 4361 | int32_t rgRowIndex = mRowIndex - mRowGroupStart; |
michael@0 | 4362 | if (uint32_t(rgRowIndex) >= mCellMap->mRows.Length()) |
michael@0 | 4363 | ABORT1(false); |
michael@0 | 4364 | const nsCellMap::CellDataArray& row = mCellMap->mRows[rgRowIndex]; |
michael@0 | 4365 | |
michael@0 | 4366 | for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) { |
michael@0 | 4367 | CellData* cellData = row.SafeElementAt(mColIndex); |
michael@0 | 4368 | if (!cellData) { // add a dead cell data |
michael@0 | 4369 | nsIntRect damageArea; |
michael@0 | 4370 | cellData = mCellMap->AppendCell(*mTableCellMap, nullptr, rgRowIndex, |
michael@0 | 4371 | false, 0, damageArea); |
michael@0 | 4372 | if (!cellData) ABORT1(false); |
michael@0 | 4373 | } |
michael@0 | 4374 | if (cellData && (cellData->IsOrig() || cellData->IsDead())) { |
michael@0 | 4375 | break; |
michael@0 | 4376 | } |
michael@0 | 4377 | } |
michael@0 | 4378 | mIsNewRow = true; |
michael@0 | 4379 | mAtEnd = false; |
michael@0 | 4380 | } |
michael@0 | 4381 | else ABORT1(false); |
michael@0 | 4382 | |
michael@0 | 4383 | return !mAtEnd; |
michael@0 | 4384 | } |
michael@0 | 4385 | |
michael@0 | 4386 | bool |
michael@0 | 4387 | BCMapCellIterator::SetNewRowGroup(bool aFindFirstDamagedRow) |
michael@0 | 4388 | { |
michael@0 | 4389 | mAtEnd = true; |
michael@0 | 4390 | int32_t numRowGroups = mRowGroups.Length(); |
michael@0 | 4391 | mCellMap = nullptr; |
michael@0 | 4392 | for (mRowGroupIndex++; mRowGroupIndex < numRowGroups; mRowGroupIndex++) { |
michael@0 | 4393 | mRowGroup = mRowGroups[mRowGroupIndex]; |
michael@0 | 4394 | int32_t rowCount = mRowGroup->GetRowCount(); |
michael@0 | 4395 | mRowGroupStart = mRowGroup->GetStartRowIndex(); |
michael@0 | 4396 | mRowGroupEnd = mRowGroupStart + rowCount - 1; |
michael@0 | 4397 | if (rowCount > 0) { |
michael@0 | 4398 | mCellMap = mTableCellMap->GetMapFor(mRowGroup, mCellMap); |
michael@0 | 4399 | if (!mCellMap) ABORT1(false); |
michael@0 | 4400 | nsTableRowFrame* firstRow = mRowGroup->GetFirstRow(); |
michael@0 | 4401 | if (aFindFirstDamagedRow) { |
michael@0 | 4402 | if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) { |
michael@0 | 4403 | // the damage area starts in the row group |
michael@0 | 4404 | if (aFindFirstDamagedRow) { |
michael@0 | 4405 | // find the correct first damaged row |
michael@0 | 4406 | int32_t numRows = mAreaStart.y - mRowGroupStart; |
michael@0 | 4407 | for (int32_t i = 0; i < numRows; i++) { |
michael@0 | 4408 | firstRow = firstRow->GetNextRow(); |
michael@0 | 4409 | if (!firstRow) ABORT1(false); |
michael@0 | 4410 | } |
michael@0 | 4411 | } |
michael@0 | 4412 | } |
michael@0 | 4413 | else { |
michael@0 | 4414 | continue; |
michael@0 | 4415 | } |
michael@0 | 4416 | } |
michael@0 | 4417 | if (SetNewRow(firstRow)) { // sets mAtEnd |
michael@0 | 4418 | break; |
michael@0 | 4419 | } |
michael@0 | 4420 | } |
michael@0 | 4421 | } |
michael@0 | 4422 | |
michael@0 | 4423 | return !mAtEnd; |
michael@0 | 4424 | } |
michael@0 | 4425 | |
michael@0 | 4426 | void |
michael@0 | 4427 | BCMapCellIterator::First(BCMapCellInfo& aMapInfo) |
michael@0 | 4428 | { |
michael@0 | 4429 | aMapInfo.ResetCellInfo(); |
michael@0 | 4430 | |
michael@0 | 4431 | SetNewRowGroup(true); // sets mAtEnd |
michael@0 | 4432 | while (!mAtEnd) { |
michael@0 | 4433 | if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) { |
michael@0 | 4434 | BCCellData* cellData = |
michael@0 | 4435 | static_cast<BCCellData*>(mCellMap->GetDataAt(mAreaStart.y - |
michael@0 | 4436 | mRowGroupStart, |
michael@0 | 4437 | mAreaStart.x)); |
michael@0 | 4438 | if (cellData && (cellData->IsOrig() || cellData->IsDead())) { |
michael@0 | 4439 | aMapInfo.SetInfo(mRow, mAreaStart.x, cellData, this); |
michael@0 | 4440 | return; |
michael@0 | 4441 | } |
michael@0 | 4442 | else { |
michael@0 | 4443 | NS_ASSERTION(((0 == mAreaStart.x) && (mRowGroupStart == mAreaStart.y)) , |
michael@0 | 4444 | "damage area expanded incorrectly"); |
michael@0 | 4445 | } |
michael@0 | 4446 | } |
michael@0 | 4447 | SetNewRowGroup(true); // sets mAtEnd |
michael@0 | 4448 | } |
michael@0 | 4449 | } |
michael@0 | 4450 | |
michael@0 | 4451 | void |
michael@0 | 4452 | BCMapCellIterator::Next(BCMapCellInfo& aMapInfo) |
michael@0 | 4453 | { |
michael@0 | 4454 | if (mAtEnd) ABORT0(); |
michael@0 | 4455 | aMapInfo.ResetCellInfo(); |
michael@0 | 4456 | |
michael@0 | 4457 | mIsNewRow = false; |
michael@0 | 4458 | mColIndex++; |
michael@0 | 4459 | while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) { |
michael@0 | 4460 | for (; mColIndex <= mAreaEnd.x; mColIndex++) { |
michael@0 | 4461 | int32_t rgRowIndex = mRowIndex - mRowGroupStart; |
michael@0 | 4462 | BCCellData* cellData = |
michael@0 | 4463 | static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, mColIndex)); |
michael@0 | 4464 | if (!cellData) { // add a dead cell data |
michael@0 | 4465 | nsIntRect damageArea; |
michael@0 | 4466 | cellData = |
michael@0 | 4467 | static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr, |
michael@0 | 4468 | rgRowIndex, false, 0, |
michael@0 | 4469 | damageArea)); |
michael@0 | 4470 | if (!cellData) ABORT0(); |
michael@0 | 4471 | } |
michael@0 | 4472 | if (cellData && (cellData->IsOrig() || cellData->IsDead())) { |
michael@0 | 4473 | aMapInfo.SetInfo(mRow, mColIndex, cellData, this); |
michael@0 | 4474 | return; |
michael@0 | 4475 | } |
michael@0 | 4476 | } |
michael@0 | 4477 | if (mRowIndex >= mRowGroupEnd) { |
michael@0 | 4478 | SetNewRowGroup(false); // could set mAtEnd |
michael@0 | 4479 | } |
michael@0 | 4480 | else { |
michael@0 | 4481 | SetNewRow(); // could set mAtEnd |
michael@0 | 4482 | } |
michael@0 | 4483 | } |
michael@0 | 4484 | mAtEnd = true; |
michael@0 | 4485 | } |
michael@0 | 4486 | |
michael@0 | 4487 | void |
michael@0 | 4488 | BCMapCellIterator::PeekRight(BCMapCellInfo& aRefInfo, |
michael@0 | 4489 | uint32_t aRowIndex, |
michael@0 | 4490 | BCMapCellInfo& aAjaInfo) |
michael@0 | 4491 | { |
michael@0 | 4492 | aAjaInfo.ResetCellInfo(); |
michael@0 | 4493 | int32_t colIndex = aRefInfo.mColIndex + aRefInfo.mColSpan; |
michael@0 | 4494 | uint32_t rgRowIndex = aRowIndex - mRowGroupStart; |
michael@0 | 4495 | |
michael@0 | 4496 | BCCellData* cellData = |
michael@0 | 4497 | static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex)); |
michael@0 | 4498 | if (!cellData) { // add a dead cell data |
michael@0 | 4499 | NS_ASSERTION(colIndex < mTableCellMap->GetColCount(), "program error"); |
michael@0 | 4500 | nsIntRect damageArea; |
michael@0 | 4501 | cellData = |
michael@0 | 4502 | static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr, |
michael@0 | 4503 | rgRowIndex, false, 0, |
michael@0 | 4504 | damageArea)); |
michael@0 | 4505 | if (!cellData) ABORT0(); |
michael@0 | 4506 | } |
michael@0 | 4507 | nsTableRowFrame* row = nullptr; |
michael@0 | 4508 | if (cellData->IsRowSpan()) { |
michael@0 | 4509 | rgRowIndex -= cellData->GetRowSpanOffset(); |
michael@0 | 4510 | cellData = |
michael@0 | 4511 | static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex)); |
michael@0 | 4512 | if (!cellData) |
michael@0 | 4513 | ABORT0(); |
michael@0 | 4514 | } |
michael@0 | 4515 | else { |
michael@0 | 4516 | row = mRow; |
michael@0 | 4517 | } |
michael@0 | 4518 | aAjaInfo.SetInfo(row, colIndex, cellData, this); |
michael@0 | 4519 | } |
michael@0 | 4520 | |
michael@0 | 4521 | void |
michael@0 | 4522 | BCMapCellIterator::PeekBottom(BCMapCellInfo& aRefInfo, |
michael@0 | 4523 | uint32_t aColIndex, |
michael@0 | 4524 | BCMapCellInfo& aAjaInfo) |
michael@0 | 4525 | { |
michael@0 | 4526 | aAjaInfo.ResetCellInfo(); |
michael@0 | 4527 | int32_t rowIndex = aRefInfo.mRowIndex + aRefInfo.mRowSpan; |
michael@0 | 4528 | int32_t rgRowIndex = rowIndex - mRowGroupStart; |
michael@0 | 4529 | nsTableRowGroupFrame* rg = mRowGroup; |
michael@0 | 4530 | nsCellMap* cellMap = mCellMap; |
michael@0 | 4531 | nsTableRowFrame* nextRow = nullptr; |
michael@0 | 4532 | if (rowIndex > mRowGroupEnd) { |
michael@0 | 4533 | int32_t nextRgIndex = mRowGroupIndex; |
michael@0 | 4534 | do { |
michael@0 | 4535 | nextRgIndex++; |
michael@0 | 4536 | rg = mRowGroups.SafeElementAt(nextRgIndex); |
michael@0 | 4537 | if (rg) { |
michael@0 | 4538 | cellMap = mTableCellMap->GetMapFor(rg, cellMap); if (!cellMap) ABORT0(); |
michael@0 | 4539 | rgRowIndex = 0; |
michael@0 | 4540 | nextRow = rg->GetFirstRow(); |
michael@0 | 4541 | } |
michael@0 | 4542 | } |
michael@0 | 4543 | while (rg && !nextRow); |
michael@0 | 4544 | if(!rg) return; |
michael@0 | 4545 | } |
michael@0 | 4546 | else { |
michael@0 | 4547 | // get the row within the same row group |
michael@0 | 4548 | nextRow = mRow; |
michael@0 | 4549 | for (int32_t i = 0; i < aRefInfo.mRowSpan; i++) { |
michael@0 | 4550 | nextRow = nextRow->GetNextRow(); if (!nextRow) ABORT0(); |
michael@0 | 4551 | } |
michael@0 | 4552 | } |
michael@0 | 4553 | |
michael@0 | 4554 | BCCellData* cellData = |
michael@0 | 4555 | static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex)); |
michael@0 | 4556 | if (!cellData) { // add a dead cell data |
michael@0 | 4557 | NS_ASSERTION(rgRowIndex < cellMap->GetRowCount(), "program error"); |
michael@0 | 4558 | nsIntRect damageArea; |
michael@0 | 4559 | cellData = |
michael@0 | 4560 | static_cast<BCCellData*>(cellMap->AppendCell(*mTableCellMap, nullptr, |
michael@0 | 4561 | rgRowIndex, false, 0, |
michael@0 | 4562 | damageArea)); |
michael@0 | 4563 | if (!cellData) ABORT0(); |
michael@0 | 4564 | } |
michael@0 | 4565 | if (cellData->IsColSpan()) { |
michael@0 | 4566 | aColIndex -= cellData->GetColSpanOffset(); |
michael@0 | 4567 | cellData = |
michael@0 | 4568 | static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex)); |
michael@0 | 4569 | } |
michael@0 | 4570 | aAjaInfo.SetInfo(nextRow, aColIndex, cellData, this, cellMap); |
michael@0 | 4571 | } |
michael@0 | 4572 | |
michael@0 | 4573 | // Assign priorities to border styles. For example, styleToPriority(NS_STYLE_BORDER_STYLE_SOLID) |
michael@0 | 4574 | // will return the priority of NS_STYLE_BORDER_STYLE_SOLID. |
michael@0 | 4575 | static uint8_t styleToPriority[13] = { 0, // NS_STYLE_BORDER_STYLE_NONE |
michael@0 | 4576 | 2, // NS_STYLE_BORDER_STYLE_GROOVE |
michael@0 | 4577 | 4, // NS_STYLE_BORDER_STYLE_RIDGE |
michael@0 | 4578 | 5, // NS_STYLE_BORDER_STYLE_DOTTED |
michael@0 | 4579 | 6, // NS_STYLE_BORDER_STYLE_DASHED |
michael@0 | 4580 | 7, // NS_STYLE_BORDER_STYLE_SOLID |
michael@0 | 4581 | 8, // NS_STYLE_BORDER_STYLE_DOUBLE |
michael@0 | 4582 | 1, // NS_STYLE_BORDER_STYLE_INSET |
michael@0 | 4583 | 3, // NS_STYLE_BORDER_STYLE_OUTSET |
michael@0 | 4584 | 9 };// NS_STYLE_BORDER_STYLE_HIDDEN |
michael@0 | 4585 | // priority rules follow CSS 2.1 spec |
michael@0 | 4586 | // 'hidden', 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', |
michael@0 | 4587 | // and the lowest: 'inset'. none is even weaker |
michael@0 | 4588 | #define CELL_CORNER true |
michael@0 | 4589 | |
michael@0 | 4590 | /** return the border style, border color for a given frame and side |
michael@0 | 4591 | * @param aFrame - query the info for this frame |
michael@0 | 4592 | * @param aSide - the side of the frame |
michael@0 | 4593 | * @param aStyle - the border style |
michael@0 | 4594 | * @param aColor - the border color |
michael@0 | 4595 | * @param aTableIsLTR - table direction is LTR |
michael@0 | 4596 | */ |
michael@0 | 4597 | static void |
michael@0 | 4598 | GetColorAndStyle(const nsIFrame* aFrame, |
michael@0 | 4599 | mozilla::css::Side aSide, |
michael@0 | 4600 | uint8_t& aStyle, |
michael@0 | 4601 | nscolor& aColor, |
michael@0 | 4602 | bool aTableIsLTR) |
michael@0 | 4603 | { |
michael@0 | 4604 | NS_PRECONDITION(aFrame, "null frame"); |
michael@0 | 4605 | // initialize out arg |
michael@0 | 4606 | aColor = 0; |
michael@0 | 4607 | const nsStyleBorder* styleData = aFrame->StyleBorder(); |
michael@0 | 4608 | if(!aTableIsLTR) { // revert the directions |
michael@0 | 4609 | if (NS_SIDE_RIGHT == aSide) { |
michael@0 | 4610 | aSide = NS_SIDE_LEFT; |
michael@0 | 4611 | } |
michael@0 | 4612 | else if (NS_SIDE_LEFT == aSide) { |
michael@0 | 4613 | aSide = NS_SIDE_RIGHT; |
michael@0 | 4614 | } |
michael@0 | 4615 | } |
michael@0 | 4616 | aStyle = styleData->GetBorderStyle(aSide); |
michael@0 | 4617 | |
michael@0 | 4618 | if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) || |
michael@0 | 4619 | (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) { |
michael@0 | 4620 | return; |
michael@0 | 4621 | } |
michael@0 | 4622 | aColor = aFrame->StyleContext()->GetVisitedDependentColor( |
michael@0 | 4623 | nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[aSide]); |
michael@0 | 4624 | } |
michael@0 | 4625 | |
michael@0 | 4626 | /** coerce the paint style as required by CSS2.1 |
michael@0 | 4627 | * @param aFrame - query the info for this frame |
michael@0 | 4628 | * @param aSide - the side of the frame |
michael@0 | 4629 | * @param aStyle - the border style |
michael@0 | 4630 | * @param aColor - the border color |
michael@0 | 4631 | * @param aTableIsLTR - table direction is LTR |
michael@0 | 4632 | */ |
michael@0 | 4633 | static void |
michael@0 | 4634 | GetPaintStyleInfo(const nsIFrame* aFrame, |
michael@0 | 4635 | mozilla::css::Side aSide, |
michael@0 | 4636 | uint8_t& aStyle, |
michael@0 | 4637 | nscolor& aColor, |
michael@0 | 4638 | bool aTableIsLTR) |
michael@0 | 4639 | { |
michael@0 | 4640 | GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR); |
michael@0 | 4641 | if (NS_STYLE_BORDER_STYLE_INSET == aStyle) { |
michael@0 | 4642 | aStyle = NS_STYLE_BORDER_STYLE_RIDGE; |
michael@0 | 4643 | } |
michael@0 | 4644 | else if (NS_STYLE_BORDER_STYLE_OUTSET == aStyle) { |
michael@0 | 4645 | aStyle = NS_STYLE_BORDER_STYLE_GROOVE; |
michael@0 | 4646 | } |
michael@0 | 4647 | } |
michael@0 | 4648 | |
michael@0 | 4649 | /** return the border style, border color and the width in pixel for a given |
michael@0 | 4650 | * frame and side |
michael@0 | 4651 | * @param aFrame - query the info for this frame |
michael@0 | 4652 | * @param aSide - the side of the frame |
michael@0 | 4653 | * @param aStyle - the border style |
michael@0 | 4654 | * @param aColor - the border color |
michael@0 | 4655 | * @param aTableIsLTR - table direction is LTR |
michael@0 | 4656 | * @param aWidth - the border width in px. |
michael@0 | 4657 | * @param aTwipsToPixels - conversion factor from twips to pixel |
michael@0 | 4658 | */ |
michael@0 | 4659 | static void |
michael@0 | 4660 | GetColorAndStyle(const nsIFrame* aFrame, |
michael@0 | 4661 | mozilla::css::Side aSide, |
michael@0 | 4662 | uint8_t& aStyle, |
michael@0 | 4663 | nscolor& aColor, |
michael@0 | 4664 | bool aTableIsLTR, |
michael@0 | 4665 | BCPixelSize& aWidth) |
michael@0 | 4666 | { |
michael@0 | 4667 | GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR); |
michael@0 | 4668 | if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) || |
michael@0 | 4669 | (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) { |
michael@0 | 4670 | aWidth = 0; |
michael@0 | 4671 | return; |
michael@0 | 4672 | } |
michael@0 | 4673 | const nsStyleBorder* styleData = aFrame->StyleBorder(); |
michael@0 | 4674 | nscoord width; |
michael@0 | 4675 | if(!aTableIsLTR) { // revert the directions |
michael@0 | 4676 | if (NS_SIDE_RIGHT == aSide) { |
michael@0 | 4677 | aSide = NS_SIDE_LEFT; |
michael@0 | 4678 | } |
michael@0 | 4679 | else if (NS_SIDE_LEFT == aSide) { |
michael@0 | 4680 | aSide = NS_SIDE_RIGHT; |
michael@0 | 4681 | } |
michael@0 | 4682 | } |
michael@0 | 4683 | width = styleData->GetComputedBorderWidth(aSide); |
michael@0 | 4684 | aWidth = nsPresContext::AppUnitsToIntCSSPixels(width); |
michael@0 | 4685 | } |
michael@0 | 4686 | |
michael@0 | 4687 | class nsDelayedCalcBCBorders : public nsRunnable { |
michael@0 | 4688 | public: |
michael@0 | 4689 | nsDelayedCalcBCBorders(nsIFrame* aFrame) : |
michael@0 | 4690 | mFrame(aFrame) {} |
michael@0 | 4691 | |
michael@0 | 4692 | NS_IMETHOD Run() MOZ_OVERRIDE { |
michael@0 | 4693 | if (mFrame) { |
michael@0 | 4694 | nsTableFrame* tableFrame = static_cast <nsTableFrame*>(mFrame.GetFrame()); |
michael@0 | 4695 | if (tableFrame->NeedToCalcBCBorders()) { |
michael@0 | 4696 | tableFrame->CalcBCBorders(); |
michael@0 | 4697 | } |
michael@0 | 4698 | } |
michael@0 | 4699 | return NS_OK; |
michael@0 | 4700 | } |
michael@0 | 4701 | private: |
michael@0 | 4702 | nsWeakFrame mFrame; |
michael@0 | 4703 | }; |
michael@0 | 4704 | |
michael@0 | 4705 | bool |
michael@0 | 4706 | nsTableFrame::BCRecalcNeeded(nsStyleContext* aOldStyleContext, |
michael@0 | 4707 | nsStyleContext* aNewStyleContext) |
michael@0 | 4708 | { |
michael@0 | 4709 | // Attention: the old style context is the one we're forgetting, |
michael@0 | 4710 | // and hence possibly completely bogus for GetStyle* purposes. |
michael@0 | 4711 | // We use PeekStyleData instead. |
michael@0 | 4712 | |
michael@0 | 4713 | const nsStyleBorder* oldStyleData = aOldStyleContext->PeekStyleBorder(); |
michael@0 | 4714 | if (!oldStyleData) |
michael@0 | 4715 | return false; |
michael@0 | 4716 | |
michael@0 | 4717 | const nsStyleBorder* newStyleData = aNewStyleContext->StyleBorder(); |
michael@0 | 4718 | nsChangeHint change = newStyleData->CalcDifference(*oldStyleData); |
michael@0 | 4719 | if (!change) |
michael@0 | 4720 | return false; |
michael@0 | 4721 | if (change & nsChangeHint_NeedReflow) |
michael@0 | 4722 | return true; // the caller only needs to mark the bc damage area |
michael@0 | 4723 | if (change & nsChangeHint_RepaintFrame) { |
michael@0 | 4724 | // we need to recompute the borders and the caller needs to mark |
michael@0 | 4725 | // the bc damage area |
michael@0 | 4726 | // XXX In principle this should only be necessary for border style changes |
michael@0 | 4727 | // However the bc painting code tries to maximize the drawn border segments |
michael@0 | 4728 | // so it stores in the cellmap where a new border segment starts and this |
michael@0 | 4729 | // introduces a unwanted cellmap data dependence on color |
michael@0 | 4730 | nsCOMPtr<nsIRunnable> evt = new nsDelayedCalcBCBorders(this); |
michael@0 | 4731 | NS_DispatchToCurrentThread(evt); |
michael@0 | 4732 | return true; |
michael@0 | 4733 | } |
michael@0 | 4734 | return false; |
michael@0 | 4735 | } |
michael@0 | 4736 | |
michael@0 | 4737 | |
michael@0 | 4738 | // Compare two border segments, this comparison depends whether the two |
michael@0 | 4739 | // segments meet at a corner and whether the second segment is horizontal. |
michael@0 | 4740 | // The return value is whichever of aBorder1 or aBorder2 dominates. |
michael@0 | 4741 | static const BCCellBorder& |
michael@0 | 4742 | CompareBorders(bool aIsCorner, // Pass true for corner calculations |
michael@0 | 4743 | const BCCellBorder& aBorder1, |
michael@0 | 4744 | const BCCellBorder& aBorder2, |
michael@0 | 4745 | bool aSecondIsHorizontal, |
michael@0 | 4746 | bool* aFirstDominates = nullptr) |
michael@0 | 4747 | { |
michael@0 | 4748 | bool firstDominates = true; |
michael@0 | 4749 | |
michael@0 | 4750 | if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder1.style) { |
michael@0 | 4751 | firstDominates = (aIsCorner) ? false : true; |
michael@0 | 4752 | } |
michael@0 | 4753 | else if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder2.style) { |
michael@0 | 4754 | firstDominates = (aIsCorner) ? true : false; |
michael@0 | 4755 | } |
michael@0 | 4756 | else if (aBorder1.width < aBorder2.width) { |
michael@0 | 4757 | firstDominates = false; |
michael@0 | 4758 | } |
michael@0 | 4759 | else if (aBorder1.width == aBorder2.width) { |
michael@0 | 4760 | if (styleToPriority[aBorder1.style] < styleToPriority[aBorder2.style]) { |
michael@0 | 4761 | firstDominates = false; |
michael@0 | 4762 | } |
michael@0 | 4763 | else if (styleToPriority[aBorder1.style] == styleToPriority[aBorder2.style]) { |
michael@0 | 4764 | if (aBorder1.owner == aBorder2.owner) { |
michael@0 | 4765 | firstDominates = !aSecondIsHorizontal; |
michael@0 | 4766 | } |
michael@0 | 4767 | else if (aBorder1.owner < aBorder2.owner) { |
michael@0 | 4768 | firstDominates = false; |
michael@0 | 4769 | } |
michael@0 | 4770 | } |
michael@0 | 4771 | } |
michael@0 | 4772 | |
michael@0 | 4773 | if (aFirstDominates) |
michael@0 | 4774 | *aFirstDominates = firstDominates; |
michael@0 | 4775 | |
michael@0 | 4776 | if (firstDominates) |
michael@0 | 4777 | return aBorder1; |
michael@0 | 4778 | return aBorder2; |
michael@0 | 4779 | } |
michael@0 | 4780 | |
michael@0 | 4781 | /** calc the dominant border by considering the table, row/col group, row/col, |
michael@0 | 4782 | * cell. |
michael@0 | 4783 | * Depending on whether the side is vertical or horizontal and whether |
michael@0 | 4784 | * adjacent frames are taken into account the ownership of a single border |
michael@0 | 4785 | * segment is defined. The return value is the dominating border |
michael@0 | 4786 | * The cellmap stores only top and left borders for each cellmap position. |
michael@0 | 4787 | * If the cell border is owned by the cell that is left of the border |
michael@0 | 4788 | * it will be an adjacent owner aka eAjaCellOwner. See celldata.h for the other |
michael@0 | 4789 | * scenarios with a adjacent owner. |
michael@0 | 4790 | * @param xxxFrame - the frame for style information, might be zero if |
michael@0 | 4791 | * it should not be considered |
michael@0 | 4792 | * @param aSide - side of the frames that should be considered |
michael@0 | 4793 | * @param aAja - the border comparison takes place from the point of |
michael@0 | 4794 | * a frame that is adjacent to the cellmap entry, for |
michael@0 | 4795 | * when a cell owns its lower border it will be the |
michael@0 | 4796 | * adjacent owner as in the cellmap only top and left |
michael@0 | 4797 | * borders are stored. |
michael@0 | 4798 | * @param aTwipsToPixels - conversion factor as borders need to be drawn pixel |
michael@0 | 4799 | * aligned. |
michael@0 | 4800 | */ |
michael@0 | 4801 | static BCCellBorder |
michael@0 | 4802 | CompareBorders(const nsIFrame* aTableFrame, |
michael@0 | 4803 | const nsIFrame* aColGroupFrame, |
michael@0 | 4804 | const nsIFrame* aColFrame, |
michael@0 | 4805 | const nsIFrame* aRowGroupFrame, |
michael@0 | 4806 | const nsIFrame* aRowFrame, |
michael@0 | 4807 | const nsIFrame* aCellFrame, |
michael@0 | 4808 | bool aTableIsLTR, |
michael@0 | 4809 | mozilla::css::Side aSide, |
michael@0 | 4810 | bool aAja) |
michael@0 | 4811 | { |
michael@0 | 4812 | BCCellBorder border, tempBorder; |
michael@0 | 4813 | bool horizontal = (NS_SIDE_TOP == aSide) || (NS_SIDE_BOTTOM == aSide); |
michael@0 | 4814 | |
michael@0 | 4815 | // start with the table as dominant if present |
michael@0 | 4816 | if (aTableFrame) { |
michael@0 | 4817 | GetColorAndStyle(aTableFrame, aSide, border.style, border.color, aTableIsLTR, border.width); |
michael@0 | 4818 | border.owner = eTableOwner; |
michael@0 | 4819 | if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { |
michael@0 | 4820 | return border; |
michael@0 | 4821 | } |
michael@0 | 4822 | } |
michael@0 | 4823 | // see if the colgroup is dominant |
michael@0 | 4824 | if (aColGroupFrame) { |
michael@0 | 4825 | GetColorAndStyle(aColGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); |
michael@0 | 4826 | tempBorder.owner = (aAja && !horizontal) ? eAjaColGroupOwner : eColGroupOwner; |
michael@0 | 4827 | // pass here and below false for aSecondIsHorizontal as it is only used for corner calculations. |
michael@0 | 4828 | border = CompareBorders(!CELL_CORNER, border, tempBorder, false); |
michael@0 | 4829 | if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { |
michael@0 | 4830 | return border; |
michael@0 | 4831 | } |
michael@0 | 4832 | } |
michael@0 | 4833 | // see if the col is dominant |
michael@0 | 4834 | if (aColFrame) { |
michael@0 | 4835 | GetColorAndStyle(aColFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); |
michael@0 | 4836 | tempBorder.owner = (aAja && !horizontal) ? eAjaColOwner : eColOwner; |
michael@0 | 4837 | border = CompareBorders(!CELL_CORNER, border, tempBorder, false); |
michael@0 | 4838 | if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { |
michael@0 | 4839 | return border; |
michael@0 | 4840 | } |
michael@0 | 4841 | } |
michael@0 | 4842 | // see if the rowgroup is dominant |
michael@0 | 4843 | if (aRowGroupFrame) { |
michael@0 | 4844 | GetColorAndStyle(aRowGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); |
michael@0 | 4845 | tempBorder.owner = (aAja && horizontal) ? eAjaRowGroupOwner : eRowGroupOwner; |
michael@0 | 4846 | border = CompareBorders(!CELL_CORNER, border, tempBorder, false); |
michael@0 | 4847 | if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { |
michael@0 | 4848 | return border; |
michael@0 | 4849 | } |
michael@0 | 4850 | } |
michael@0 | 4851 | // see if the row is dominant |
michael@0 | 4852 | if (aRowFrame) { |
michael@0 | 4853 | GetColorAndStyle(aRowFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); |
michael@0 | 4854 | tempBorder.owner = (aAja && horizontal) ? eAjaRowOwner : eRowOwner; |
michael@0 | 4855 | border = CompareBorders(!CELL_CORNER, border, tempBorder, false); |
michael@0 | 4856 | if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { |
michael@0 | 4857 | return border; |
michael@0 | 4858 | } |
michael@0 | 4859 | } |
michael@0 | 4860 | // see if the cell is dominant |
michael@0 | 4861 | if (aCellFrame) { |
michael@0 | 4862 | GetColorAndStyle(aCellFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); |
michael@0 | 4863 | tempBorder.owner = (aAja) ? eAjaCellOwner : eCellOwner; |
michael@0 | 4864 | border = CompareBorders(!CELL_CORNER, border, tempBorder, false); |
michael@0 | 4865 | } |
michael@0 | 4866 | return border; |
michael@0 | 4867 | } |
michael@0 | 4868 | |
michael@0 | 4869 | static bool |
michael@0 | 4870 | Perpendicular(mozilla::css::Side aSide1, |
michael@0 | 4871 | mozilla::css::Side aSide2) |
michael@0 | 4872 | { |
michael@0 | 4873 | switch (aSide1) { |
michael@0 | 4874 | case NS_SIDE_TOP: |
michael@0 | 4875 | return (NS_SIDE_BOTTOM != aSide2); |
michael@0 | 4876 | case NS_SIDE_RIGHT: |
michael@0 | 4877 | return (NS_SIDE_LEFT != aSide2); |
michael@0 | 4878 | case NS_SIDE_BOTTOM: |
michael@0 | 4879 | return (NS_SIDE_TOP != aSide2); |
michael@0 | 4880 | default: // NS_SIDE_LEFT |
michael@0 | 4881 | return (NS_SIDE_RIGHT != aSide2); |
michael@0 | 4882 | } |
michael@0 | 4883 | } |
michael@0 | 4884 | |
michael@0 | 4885 | // XXX allocate this as number-of-cols+1 instead of number-of-cols+1 * number-of-rows+1 |
michael@0 | 4886 | struct BCCornerInfo |
michael@0 | 4887 | { |
michael@0 | 4888 | BCCornerInfo() { ownerColor = 0; ownerWidth = subWidth = ownerElem = subSide = |
michael@0 | 4889 | subElem = hasDashDot = numSegs = bevel = 0; ownerSide = NS_SIDE_TOP; |
michael@0 | 4890 | ownerStyle = 0xFF; subStyle = NS_STYLE_BORDER_STYLE_SOLID; } |
michael@0 | 4891 | void Set(mozilla::css::Side aSide, |
michael@0 | 4892 | BCCellBorder border); |
michael@0 | 4893 | |
michael@0 | 4894 | void Update(mozilla::css::Side aSide, |
michael@0 | 4895 | BCCellBorder border); |
michael@0 | 4896 | |
michael@0 | 4897 | nscolor ownerColor; // color of borderOwner |
michael@0 | 4898 | uint16_t ownerWidth; // pixel width of borderOwner |
michael@0 | 4899 | uint16_t subWidth; // pixel width of the largest border intersecting the border perpendicular |
michael@0 | 4900 | // to ownerSide |
michael@0 | 4901 | uint32_t ownerSide:2; // mozilla::css::Side (e.g NS_SIDE_TOP, NS_SIDE_RIGHT, etc) of the border |
michael@0 | 4902 | // owning the corner relative to the corner |
michael@0 | 4903 | uint32_t ownerElem:3; // elem type (e.g. eTable, eGroup, etc) owning the corner |
michael@0 | 4904 | uint32_t ownerStyle:8; // border style of ownerElem |
michael@0 | 4905 | uint32_t subSide:2; // side of border with subWidth relative to the corner |
michael@0 | 4906 | uint32_t subElem:3; // elem type (e.g. eTable, eGroup, etc) of sub owner |
michael@0 | 4907 | uint32_t subStyle:8; // border style of subElem |
michael@0 | 4908 | uint32_t hasDashDot:1; // does a dashed, dotted segment enter the corner, they cannot be beveled |
michael@0 | 4909 | uint32_t numSegs:3; // number of segments entering corner |
michael@0 | 4910 | uint32_t bevel:1; // is the corner beveled (uses the above two fields together with subWidth) |
michael@0 | 4911 | uint32_t unused:1; |
michael@0 | 4912 | }; |
michael@0 | 4913 | |
michael@0 | 4914 | void |
michael@0 | 4915 | BCCornerInfo::Set(mozilla::css::Side aSide, |
michael@0 | 4916 | BCCellBorder aBorder) |
michael@0 | 4917 | { |
michael@0 | 4918 | ownerElem = aBorder.owner; |
michael@0 | 4919 | ownerStyle = aBorder.style; |
michael@0 | 4920 | ownerWidth = aBorder.width; |
michael@0 | 4921 | ownerColor = aBorder.color; |
michael@0 | 4922 | ownerSide = aSide; |
michael@0 | 4923 | hasDashDot = 0; |
michael@0 | 4924 | numSegs = 0; |
michael@0 | 4925 | if (aBorder.width > 0) { |
michael@0 | 4926 | numSegs++; |
michael@0 | 4927 | hasDashDot = (NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) || |
michael@0 | 4928 | (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style); |
michael@0 | 4929 | } |
michael@0 | 4930 | bevel = 0; |
michael@0 | 4931 | subWidth = 0; |
michael@0 | 4932 | // the following will get set later |
michael@0 | 4933 | subSide = ((aSide == NS_SIDE_LEFT) || (aSide == NS_SIDE_RIGHT)) ? NS_SIDE_TOP : NS_SIDE_LEFT; |
michael@0 | 4934 | subElem = eTableOwner; |
michael@0 | 4935 | subStyle = NS_STYLE_BORDER_STYLE_SOLID; |
michael@0 | 4936 | } |
michael@0 | 4937 | |
michael@0 | 4938 | void |
michael@0 | 4939 | BCCornerInfo::Update(mozilla::css::Side aSide, |
michael@0 | 4940 | BCCellBorder aBorder) |
michael@0 | 4941 | { |
michael@0 | 4942 | bool existingWins = false; |
michael@0 | 4943 | if (0xFF == ownerStyle) { // initial value indiating that it hasn't been set yet |
michael@0 | 4944 | Set(aSide, aBorder); |
michael@0 | 4945 | } |
michael@0 | 4946 | else { |
michael@0 | 4947 | bool horizontal = (NS_SIDE_LEFT == aSide) || (NS_SIDE_RIGHT == aSide); // relative to the corner |
michael@0 | 4948 | BCCellBorder oldBorder, tempBorder; |
michael@0 | 4949 | oldBorder.owner = (BCBorderOwner) ownerElem; |
michael@0 | 4950 | oldBorder.style = ownerStyle; |
michael@0 | 4951 | oldBorder.width = ownerWidth; |
michael@0 | 4952 | oldBorder.color = ownerColor; |
michael@0 | 4953 | |
michael@0 | 4954 | mozilla::css::Side oldSide = mozilla::css::Side(ownerSide); |
michael@0 | 4955 | |
michael@0 | 4956 | tempBorder = CompareBorders(CELL_CORNER, oldBorder, aBorder, horizontal, &existingWins); |
michael@0 | 4957 | |
michael@0 | 4958 | ownerElem = tempBorder.owner; |
michael@0 | 4959 | ownerStyle = tempBorder.style; |
michael@0 | 4960 | ownerWidth = tempBorder.width; |
michael@0 | 4961 | ownerColor = tempBorder.color; |
michael@0 | 4962 | if (existingWins) { // existing corner is dominant |
michael@0 | 4963 | if (::Perpendicular(mozilla::css::Side(ownerSide), aSide)) { |
michael@0 | 4964 | // see if the new sub info replaces the old |
michael@0 | 4965 | BCCellBorder subBorder; |
michael@0 | 4966 | subBorder.owner = (BCBorderOwner) subElem; |
michael@0 | 4967 | subBorder.style = subStyle; |
michael@0 | 4968 | subBorder.width = subWidth; |
michael@0 | 4969 | subBorder.color = 0; // we are not interested in subBorder color |
michael@0 | 4970 | bool firstWins; |
michael@0 | 4971 | |
michael@0 | 4972 | tempBorder = CompareBorders(CELL_CORNER, subBorder, aBorder, horizontal, &firstWins); |
michael@0 | 4973 | |
michael@0 | 4974 | subElem = tempBorder.owner; |
michael@0 | 4975 | subStyle = tempBorder.style; |
michael@0 | 4976 | subWidth = tempBorder.width; |
michael@0 | 4977 | if (!firstWins) { |
michael@0 | 4978 | subSide = aSide; |
michael@0 | 4979 | } |
michael@0 | 4980 | } |
michael@0 | 4981 | } |
michael@0 | 4982 | else { // input args are dominant |
michael@0 | 4983 | ownerSide = aSide; |
michael@0 | 4984 | if (::Perpendicular(oldSide, mozilla::css::Side(ownerSide))) { |
michael@0 | 4985 | subElem = oldBorder.owner; |
michael@0 | 4986 | subStyle = oldBorder.style; |
michael@0 | 4987 | subWidth = oldBorder.width; |
michael@0 | 4988 | subSide = oldSide; |
michael@0 | 4989 | } |
michael@0 | 4990 | } |
michael@0 | 4991 | if (aBorder.width > 0) { |
michael@0 | 4992 | numSegs++; |
michael@0 | 4993 | if (!hasDashDot && ((NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) || |
michael@0 | 4994 | (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style))) { |
michael@0 | 4995 | hasDashDot = 1; |
michael@0 | 4996 | } |
michael@0 | 4997 | } |
michael@0 | 4998 | |
michael@0 | 4999 | // bevel the corner if only two perpendicular non dashed/dotted segments enter the corner |
michael@0 | 5000 | bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot); |
michael@0 | 5001 | } |
michael@0 | 5002 | } |
michael@0 | 5003 | |
michael@0 | 5004 | struct BCCorners |
michael@0 | 5005 | { |
michael@0 | 5006 | BCCorners(int32_t aNumCorners, |
michael@0 | 5007 | int32_t aStartIndex); |
michael@0 | 5008 | |
michael@0 | 5009 | ~BCCorners() { delete [] corners; } |
michael@0 | 5010 | |
michael@0 | 5011 | BCCornerInfo& operator [](int32_t i) const |
michael@0 | 5012 | { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error"); |
michael@0 | 5013 | return corners[clamped(i, startIndex, endIndex) - startIndex]; } |
michael@0 | 5014 | |
michael@0 | 5015 | int32_t startIndex; |
michael@0 | 5016 | int32_t endIndex; |
michael@0 | 5017 | BCCornerInfo* corners; |
michael@0 | 5018 | }; |
michael@0 | 5019 | |
michael@0 | 5020 | BCCorners::BCCorners(int32_t aNumCorners, |
michael@0 | 5021 | int32_t aStartIndex) |
michael@0 | 5022 | { |
michael@0 | 5023 | NS_ASSERTION((aNumCorners > 0) && (aStartIndex >= 0), "program error"); |
michael@0 | 5024 | startIndex = aStartIndex; |
michael@0 | 5025 | endIndex = aStartIndex + aNumCorners - 1; |
michael@0 | 5026 | corners = new BCCornerInfo[aNumCorners]; |
michael@0 | 5027 | } |
michael@0 | 5028 | |
michael@0 | 5029 | |
michael@0 | 5030 | struct BCCellBorders |
michael@0 | 5031 | { |
michael@0 | 5032 | BCCellBorders(int32_t aNumBorders, |
michael@0 | 5033 | int32_t aStartIndex); |
michael@0 | 5034 | |
michael@0 | 5035 | ~BCCellBorders() { delete [] borders; } |
michael@0 | 5036 | |
michael@0 | 5037 | BCCellBorder& operator [](int32_t i) const |
michael@0 | 5038 | { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error"); |
michael@0 | 5039 | return borders[clamped(i, startIndex, endIndex) - startIndex]; } |
michael@0 | 5040 | |
michael@0 | 5041 | int32_t startIndex; |
michael@0 | 5042 | int32_t endIndex; |
michael@0 | 5043 | BCCellBorder* borders; |
michael@0 | 5044 | }; |
michael@0 | 5045 | |
michael@0 | 5046 | BCCellBorders::BCCellBorders(int32_t aNumBorders, |
michael@0 | 5047 | int32_t aStartIndex) |
michael@0 | 5048 | { |
michael@0 | 5049 | NS_ASSERTION((aNumBorders > 0) && (aStartIndex >= 0), "program error"); |
michael@0 | 5050 | startIndex = aStartIndex; |
michael@0 | 5051 | endIndex = aStartIndex + aNumBorders - 1; |
michael@0 | 5052 | borders = new BCCellBorder[aNumBorders]; |
michael@0 | 5053 | } |
michael@0 | 5054 | |
michael@0 | 5055 | // this function sets the new border properties and returns true if the border |
michael@0 | 5056 | // segment will start a new segment and not be accumulated into the previous |
michael@0 | 5057 | // segment. |
michael@0 | 5058 | static bool |
michael@0 | 5059 | SetBorder(const BCCellBorder& aNewBorder, |
michael@0 | 5060 | BCCellBorder& aBorder) |
michael@0 | 5061 | { |
michael@0 | 5062 | bool changed = (aNewBorder.style != aBorder.style) || |
michael@0 | 5063 | (aNewBorder.width != aBorder.width) || |
michael@0 | 5064 | (aNewBorder.color != aBorder.color); |
michael@0 | 5065 | aBorder.color = aNewBorder.color; |
michael@0 | 5066 | aBorder.width = aNewBorder.width; |
michael@0 | 5067 | aBorder.style = aNewBorder.style; |
michael@0 | 5068 | aBorder.owner = aNewBorder.owner; |
michael@0 | 5069 | |
michael@0 | 5070 | return changed; |
michael@0 | 5071 | } |
michael@0 | 5072 | |
michael@0 | 5073 | // this function will set the horizontal border. It will return true if the |
michael@0 | 5074 | // existing segment will not be continued. Having a vertical owner of a corner |
michael@0 | 5075 | // should also start a new segment. |
michael@0 | 5076 | static bool |
michael@0 | 5077 | SetHorBorder(const BCCellBorder& aNewBorder, |
michael@0 | 5078 | const BCCornerInfo& aCorner, |
michael@0 | 5079 | BCCellBorder& aBorder) |
michael@0 | 5080 | { |
michael@0 | 5081 | bool startSeg = ::SetBorder(aNewBorder, aBorder); |
michael@0 | 5082 | if (!startSeg) { |
michael@0 | 5083 | startSeg = ((NS_SIDE_LEFT != aCorner.ownerSide) && (NS_SIDE_RIGHT != aCorner.ownerSide)); |
michael@0 | 5084 | } |
michael@0 | 5085 | return startSeg; |
michael@0 | 5086 | } |
michael@0 | 5087 | |
michael@0 | 5088 | // Make the damage area larger on the top and bottom by at least one row and on the left and right |
michael@0 | 5089 | // at least one column. This is done so that adjacent elements are part of the border calculations. |
michael@0 | 5090 | // The extra segments and borders outside the actual damage area will not be updated in the cell map, |
michael@0 | 5091 | // because they in turn would need info from adjacent segments outside the damage area to be accurate. |
michael@0 | 5092 | void |
michael@0 | 5093 | nsTableFrame::ExpandBCDamageArea(nsIntRect& aRect) const |
michael@0 | 5094 | { |
michael@0 | 5095 | int32_t numRows = GetRowCount(); |
michael@0 | 5096 | int32_t numCols = GetColCount(); |
michael@0 | 5097 | |
michael@0 | 5098 | int32_t dStartX = aRect.x; |
michael@0 | 5099 | int32_t dEndX = aRect.XMost() - 1; |
michael@0 | 5100 | int32_t dStartY = aRect.y; |
michael@0 | 5101 | int32_t dEndY = aRect.YMost() - 1; |
michael@0 | 5102 | |
michael@0 | 5103 | // expand the damage area in each direction |
michael@0 | 5104 | if (dStartX > 0) { |
michael@0 | 5105 | dStartX--; |
michael@0 | 5106 | } |
michael@0 | 5107 | if (dEndX < (numCols - 1)) { |
michael@0 | 5108 | dEndX++; |
michael@0 | 5109 | } |
michael@0 | 5110 | if (dStartY > 0) { |
michael@0 | 5111 | dStartY--; |
michael@0 | 5112 | } |
michael@0 | 5113 | if (dEndY < (numRows - 1)) { |
michael@0 | 5114 | dEndY++; |
michael@0 | 5115 | } |
michael@0 | 5116 | // Check the damage area so that there are no cells spanning in or out. If there are any then |
michael@0 | 5117 | // make the damage area as big as the table, similarly to the way the cell map decides whether |
michael@0 | 5118 | // to rebuild versus expand. This could be optimized to expand to the smallest area that contains |
michael@0 | 5119 | // no spanners, but it may not be worth the effort in general, and it would need to be done in the |
michael@0 | 5120 | // cell map as well. |
michael@0 | 5121 | bool haveSpanner = false; |
michael@0 | 5122 | if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) { |
michael@0 | 5123 | nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0(); |
michael@0 | 5124 | // Get the ordered row groups |
michael@0 | 5125 | RowGroupArray rowGroups; |
michael@0 | 5126 | OrderRowGroups(rowGroups); |
michael@0 | 5127 | |
michael@0 | 5128 | // Scope outside loop to be used as hint. |
michael@0 | 5129 | nsCellMap* cellMap = nullptr; |
michael@0 | 5130 | for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { |
michael@0 | 5131 | nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
michael@0 | 5132 | int32_t rgStartY = rgFrame->GetStartRowIndex(); |
michael@0 | 5133 | int32_t rgEndY = rgStartY + rgFrame->GetRowCount() - 1; |
michael@0 | 5134 | if (dEndY < rgStartY) |
michael@0 | 5135 | break; |
michael@0 | 5136 | cellMap = tableCellMap->GetMapFor(rgFrame, cellMap); |
michael@0 | 5137 | if (!cellMap) ABORT0(); |
michael@0 | 5138 | // check for spanners from above and below |
michael@0 | 5139 | if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) { |
michael@0 | 5140 | if (uint32_t(dStartY - rgStartY) >= cellMap->mRows.Length()) |
michael@0 | 5141 | ABORT0(); |
michael@0 | 5142 | const nsCellMap::CellDataArray& row = |
michael@0 | 5143 | cellMap->mRows[dStartY - rgStartY]; |
michael@0 | 5144 | for (int32_t x = dStartX; x <= dEndX; x++) { |
michael@0 | 5145 | CellData* cellData = row.SafeElementAt(x); |
michael@0 | 5146 | if (cellData && (cellData->IsRowSpan())) { |
michael@0 | 5147 | haveSpanner = true; |
michael@0 | 5148 | break; |
michael@0 | 5149 | } |
michael@0 | 5150 | } |
michael@0 | 5151 | if (dEndY < rgEndY) { |
michael@0 | 5152 | if (uint32_t(dEndY + 1 - rgStartY) >= cellMap->mRows.Length()) |
michael@0 | 5153 | ABORT0(); |
michael@0 | 5154 | const nsCellMap::CellDataArray& row2 = |
michael@0 | 5155 | cellMap->mRows[dEndY + 1 - rgStartY]; |
michael@0 | 5156 | for (int32_t x = dStartX; x <= dEndX; x++) { |
michael@0 | 5157 | CellData* cellData = row2.SafeElementAt(x); |
michael@0 | 5158 | if (cellData && (cellData->IsRowSpan())) { |
michael@0 | 5159 | haveSpanner = true; |
michael@0 | 5160 | break; |
michael@0 | 5161 | } |
michael@0 | 5162 | } |
michael@0 | 5163 | } |
michael@0 | 5164 | } |
michael@0 | 5165 | // check for spanners on the left and right |
michael@0 | 5166 | int32_t iterStartY = -1; |
michael@0 | 5167 | int32_t iterEndY = -1; |
michael@0 | 5168 | if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) { |
michael@0 | 5169 | // the damage area starts in the row group |
michael@0 | 5170 | iterStartY = dStartY; |
michael@0 | 5171 | iterEndY = std::min(dEndY, rgEndY); |
michael@0 | 5172 | } |
michael@0 | 5173 | else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) { |
michael@0 | 5174 | // the damage area ends in the row group |
michael@0 | 5175 | iterStartY = rgStartY; |
michael@0 | 5176 | iterEndY = dEndY; |
michael@0 | 5177 | } |
michael@0 | 5178 | else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) { |
michael@0 | 5179 | // the damage area contains the row group |
michael@0 | 5180 | iterStartY = rgStartY; |
michael@0 | 5181 | iterEndY = rgEndY; |
michael@0 | 5182 | } |
michael@0 | 5183 | if ((iterStartY >= 0) && (iterEndY >= 0)) { |
michael@0 | 5184 | for (int32_t y = iterStartY; y <= iterEndY; y++) { |
michael@0 | 5185 | if (uint32_t(y - rgStartY) >= cellMap->mRows.Length()) |
michael@0 | 5186 | ABORT0(); |
michael@0 | 5187 | const nsCellMap::CellDataArray& row = |
michael@0 | 5188 | cellMap->mRows[y - rgStartY]; |
michael@0 | 5189 | CellData* cellData = row.SafeElementAt(dStartX); |
michael@0 | 5190 | if (cellData && (cellData->IsColSpan())) { |
michael@0 | 5191 | haveSpanner = true; |
michael@0 | 5192 | break; |
michael@0 | 5193 | } |
michael@0 | 5194 | if (dEndX < (numCols - 1)) { |
michael@0 | 5195 | cellData = row.SafeElementAt(dEndX + 1); |
michael@0 | 5196 | if (cellData && (cellData->IsColSpan())) { |
michael@0 | 5197 | haveSpanner = true; |
michael@0 | 5198 | break; |
michael@0 | 5199 | } |
michael@0 | 5200 | } |
michael@0 | 5201 | } |
michael@0 | 5202 | } |
michael@0 | 5203 | } |
michael@0 | 5204 | } |
michael@0 | 5205 | if (haveSpanner) { |
michael@0 | 5206 | // make the damage area the whole table |
michael@0 | 5207 | aRect.x = 0; |
michael@0 | 5208 | aRect.y = 0; |
michael@0 | 5209 | aRect.width = numCols; |
michael@0 | 5210 | aRect.height = numRows; |
michael@0 | 5211 | } |
michael@0 | 5212 | else { |
michael@0 | 5213 | aRect.x = dStartX; |
michael@0 | 5214 | aRect.y = dStartY; |
michael@0 | 5215 | aRect.width = 1 + dEndX - dStartX; |
michael@0 | 5216 | aRect.height = 1 + dEndY - dStartY; |
michael@0 | 5217 | } |
michael@0 | 5218 | } |
michael@0 | 5219 | |
michael@0 | 5220 | |
michael@0 | 5221 | #define ADJACENT true |
michael@0 | 5222 | #define HORIZONTAL true |
michael@0 | 5223 | |
michael@0 | 5224 | void |
michael@0 | 5225 | BCMapCellInfo::SetTableTopLeftContBCBorder() |
michael@0 | 5226 | { |
michael@0 | 5227 | BCCellBorder currentBorder; |
michael@0 | 5228 | //calculate continuous top first row & rowgroup border: special case |
michael@0 | 5229 | //because it must include the table in the collapse |
michael@0 | 5230 | if (mTopRow) { |
michael@0 | 5231 | currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup, |
michael@0 | 5232 | mTopRow, nullptr, mTableIsLTR, |
michael@0 | 5233 | NS_SIDE_TOP, !ADJACENT); |
michael@0 | 5234 | mTopRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width); |
michael@0 | 5235 | } |
michael@0 | 5236 | if (mCgAtRight && mColGroup) { |
michael@0 | 5237 | //calculate continuous top colgroup border once per colgroup |
michael@0 | 5238 | currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup, |
michael@0 | 5239 | mTopRow, nullptr, mTableIsLTR, |
michael@0 | 5240 | NS_SIDE_TOP, !ADJACENT); |
michael@0 | 5241 | mColGroup->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width); |
michael@0 | 5242 | } |
michael@0 | 5243 | if (0 == mColIndex) { |
michael@0 | 5244 | currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, nullptr, |
michael@0 | 5245 | nullptr, nullptr, mTableIsLTR, NS_SIDE_LEFT, |
michael@0 | 5246 | !ADJACENT); |
michael@0 | 5247 | mTableFrame->SetContinuousLeftBCBorderWidth(currentBorder.width); |
michael@0 | 5248 | } |
michael@0 | 5249 | } |
michael@0 | 5250 | |
michael@0 | 5251 | void |
michael@0 | 5252 | BCMapCellInfo::SetRowGroupLeftContBCBorder() |
michael@0 | 5253 | { |
michael@0 | 5254 | BCCellBorder currentBorder; |
michael@0 | 5255 | //get row group continuous borders |
michael@0 | 5256 | if (mRgAtBottom && mRowGroup) { //once per row group, so check for bottom |
michael@0 | 5257 | currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup, |
michael@0 | 5258 | nullptr, nullptr, mTableIsLTR, NS_SIDE_LEFT, |
michael@0 | 5259 | !ADJACENT); |
michael@0 | 5260 | mRowGroup->SetContinuousBCBorderWidth(mStartSide, currentBorder.width); |
michael@0 | 5261 | } |
michael@0 | 5262 | } |
michael@0 | 5263 | |
michael@0 | 5264 | void |
michael@0 | 5265 | BCMapCellInfo::SetRowGroupRightContBCBorder() |
michael@0 | 5266 | { |
michael@0 | 5267 | BCCellBorder currentBorder; |
michael@0 | 5268 | //get row group continuous borders |
michael@0 | 5269 | if (mRgAtBottom && mRowGroup) { //once per mRowGroup, so check for bottom |
michael@0 | 5270 | currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup, |
michael@0 | 5271 | nullptr, nullptr, mTableIsLTR, NS_SIDE_RIGHT, |
michael@0 | 5272 | ADJACENT); |
michael@0 | 5273 | mRowGroup->SetContinuousBCBorderWidth(mEndSide, currentBorder.width); |
michael@0 | 5274 | } |
michael@0 | 5275 | } |
michael@0 | 5276 | |
michael@0 | 5277 | void |
michael@0 | 5278 | BCMapCellInfo::SetColumnTopRightContBCBorder() |
michael@0 | 5279 | { |
michael@0 | 5280 | BCCellBorder currentBorder; |
michael@0 | 5281 | //calculate column continuous borders |
michael@0 | 5282 | //we only need to do this once, so we'll do it only on the first row |
michael@0 | 5283 | currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame, |
michael@0 | 5284 | mCurrentColFrame, mRowGroup, mTopRow, nullptr, |
michael@0 | 5285 | mTableIsLTR, NS_SIDE_TOP, !ADJACENT); |
michael@0 | 5286 | ((nsTableColFrame*) mCurrentColFrame)->SetContinuousBCBorderWidth(NS_SIDE_TOP, |
michael@0 | 5287 | currentBorder.width); |
michael@0 | 5288 | if (mNumTableCols == GetCellEndColIndex() + 1) { |
michael@0 | 5289 | currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame, |
michael@0 | 5290 | mCurrentColFrame, nullptr, nullptr, nullptr, |
michael@0 | 5291 | mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT); |
michael@0 | 5292 | } |
michael@0 | 5293 | else { |
michael@0 | 5294 | currentBorder = CompareBorders(nullptr, mCurrentColGroupFrame, |
michael@0 | 5295 | mCurrentColFrame, nullptr,nullptr, nullptr, |
michael@0 | 5296 | mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT); |
michael@0 | 5297 | } |
michael@0 | 5298 | mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_RIGHT, |
michael@0 | 5299 | currentBorder.width); |
michael@0 | 5300 | } |
michael@0 | 5301 | |
michael@0 | 5302 | void |
michael@0 | 5303 | BCMapCellInfo::SetColumnBottomContBCBorder() |
michael@0 | 5304 | { |
michael@0 | 5305 | BCCellBorder currentBorder; |
michael@0 | 5306 | //get col continuous border |
michael@0 | 5307 | currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame, |
michael@0 | 5308 | mCurrentColFrame, mRowGroup, mBottomRow, |
michael@0 | 5309 | nullptr, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT); |
michael@0 | 5310 | mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, |
michael@0 | 5311 | currentBorder.width); |
michael@0 | 5312 | } |
michael@0 | 5313 | |
michael@0 | 5314 | void |
michael@0 | 5315 | BCMapCellInfo::SetColGroupBottomContBCBorder() |
michael@0 | 5316 | { |
michael@0 | 5317 | BCCellBorder currentBorder; |
michael@0 | 5318 | if (mColGroup) { |
michael@0 | 5319 | currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup, |
michael@0 | 5320 | mBottomRow, nullptr, mTableIsLTR, |
michael@0 | 5321 | NS_SIDE_BOTTOM, ADJACENT); |
michael@0 | 5322 | mColGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width); |
michael@0 | 5323 | } |
michael@0 | 5324 | } |
michael@0 | 5325 | |
michael@0 | 5326 | void |
michael@0 | 5327 | BCMapCellInfo::SetRowGroupBottomContBCBorder() |
michael@0 | 5328 | { |
michael@0 | 5329 | BCCellBorder currentBorder; |
michael@0 | 5330 | if (mRowGroup) { |
michael@0 | 5331 | currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup, |
michael@0 | 5332 | mBottomRow, nullptr, mTableIsLTR, |
michael@0 | 5333 | NS_SIDE_BOTTOM, ADJACENT); |
michael@0 | 5334 | mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width); |
michael@0 | 5335 | } |
michael@0 | 5336 | } |
michael@0 | 5337 | |
michael@0 | 5338 | void |
michael@0 | 5339 | BCMapCellInfo::SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup, |
michael@0 | 5340 | nsTableRowFrame* aNextRow) |
michael@0 | 5341 | { |
michael@0 | 5342 | BCCellBorder currentBorder, adjacentBorder; |
michael@0 | 5343 | |
michael@0 | 5344 | const nsIFrame* rowgroup = (mRgAtBottom) ? mRowGroup : nullptr; |
michael@0 | 5345 | currentBorder = CompareBorders(nullptr, nullptr, nullptr, rowgroup, mBottomRow, |
michael@0 | 5346 | nullptr, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT); |
michael@0 | 5347 | |
michael@0 | 5348 | adjacentBorder = CompareBorders(nullptr, nullptr, nullptr, aNextRowGroup, |
michael@0 | 5349 | aNextRow, nullptr, mTableIsLTR, NS_SIDE_TOP, |
michael@0 | 5350 | !ADJACENT); |
michael@0 | 5351 | currentBorder = CompareBorders(false, currentBorder, adjacentBorder, |
michael@0 | 5352 | HORIZONTAL); |
michael@0 | 5353 | if (aNextRow) { |
michael@0 | 5354 | aNextRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width); |
michael@0 | 5355 | } |
michael@0 | 5356 | if (mRgAtBottom && mRowGroup) { |
michael@0 | 5357 | mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width); |
michael@0 | 5358 | } |
michael@0 | 5359 | } |
michael@0 | 5360 | |
michael@0 | 5361 | void |
michael@0 | 5362 | BCMapCellInfo::SetRowLeftContBCBorder() |
michael@0 | 5363 | { |
michael@0 | 5364 | //get row continuous borders |
michael@0 | 5365 | if (mCurrentRowFrame) { |
michael@0 | 5366 | BCCellBorder currentBorder; |
michael@0 | 5367 | currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup, |
michael@0 | 5368 | mCurrentRowFrame, nullptr, mTableIsLTR, |
michael@0 | 5369 | NS_SIDE_LEFT, !ADJACENT); |
michael@0 | 5370 | mCurrentRowFrame->SetContinuousBCBorderWidth(mStartSide, |
michael@0 | 5371 | currentBorder.width); |
michael@0 | 5372 | } |
michael@0 | 5373 | } |
michael@0 | 5374 | |
michael@0 | 5375 | void |
michael@0 | 5376 | BCMapCellInfo::SetRowRightContBCBorder() |
michael@0 | 5377 | { |
michael@0 | 5378 | if (mCurrentRowFrame) { |
michael@0 | 5379 | BCCellBorder currentBorder; |
michael@0 | 5380 | currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup, |
michael@0 | 5381 | mCurrentRowFrame, nullptr, mTableIsLTR, |
michael@0 | 5382 | NS_SIDE_RIGHT, ADJACENT); |
michael@0 | 5383 | mCurrentRowFrame->SetContinuousBCBorderWidth(mEndSide, |
michael@0 | 5384 | currentBorder.width); |
michael@0 | 5385 | } |
michael@0 | 5386 | } |
michael@0 | 5387 | void |
michael@0 | 5388 | BCMapCellInfo::SetTableTopBorderWidth(BCPixelSize aWidth) |
michael@0 | 5389 | { |
michael@0 | 5390 | mTableBCData->mTopBorderWidth = std::max(mTableBCData->mTopBorderWidth, aWidth); |
michael@0 | 5391 | } |
michael@0 | 5392 | |
michael@0 | 5393 | void |
michael@0 | 5394 | BCMapCellInfo::SetTableLeftBorderWidth(int32_t aRowY, BCPixelSize aWidth) |
michael@0 | 5395 | { |
michael@0 | 5396 | // update the left/right first cell border |
michael@0 | 5397 | if (aRowY == 0) { |
michael@0 | 5398 | if (mTableIsLTR) { |
michael@0 | 5399 | mTableBCData->mLeftCellBorderWidth = aWidth; |
michael@0 | 5400 | } |
michael@0 | 5401 | else { |
michael@0 | 5402 | mTableBCData->mRightCellBorderWidth = aWidth; |
michael@0 | 5403 | } |
michael@0 | 5404 | } |
michael@0 | 5405 | mTableBCData->mLeftBorderWidth = std::max(mTableBCData->mLeftBorderWidth, |
michael@0 | 5406 | aWidth); |
michael@0 | 5407 | } |
michael@0 | 5408 | |
michael@0 | 5409 | void |
michael@0 | 5410 | BCMapCellInfo::SetTableRightBorderWidth(int32_t aRowY, BCPixelSize aWidth) |
michael@0 | 5411 | { |
michael@0 | 5412 | // update the left/right first cell border |
michael@0 | 5413 | if (aRowY == 0) { |
michael@0 | 5414 | if (mTableIsLTR) { |
michael@0 | 5415 | mTableBCData->mRightCellBorderWidth = aWidth; |
michael@0 | 5416 | } |
michael@0 | 5417 | else { |
michael@0 | 5418 | mTableBCData->mLeftCellBorderWidth = aWidth; |
michael@0 | 5419 | } |
michael@0 | 5420 | } |
michael@0 | 5421 | mTableBCData->mRightBorderWidth = std::max(mTableBCData->mRightBorderWidth, |
michael@0 | 5422 | aWidth); |
michael@0 | 5423 | } |
michael@0 | 5424 | |
michael@0 | 5425 | void |
michael@0 | 5426 | BCMapCellInfo::SetRightBorderWidths(BCPixelSize aWidth) |
michael@0 | 5427 | { |
michael@0 | 5428 | // update the borders of the cells and cols affected |
michael@0 | 5429 | if (mCell) { |
michael@0 | 5430 | mCell->SetBorderWidth(mEndSide, std::max(aWidth, |
michael@0 | 5431 | mCell->GetBorderWidth(mEndSide))); |
michael@0 | 5432 | } |
michael@0 | 5433 | if (mRightCol) { |
michael@0 | 5434 | BCPixelSize half = BC_BORDER_LEFT_HALF(aWidth); |
michael@0 | 5435 | mRightCol->SetRightBorderWidth(std::max(nscoord(half), |
michael@0 | 5436 | mRightCol->GetRightBorderWidth())); |
michael@0 | 5437 | } |
michael@0 | 5438 | } |
michael@0 | 5439 | |
michael@0 | 5440 | void |
michael@0 | 5441 | BCMapCellInfo::SetBottomBorderWidths(BCPixelSize aWidth) |
michael@0 | 5442 | { |
michael@0 | 5443 | // update the borders of the affected cells and rows |
michael@0 | 5444 | if (mCell) { |
michael@0 | 5445 | mCell->SetBorderWidth(NS_SIDE_BOTTOM, std::max(aWidth, |
michael@0 | 5446 | mCell->GetBorderWidth(NS_SIDE_BOTTOM))); |
michael@0 | 5447 | } |
michael@0 | 5448 | if (mBottomRow) { |
michael@0 | 5449 | BCPixelSize half = BC_BORDER_TOP_HALF(aWidth); |
michael@0 | 5450 | mBottomRow->SetBottomBCBorderWidth(std::max(nscoord(half), |
michael@0 | 5451 | mBottomRow->GetBottomBCBorderWidth())); |
michael@0 | 5452 | } |
michael@0 | 5453 | } |
michael@0 | 5454 | void |
michael@0 | 5455 | BCMapCellInfo::SetTopBorderWidths(BCPixelSize aWidth) |
michael@0 | 5456 | { |
michael@0 | 5457 | if (mCell) { |
michael@0 | 5458 | mCell->SetBorderWidth(NS_SIDE_TOP, std::max(aWidth, |
michael@0 | 5459 | mCell->GetBorderWidth(NS_SIDE_TOP))); |
michael@0 | 5460 | } |
michael@0 | 5461 | if (mTopRow) { |
michael@0 | 5462 | BCPixelSize half = BC_BORDER_BOTTOM_HALF(aWidth); |
michael@0 | 5463 | mTopRow->SetTopBCBorderWidth(std::max(nscoord(half), |
michael@0 | 5464 | mTopRow->GetTopBCBorderWidth())); |
michael@0 | 5465 | } |
michael@0 | 5466 | } |
michael@0 | 5467 | void |
michael@0 | 5468 | BCMapCellInfo::SetLeftBorderWidths(BCPixelSize aWidth) |
michael@0 | 5469 | { |
michael@0 | 5470 | if (mCell) { |
michael@0 | 5471 | mCell->SetBorderWidth(mStartSide, std::max(aWidth, |
michael@0 | 5472 | mCell->GetBorderWidth(mStartSide))); |
michael@0 | 5473 | } |
michael@0 | 5474 | if (mLeftCol) { |
michael@0 | 5475 | BCPixelSize half = BC_BORDER_RIGHT_HALF(aWidth); |
michael@0 | 5476 | mLeftCol->SetLeftBorderWidth(std::max(nscoord(half), |
michael@0 | 5477 | mLeftCol->GetLeftBorderWidth())); |
michael@0 | 5478 | } |
michael@0 | 5479 | } |
michael@0 | 5480 | |
michael@0 | 5481 | void |
michael@0 | 5482 | BCMapCellInfo::SetTableBottomBorderWidth(BCPixelSize aWidth) |
michael@0 | 5483 | { |
michael@0 | 5484 | mTableBCData->mBottomBorderWidth = std::max(mTableBCData->mBottomBorderWidth, |
michael@0 | 5485 | aWidth); |
michael@0 | 5486 | } |
michael@0 | 5487 | |
michael@0 | 5488 | void |
michael@0 | 5489 | BCMapCellInfo::SetColumn(int32_t aColX) |
michael@0 | 5490 | { |
michael@0 | 5491 | mCurrentColFrame = mTableFrame->GetColFrame(aColX); |
michael@0 | 5492 | if (!mCurrentColFrame) { |
michael@0 | 5493 | NS_ERROR("null mCurrentColFrame"); |
michael@0 | 5494 | } |
michael@0 | 5495 | mCurrentColGroupFrame = static_cast<nsTableColGroupFrame*> |
michael@0 | 5496 | (mCurrentColFrame->GetParent()); |
michael@0 | 5497 | if (!mCurrentColGroupFrame) { |
michael@0 | 5498 | NS_ERROR("null mCurrentColGroupFrame"); |
michael@0 | 5499 | } |
michael@0 | 5500 | } |
michael@0 | 5501 | |
michael@0 | 5502 | void |
michael@0 | 5503 | BCMapCellInfo::IncrementRow(bool aResetToTopRowOfCell) |
michael@0 | 5504 | { |
michael@0 | 5505 | mCurrentRowFrame = (aResetToTopRowOfCell) ? mTopRow : |
michael@0 | 5506 | mCurrentRowFrame->GetNextRow(); |
michael@0 | 5507 | } |
michael@0 | 5508 | |
michael@0 | 5509 | BCCellBorder |
michael@0 | 5510 | BCMapCellInfo::GetTopEdgeBorder() |
michael@0 | 5511 | { |
michael@0 | 5512 | return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame, |
michael@0 | 5513 | mRowGroup, mTopRow, mCell, mTableIsLTR, NS_SIDE_TOP, |
michael@0 | 5514 | !ADJACENT); |
michael@0 | 5515 | } |
michael@0 | 5516 | |
michael@0 | 5517 | BCCellBorder |
michael@0 | 5518 | BCMapCellInfo::GetBottomEdgeBorder() |
michael@0 | 5519 | { |
michael@0 | 5520 | return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame, |
michael@0 | 5521 | mRowGroup, mBottomRow, mCell, mTableIsLTR, |
michael@0 | 5522 | NS_SIDE_BOTTOM, ADJACENT); |
michael@0 | 5523 | } |
michael@0 | 5524 | BCCellBorder |
michael@0 | 5525 | BCMapCellInfo::GetLeftEdgeBorder() |
michael@0 | 5526 | { |
michael@0 | 5527 | return CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup, |
michael@0 | 5528 | mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_LEFT, |
michael@0 | 5529 | !ADJACENT); |
michael@0 | 5530 | } |
michael@0 | 5531 | BCCellBorder |
michael@0 | 5532 | BCMapCellInfo::GetRightEdgeBorder() |
michael@0 | 5533 | { |
michael@0 | 5534 | return CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup, |
michael@0 | 5535 | mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_RIGHT, |
michael@0 | 5536 | ADJACENT); |
michael@0 | 5537 | } |
michael@0 | 5538 | BCCellBorder |
michael@0 | 5539 | BCMapCellInfo::GetRightInternalBorder() |
michael@0 | 5540 | { |
michael@0 | 5541 | const nsIFrame* cg = (mCgAtRight) ? mColGroup : nullptr; |
michael@0 | 5542 | return CompareBorders(nullptr, cg, mRightCol, nullptr, nullptr, mCell, |
michael@0 | 5543 | mTableIsLTR, NS_SIDE_RIGHT, ADJACENT); |
michael@0 | 5544 | } |
michael@0 | 5545 | |
michael@0 | 5546 | BCCellBorder |
michael@0 | 5547 | BCMapCellInfo::GetLeftInternalBorder() |
michael@0 | 5548 | { |
michael@0 | 5549 | const nsIFrame* cg = (mCgAtLeft) ? mColGroup : nullptr; |
michael@0 | 5550 | return CompareBorders(nullptr, cg, mLeftCol, nullptr, nullptr, mCell, |
michael@0 | 5551 | mTableIsLTR, NS_SIDE_LEFT, !ADJACENT); |
michael@0 | 5552 | } |
michael@0 | 5553 | |
michael@0 | 5554 | BCCellBorder |
michael@0 | 5555 | BCMapCellInfo::GetBottomInternalBorder() |
michael@0 | 5556 | { |
michael@0 | 5557 | const nsIFrame* rg = (mRgAtBottom) ? mRowGroup : nullptr; |
michael@0 | 5558 | return CompareBorders(nullptr, nullptr, nullptr, rg, mBottomRow, mCell, |
michael@0 | 5559 | mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT); |
michael@0 | 5560 | } |
michael@0 | 5561 | |
michael@0 | 5562 | BCCellBorder |
michael@0 | 5563 | BCMapCellInfo::GetTopInternalBorder() |
michael@0 | 5564 | { |
michael@0 | 5565 | const nsIFrame* rg = (mRgAtTop) ? mRowGroup : nullptr; |
michael@0 | 5566 | return CompareBorders(nullptr, nullptr, nullptr, rg, mTopRow, mCell, |
michael@0 | 5567 | mTableIsLTR, NS_SIDE_TOP, !ADJACENT); |
michael@0 | 5568 | } |
michael@0 | 5569 | |
michael@0 | 5570 | /* Here is the order for storing border edges in the cell map as a cell is processed. There are |
michael@0 | 5571 | n=colspan top and bottom border edges per cell and n=rowspan left and right border edges per cell. |
michael@0 | 5572 | |
michael@0 | 5573 | 1) On the top edge of the table, store the top edge. Never store the top edge otherwise, since |
michael@0 | 5574 | a bottom edge from a cell above will take care of it. |
michael@0 | 5575 | 2) On the left edge of the table, store the left edge. Never store the left edge othewise, since |
michael@0 | 5576 | a right edge from a cell to the left will take care of it. |
michael@0 | 5577 | 3) Store the right edge (or edges if a row span) |
michael@0 | 5578 | 4) Store the bottom edge (or edges if a col span) |
michael@0 | 5579 | |
michael@0 | 5580 | Since corners are computed with only an array of BCCornerInfo indexed by the number-of-cols, corner |
michael@0 | 5581 | calculations are somewhat complicated. Using an array with number-of-rows * number-of-col entries |
michael@0 | 5582 | would simplify this, but at an extra in memory cost of nearly 12 bytes per cell map entry. Collapsing |
michael@0 | 5583 | borders already have about an extra 8 byte per cell map entry overhead (this could be |
michael@0 | 5584 | reduced to 4 bytes if we are willing to not store border widths in nsTableCellFrame), Here are the |
michael@0 | 5585 | rules in priority order for storing cornes in the cell map as a cell is processed. top-left means the |
michael@0 | 5586 | left endpoint of the border edge on the top of the cell. There are n=colspan top and bottom border |
michael@0 | 5587 | edges per cell and n=rowspan left and right border edges per cell. |
michael@0 | 5588 | |
michael@0 | 5589 | 1) On the top edge of the table, store the top-left corner, unless on the left edge of the table. |
michael@0 | 5590 | Never store the top-right corner, since it will get stored as a right-top corner. |
michael@0 | 5591 | 2) On the left edge of the table, store the left-top corner. Never store the left-bottom corner, |
michael@0 | 5592 | since it will get stored as a bottom-left corner. |
michael@0 | 5593 | 3) Store the right-top corner if (a) it is the top right corner of the table or (b) it is not on |
michael@0 | 5594 | the top edge of the table. Never store the right-bottom corner since it will get stored as a |
michael@0 | 5595 | bottom-right corner. |
michael@0 | 5596 | 4) Store the bottom-right corner, if it is the bottom right corner of the table. Never store it |
michael@0 | 5597 | otherwise, since it will get stored as either a right-top corner by a cell below or |
michael@0 | 5598 | a bottom-left corner from a cell to the right. |
michael@0 | 5599 | 5) Store the bottom-left corner, if (a) on the bottom edge of the table or (b) if the left edge hits |
michael@0 | 5600 | the top side of a colspan in its interior. Never store the corner otherwise, since it will |
michael@0 | 5601 | get stored as a right-top corner by a cell from below. |
michael@0 | 5602 | |
michael@0 | 5603 | XXX the BC-RTL hack - The correct fix would be a rewrite as described in bug 203686. |
michael@0 | 5604 | In order to draw borders in rtl conditions somehow correct, the existing structure which relies |
michael@0 | 5605 | heavily on the assumption that the next cell sibling will be on the right side, has been modified. |
michael@0 | 5606 | We flip the border during painting and during style lookup. Look for tableIsLTR for places where |
michael@0 | 5607 | the flipping is done. |
michael@0 | 5608 | */ |
michael@0 | 5609 | |
michael@0 | 5610 | |
michael@0 | 5611 | |
michael@0 | 5612 | // Calc the dominant border at every cell edge and corner within the current damage area |
michael@0 | 5613 | void |
michael@0 | 5614 | nsTableFrame::CalcBCBorders() |
michael@0 | 5615 | { |
michael@0 | 5616 | NS_ASSERTION(IsBorderCollapse(), |
michael@0 | 5617 | "calling CalcBCBorders on separated-border table"); |
michael@0 | 5618 | nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0(); |
michael@0 | 5619 | int32_t numRows = GetRowCount(); |
michael@0 | 5620 | int32_t numCols = GetColCount(); |
michael@0 | 5621 | if (!numRows || !numCols) |
michael@0 | 5622 | return; // nothing to do |
michael@0 | 5623 | |
michael@0 | 5624 | // Get the property holding the table damage area and border widths |
michael@0 | 5625 | BCPropertyData* propData = GetBCProperty(); |
michael@0 | 5626 | if (!propData) ABORT0(); |
michael@0 | 5627 | |
michael@0 | 5628 | |
michael@0 | 5629 | |
michael@0 | 5630 | // calculate an expanded damage area |
michael@0 | 5631 | nsIntRect damageArea(propData->mDamageArea); |
michael@0 | 5632 | ExpandBCDamageArea(damageArea); |
michael@0 | 5633 | |
michael@0 | 5634 | // segments that are on the table border edges need |
michael@0 | 5635 | // to be initialized only once |
michael@0 | 5636 | bool tableBorderReset[4]; |
michael@0 | 5637 | for (uint32_t sideX = NS_SIDE_TOP; sideX <= NS_SIDE_LEFT; sideX++) { |
michael@0 | 5638 | tableBorderReset[sideX] = false; |
michael@0 | 5639 | } |
michael@0 | 5640 | |
michael@0 | 5641 | // vertical borders indexed in x-direction (cols) |
michael@0 | 5642 | BCCellBorders lastVerBorders(damageArea.width + 1, damageArea.x); |
michael@0 | 5643 | if (!lastVerBorders.borders) ABORT0(); |
michael@0 | 5644 | BCCellBorder lastTopBorder, lastBottomBorder; |
michael@0 | 5645 | // horizontal borders indexed in x-direction (cols) |
michael@0 | 5646 | BCCellBorders lastBottomBorders(damageArea.width + 1, damageArea.x); |
michael@0 | 5647 | if (!lastBottomBorders.borders) ABORT0(); |
michael@0 | 5648 | bool startSeg; |
michael@0 | 5649 | bool gotRowBorder = false; |
michael@0 | 5650 | |
michael@0 | 5651 | BCMapCellInfo info(this), ajaInfo(this); |
michael@0 | 5652 | |
michael@0 | 5653 | BCCellBorder currentBorder, adjacentBorder; |
michael@0 | 5654 | BCCorners topCorners(damageArea.width + 1, damageArea.x); |
michael@0 | 5655 | if (!topCorners.corners) ABORT0(); |
michael@0 | 5656 | BCCorners bottomCorners(damageArea.width + 1, damageArea.x); |
michael@0 | 5657 | if (!bottomCorners.corners) ABORT0(); |
michael@0 | 5658 | |
michael@0 | 5659 | BCMapCellIterator iter(this, damageArea); |
michael@0 | 5660 | for (iter.First(info); !iter.mAtEnd; iter.Next(info)) { |
michael@0 | 5661 | // see if lastTopBorder, lastBottomBorder need to be reset |
michael@0 | 5662 | if (iter.IsNewRow()) { |
michael@0 | 5663 | gotRowBorder = false; |
michael@0 | 5664 | lastTopBorder.Reset(info.mRowIndex, info.mRowSpan); |
michael@0 | 5665 | lastBottomBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan); |
michael@0 | 5666 | } |
michael@0 | 5667 | else if (info.mColIndex > damageArea.x) { |
michael@0 | 5668 | lastBottomBorder = lastBottomBorders[info.mColIndex - 1]; |
michael@0 | 5669 | if (info.mRowIndex > |
michael@0 | 5670 | (lastBottomBorder.rowIndex - lastBottomBorder.rowSpan)) { |
michael@0 | 5671 | // the top border's left edge butts against the middle of a rowspan |
michael@0 | 5672 | lastTopBorder.Reset(info.mRowIndex, info.mRowSpan); |
michael@0 | 5673 | } |
michael@0 | 5674 | if (lastBottomBorder.rowIndex > (info.GetCellEndRowIndex() + 1)) { |
michael@0 | 5675 | // the bottom border's left edge butts against the middle of a rowspan |
michael@0 | 5676 | lastBottomBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan); |
michael@0 | 5677 | } |
michael@0 | 5678 | } |
michael@0 | 5679 | |
michael@0 | 5680 | // find the dominant border considering the cell's top border and the table, |
michael@0 | 5681 | // row group, row if the border is at the top of the table, otherwise it was |
michael@0 | 5682 | // processed in a previous row |
michael@0 | 5683 | if (0 == info.mRowIndex) { |
michael@0 | 5684 | if (!tableBorderReset[NS_SIDE_TOP]) { |
michael@0 | 5685 | propData->mTopBorderWidth = 0; |
michael@0 | 5686 | tableBorderReset[NS_SIDE_TOP] = true; |
michael@0 | 5687 | } |
michael@0 | 5688 | for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex(); |
michael@0 | 5689 | colX++) { |
michael@0 | 5690 | info.SetColumn(colX); |
michael@0 | 5691 | currentBorder = info.GetTopEdgeBorder(); |
michael@0 | 5692 | // update/store the top left & top right corners of the seg |
michael@0 | 5693 | BCCornerInfo& tlCorner = topCorners[colX]; // top left |
michael@0 | 5694 | if (0 == colX) { |
michael@0 | 5695 | // we are on right hand side of the corner |
michael@0 | 5696 | tlCorner.Set(NS_SIDE_RIGHT, currentBorder); |
michael@0 | 5697 | } |
michael@0 | 5698 | else { |
michael@0 | 5699 | tlCorner.Update(NS_SIDE_RIGHT, currentBorder); |
michael@0 | 5700 | tableCellMap->SetBCBorderCorner(eTopLeft, *iter.mCellMap, 0, 0, colX, |
michael@0 | 5701 | mozilla::css::Side(tlCorner.ownerSide), |
michael@0 | 5702 | tlCorner.subWidth, |
michael@0 | 5703 | tlCorner.bevel); |
michael@0 | 5704 | } |
michael@0 | 5705 | topCorners[colX + 1].Set(NS_SIDE_LEFT, currentBorder); // top right |
michael@0 | 5706 | // update lastTopBorder and see if a new segment starts |
michael@0 | 5707 | startSeg = SetHorBorder(currentBorder, tlCorner, lastTopBorder); |
michael@0 | 5708 | // store the border segment in the cell map |
michael@0 | 5709 | tableCellMap->SetBCBorderEdge(NS_SIDE_TOP, *iter.mCellMap, 0, 0, colX, |
michael@0 | 5710 | 1, currentBorder.owner, |
michael@0 | 5711 | currentBorder.width, startSeg); |
michael@0 | 5712 | |
michael@0 | 5713 | info.SetTableTopBorderWidth(currentBorder.width); |
michael@0 | 5714 | info.SetTopBorderWidths(currentBorder.width); |
michael@0 | 5715 | info.SetColumnTopRightContBCBorder(); |
michael@0 | 5716 | } |
michael@0 | 5717 | info.SetTableTopLeftContBCBorder(); |
michael@0 | 5718 | } |
michael@0 | 5719 | else { |
michael@0 | 5720 | // see if the top border needs to be the start of a segment due to a |
michael@0 | 5721 | // vertical border owning the corner |
michael@0 | 5722 | if (info.mColIndex > 0) { |
michael@0 | 5723 | BCData& data = info.mCellData->mData; |
michael@0 | 5724 | if (!data.IsTopStart()) { |
michael@0 | 5725 | mozilla::css::Side cornerSide; |
michael@0 | 5726 | bool bevel; |
michael@0 | 5727 | data.GetCorner(cornerSide, bevel); |
michael@0 | 5728 | if ((NS_SIDE_TOP == cornerSide) || (NS_SIDE_BOTTOM == cornerSide)) { |
michael@0 | 5729 | data.SetTopStart(true); |
michael@0 | 5730 | } |
michael@0 | 5731 | } |
michael@0 | 5732 | } |
michael@0 | 5733 | } |
michael@0 | 5734 | |
michael@0 | 5735 | // find the dominant border considering the cell's left border and the |
michael@0 | 5736 | // table, col group, col if the border is at the left of the table, |
michael@0 | 5737 | // otherwise it was processed in a previous col |
michael@0 | 5738 | if (0 == info.mColIndex) { |
michael@0 | 5739 | if (!tableBorderReset[NS_SIDE_LEFT]) { |
michael@0 | 5740 | propData->mLeftBorderWidth = 0; |
michael@0 | 5741 | tableBorderReset[NS_SIDE_LEFT] = true; |
michael@0 | 5742 | } |
michael@0 | 5743 | info.mCurrentRowFrame = nullptr; |
michael@0 | 5744 | for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex(); |
michael@0 | 5745 | rowY++) { |
michael@0 | 5746 | info.IncrementRow(rowY == info.mRowIndex); |
michael@0 | 5747 | currentBorder = info.GetLeftEdgeBorder(); |
michael@0 | 5748 | BCCornerInfo& tlCorner = (0 == rowY) ? topCorners[0] : bottomCorners[0]; |
michael@0 | 5749 | tlCorner.Update(NS_SIDE_BOTTOM, currentBorder); |
michael@0 | 5750 | tableCellMap->SetBCBorderCorner(eTopLeft, *iter.mCellMap, |
michael@0 | 5751 | iter.mRowGroupStart, rowY, 0, |
michael@0 | 5752 | mozilla::css::Side(tlCorner.ownerSide), |
michael@0 | 5753 | tlCorner.subWidth, |
michael@0 | 5754 | tlCorner.bevel); |
michael@0 | 5755 | bottomCorners[0].Set(NS_SIDE_TOP, currentBorder); // bottom left |
michael@0 | 5756 | |
michael@0 | 5757 | // update lastVerBordersBorder and see if a new segment starts |
michael@0 | 5758 | startSeg = SetBorder(currentBorder, lastVerBorders[0]); |
michael@0 | 5759 | // store the border segment in the cell map |
michael@0 | 5760 | tableCellMap->SetBCBorderEdge(NS_SIDE_LEFT, *iter.mCellMap, |
michael@0 | 5761 | iter.mRowGroupStart, rowY, info.mColIndex, |
michael@0 | 5762 | 1, currentBorder.owner, |
michael@0 | 5763 | currentBorder.width, startSeg); |
michael@0 | 5764 | info.SetTableLeftBorderWidth(rowY , currentBorder.width); |
michael@0 | 5765 | info.SetLeftBorderWidths(currentBorder.width); |
michael@0 | 5766 | info.SetRowLeftContBCBorder(); |
michael@0 | 5767 | } |
michael@0 | 5768 | info.SetRowGroupLeftContBCBorder(); |
michael@0 | 5769 | } |
michael@0 | 5770 | |
michael@0 | 5771 | // find the dominant border considering the cell's right border, adjacent |
michael@0 | 5772 | // cells and the table, row group, row |
michael@0 | 5773 | if (info.mNumTableCols == info.GetCellEndColIndex() + 1) { |
michael@0 | 5774 | // touches right edge of table |
michael@0 | 5775 | if (!tableBorderReset[NS_SIDE_RIGHT]) { |
michael@0 | 5776 | propData->mRightBorderWidth = 0; |
michael@0 | 5777 | tableBorderReset[NS_SIDE_RIGHT] = true; |
michael@0 | 5778 | } |
michael@0 | 5779 | info.mCurrentRowFrame = nullptr; |
michael@0 | 5780 | for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex(); |
michael@0 | 5781 | rowY++) { |
michael@0 | 5782 | info.IncrementRow(rowY == info.mRowIndex); |
michael@0 | 5783 | currentBorder = info.GetRightEdgeBorder(); |
michael@0 | 5784 | // update/store the top right & bottom right corners |
michael@0 | 5785 | BCCornerInfo& trCorner = (0 == rowY) ? |
michael@0 | 5786 | topCorners[info.GetCellEndColIndex() + 1] : |
michael@0 | 5787 | bottomCorners[info.GetCellEndColIndex() + 1]; |
michael@0 | 5788 | trCorner.Update(NS_SIDE_BOTTOM, currentBorder); // top right |
michael@0 | 5789 | tableCellMap->SetBCBorderCorner(eTopRight, *iter.mCellMap, |
michael@0 | 5790 | iter.mRowGroupStart, rowY, |
michael@0 | 5791 | info.GetCellEndColIndex(), |
michael@0 | 5792 | mozilla::css::Side(trCorner.ownerSide), |
michael@0 | 5793 | trCorner.subWidth, |
michael@0 | 5794 | trCorner.bevel); |
michael@0 | 5795 | BCCornerInfo& brCorner = bottomCorners[info.GetCellEndColIndex() + 1]; |
michael@0 | 5796 | brCorner.Set(NS_SIDE_TOP, currentBorder); // bottom right |
michael@0 | 5797 | tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap, |
michael@0 | 5798 | iter.mRowGroupStart, rowY, |
michael@0 | 5799 | info.GetCellEndColIndex(), |
michael@0 | 5800 | mozilla::css::Side(brCorner.ownerSide), |
michael@0 | 5801 | brCorner.subWidth, |
michael@0 | 5802 | brCorner.bevel); |
michael@0 | 5803 | // update lastVerBorders and see if a new segment starts |
michael@0 | 5804 | startSeg = SetBorder(currentBorder, |
michael@0 | 5805 | lastVerBorders[info.GetCellEndColIndex() + 1]); |
michael@0 | 5806 | // store the border segment in the cell map and update cellBorders |
michael@0 | 5807 | tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *iter.mCellMap, |
michael@0 | 5808 | iter.mRowGroupStart, rowY, |
michael@0 | 5809 | info.GetCellEndColIndex(), 1, |
michael@0 | 5810 | currentBorder.owner, currentBorder.width, |
michael@0 | 5811 | startSeg); |
michael@0 | 5812 | info.SetTableRightBorderWidth(rowY, currentBorder.width); |
michael@0 | 5813 | info.SetRightBorderWidths(currentBorder.width); |
michael@0 | 5814 | info.SetRowRightContBCBorder(); |
michael@0 | 5815 | } |
michael@0 | 5816 | info.SetRowGroupRightContBCBorder(); |
michael@0 | 5817 | } |
michael@0 | 5818 | else { |
michael@0 | 5819 | int32_t segLength = 0; |
michael@0 | 5820 | BCMapCellInfo priorAjaInfo(this); |
michael@0 | 5821 | for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex(); |
michael@0 | 5822 | rowY += segLength) { |
michael@0 | 5823 | iter.PeekRight(info, rowY, ajaInfo); |
michael@0 | 5824 | currentBorder = info.GetRightInternalBorder(); |
michael@0 | 5825 | adjacentBorder = ajaInfo.GetLeftInternalBorder(); |
michael@0 | 5826 | currentBorder = CompareBorders(!CELL_CORNER, currentBorder, |
michael@0 | 5827 | adjacentBorder, !HORIZONTAL); |
michael@0 | 5828 | |
michael@0 | 5829 | segLength = std::max(1, ajaInfo.mRowIndex + ajaInfo.mRowSpan - rowY); |
michael@0 | 5830 | segLength = std::min(segLength, info.mRowIndex + info.mRowSpan - rowY); |
michael@0 | 5831 | |
michael@0 | 5832 | // update lastVerBorders and see if a new segment starts |
michael@0 | 5833 | startSeg = SetBorder(currentBorder, |
michael@0 | 5834 | lastVerBorders[info.GetCellEndColIndex() + 1]); |
michael@0 | 5835 | // store the border segment in the cell map and update cellBorders |
michael@0 | 5836 | if (info.GetCellEndColIndex() < damageArea.XMost() && |
michael@0 | 5837 | rowY >= damageArea.y && rowY < damageArea.YMost()) { |
michael@0 | 5838 | tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *iter.mCellMap, |
michael@0 | 5839 | iter.mRowGroupStart, rowY, |
michael@0 | 5840 | info.GetCellEndColIndex(), segLength, |
michael@0 | 5841 | currentBorder.owner, |
michael@0 | 5842 | currentBorder.width, startSeg); |
michael@0 | 5843 | info.SetRightBorderWidths(currentBorder.width); |
michael@0 | 5844 | ajaInfo.SetLeftBorderWidths(currentBorder.width); |
michael@0 | 5845 | } |
michael@0 | 5846 | // update the top right corner |
michael@0 | 5847 | bool hitsSpanOnRight = (rowY > ajaInfo.mRowIndex) && |
michael@0 | 5848 | (rowY < ajaInfo.mRowIndex + ajaInfo.mRowSpan); |
michael@0 | 5849 | BCCornerInfo* trCorner = ((0 == rowY) || hitsSpanOnRight) ? |
michael@0 | 5850 | &topCorners[info.GetCellEndColIndex() + 1] : |
michael@0 | 5851 | &bottomCorners[info.GetCellEndColIndex() + 1]; |
michael@0 | 5852 | trCorner->Update(NS_SIDE_BOTTOM, currentBorder); |
michael@0 | 5853 | // if this is not the first time through, |
michael@0 | 5854 | // consider the segment to the right |
michael@0 | 5855 | if (rowY != info.mRowIndex) { |
michael@0 | 5856 | currentBorder = priorAjaInfo.GetBottomInternalBorder(); |
michael@0 | 5857 | adjacentBorder = ajaInfo.GetTopInternalBorder(); |
michael@0 | 5858 | currentBorder = CompareBorders(!CELL_CORNER, currentBorder, |
michael@0 | 5859 | adjacentBorder, HORIZONTAL); |
michael@0 | 5860 | trCorner->Update(NS_SIDE_RIGHT, currentBorder); |
michael@0 | 5861 | } |
michael@0 | 5862 | // store the top right corner in the cell map |
michael@0 | 5863 | if (info.GetCellEndColIndex() < damageArea.XMost() && |
michael@0 | 5864 | rowY >= damageArea.y) { |
michael@0 | 5865 | if (0 != rowY) { |
michael@0 | 5866 | tableCellMap->SetBCBorderCorner(eTopRight, *iter.mCellMap, |
michael@0 | 5867 | iter.mRowGroupStart, rowY, |
michael@0 | 5868 | info.GetCellEndColIndex(), |
michael@0 | 5869 | mozilla::css::Side(trCorner->ownerSide), |
michael@0 | 5870 | trCorner->subWidth, |
michael@0 | 5871 | trCorner->bevel); |
michael@0 | 5872 | } |
michael@0 | 5873 | // store any corners this cell spans together with the aja cell |
michael@0 | 5874 | for (int32_t rX = rowY + 1; rX < rowY + segLength; rX++) { |
michael@0 | 5875 | tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap, |
michael@0 | 5876 | iter.mRowGroupStart, rX, |
michael@0 | 5877 | info.GetCellEndColIndex(), |
michael@0 | 5878 | mozilla::css::Side(trCorner->ownerSide), |
michael@0 | 5879 | trCorner->subWidth, false); |
michael@0 | 5880 | } |
michael@0 | 5881 | } |
michael@0 | 5882 | // update bottom right corner, topCorners, bottomCorners |
michael@0 | 5883 | hitsSpanOnRight = (rowY + segLength < |
michael@0 | 5884 | ajaInfo.mRowIndex + ajaInfo.mRowSpan); |
michael@0 | 5885 | BCCornerInfo& brCorner = (hitsSpanOnRight) ? |
michael@0 | 5886 | topCorners[info.GetCellEndColIndex() + 1] : |
michael@0 | 5887 | bottomCorners[info.GetCellEndColIndex() + 1]; |
michael@0 | 5888 | brCorner.Set(NS_SIDE_TOP, currentBorder); |
michael@0 | 5889 | priorAjaInfo = ajaInfo; |
michael@0 | 5890 | } |
michael@0 | 5891 | } |
michael@0 | 5892 | for (int32_t colX = info.mColIndex + 1; colX <= info.GetCellEndColIndex(); |
michael@0 | 5893 | colX++) { |
michael@0 | 5894 | lastVerBorders[colX].Reset(0,1); |
michael@0 | 5895 | } |
michael@0 | 5896 | |
michael@0 | 5897 | // find the dominant border considering the cell's bottom border, adjacent |
michael@0 | 5898 | // cells and the table, row group, row |
michael@0 | 5899 | if (info.mNumTableRows == info.GetCellEndRowIndex() + 1) { |
michael@0 | 5900 | // touches bottom edge of table |
michael@0 | 5901 | if (!tableBorderReset[NS_SIDE_BOTTOM]) { |
michael@0 | 5902 | propData->mBottomBorderWidth = 0; |
michael@0 | 5903 | tableBorderReset[NS_SIDE_BOTTOM] = true; |
michael@0 | 5904 | } |
michael@0 | 5905 | for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex(); |
michael@0 | 5906 | colX++) { |
michael@0 | 5907 | info.SetColumn(colX); |
michael@0 | 5908 | currentBorder = info.GetBottomEdgeBorder(); |
michael@0 | 5909 | // update/store the bottom left & bottom right corners |
michael@0 | 5910 | BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left |
michael@0 | 5911 | blCorner.Update(NS_SIDE_RIGHT, currentBorder); |
michael@0 | 5912 | tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap, |
michael@0 | 5913 | iter.mRowGroupStart, |
michael@0 | 5914 | info.GetCellEndRowIndex(), |
michael@0 | 5915 | colX, |
michael@0 | 5916 | mozilla::css::Side(blCorner.ownerSide), |
michael@0 | 5917 | blCorner.subWidth, blCorner.bevel); |
michael@0 | 5918 | BCCornerInfo& brCorner = bottomCorners[colX + 1]; // bottom right |
michael@0 | 5919 | brCorner.Update(NS_SIDE_LEFT, currentBorder); |
michael@0 | 5920 | if (info.mNumTableCols == colX + 1) { // lower right corner of the table |
michael@0 | 5921 | tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap, |
michael@0 | 5922 | iter.mRowGroupStart, |
michael@0 | 5923 | info.GetCellEndRowIndex(),colX, |
michael@0 | 5924 | mozilla::css::Side(brCorner.ownerSide), |
michael@0 | 5925 | brCorner.subWidth, |
michael@0 | 5926 | brCorner.bevel, true); |
michael@0 | 5927 | } |
michael@0 | 5928 | // update lastBottomBorder and see if a new segment starts |
michael@0 | 5929 | startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder); |
michael@0 | 5930 | if (!startSeg) { |
michael@0 | 5931 | // make sure that we did not compare apples to oranges i.e. the |
michael@0 | 5932 | // current border should be a continuation of the lastBottomBorder, |
michael@0 | 5933 | // as it is a bottom border |
michael@0 | 5934 | // add 1 to the info.GetCellEndRowIndex() |
michael@0 | 5935 | startSeg = (lastBottomBorder.rowIndex != |
michael@0 | 5936 | (info.GetCellEndRowIndex() + 1)); |
michael@0 | 5937 | } |
michael@0 | 5938 | // store the border segment in the cell map and update cellBorders |
michael@0 | 5939 | tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *iter.mCellMap, |
michael@0 | 5940 | iter.mRowGroupStart, |
michael@0 | 5941 | info.GetCellEndRowIndex(), |
michael@0 | 5942 | colX, 1, currentBorder.owner, |
michael@0 | 5943 | currentBorder.width, startSeg); |
michael@0 | 5944 | // update lastBottomBorders |
michael@0 | 5945 | lastBottomBorder.rowIndex = info.GetCellEndRowIndex() + 1; |
michael@0 | 5946 | lastBottomBorder.rowSpan = info.mRowSpan; |
michael@0 | 5947 | lastBottomBorders[colX] = lastBottomBorder; |
michael@0 | 5948 | |
michael@0 | 5949 | info.SetBottomBorderWidths(currentBorder.width); |
michael@0 | 5950 | info.SetTableBottomBorderWidth(currentBorder.width); |
michael@0 | 5951 | info.SetColumnBottomContBCBorder(); |
michael@0 | 5952 | } |
michael@0 | 5953 | info.SetRowGroupBottomContBCBorder(); |
michael@0 | 5954 | info.SetColGroupBottomContBCBorder(); |
michael@0 | 5955 | } |
michael@0 | 5956 | else { |
michael@0 | 5957 | int32_t segLength = 0; |
michael@0 | 5958 | for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex(); |
michael@0 | 5959 | colX += segLength) { |
michael@0 | 5960 | iter.PeekBottom(info, colX, ajaInfo); |
michael@0 | 5961 | currentBorder = info.GetBottomInternalBorder(); |
michael@0 | 5962 | adjacentBorder = ajaInfo.GetTopInternalBorder(); |
michael@0 | 5963 | currentBorder = CompareBorders(!CELL_CORNER, currentBorder, |
michael@0 | 5964 | adjacentBorder, HORIZONTAL); |
michael@0 | 5965 | segLength = std::max(1, ajaInfo.mColIndex + ajaInfo.mColSpan - colX); |
michael@0 | 5966 | segLength = std::min(segLength, info.mColIndex + info.mColSpan - colX); |
michael@0 | 5967 | |
michael@0 | 5968 | // update, store the bottom left corner |
michael@0 | 5969 | BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left |
michael@0 | 5970 | bool hitsSpanBelow = (colX > ajaInfo.mColIndex) && |
michael@0 | 5971 | (colX < ajaInfo.mColIndex + ajaInfo.mColSpan); |
michael@0 | 5972 | bool update = true; |
michael@0 | 5973 | if ((colX == info.mColIndex) && (colX > damageArea.x)) { |
michael@0 | 5974 | int32_t prevRowIndex = lastBottomBorders[colX - 1].rowIndex; |
michael@0 | 5975 | if (prevRowIndex > info.GetCellEndRowIndex() + 1) { |
michael@0 | 5976 | // hits a rowspan on the right |
michael@0 | 5977 | update = false; |
michael@0 | 5978 | // the corner was taken care of during the cell on the left |
michael@0 | 5979 | } |
michael@0 | 5980 | else if (prevRowIndex < info.GetCellEndRowIndex() + 1) { |
michael@0 | 5981 | // spans below the cell to the left |
michael@0 | 5982 | topCorners[colX] = blCorner; |
michael@0 | 5983 | blCorner.Set(NS_SIDE_RIGHT, currentBorder); |
michael@0 | 5984 | update = false; |
michael@0 | 5985 | } |
michael@0 | 5986 | } |
michael@0 | 5987 | if (update) { |
michael@0 | 5988 | blCorner.Update(NS_SIDE_RIGHT, currentBorder); |
michael@0 | 5989 | } |
michael@0 | 5990 | if (info.GetCellEndRowIndex() < damageArea.YMost() && |
michael@0 | 5991 | (colX >= damageArea.x)) { |
michael@0 | 5992 | if (hitsSpanBelow) { |
michael@0 | 5993 | tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap, |
michael@0 | 5994 | iter.mRowGroupStart, |
michael@0 | 5995 | info.GetCellEndRowIndex(), colX, |
michael@0 | 5996 | mozilla::css::Side(blCorner.ownerSide), |
michael@0 | 5997 | blCorner.subWidth, blCorner.bevel); |
michael@0 | 5998 | } |
michael@0 | 5999 | // store any corners this cell spans together with the aja cell |
michael@0 | 6000 | for (int32_t cX = colX + 1; cX < colX + segLength; cX++) { |
michael@0 | 6001 | BCCornerInfo& corner = bottomCorners[cX]; |
michael@0 | 6002 | corner.Set(NS_SIDE_RIGHT, currentBorder); |
michael@0 | 6003 | tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap, |
michael@0 | 6004 | iter.mRowGroupStart, |
michael@0 | 6005 | info.GetCellEndRowIndex(), cX, |
michael@0 | 6006 | mozilla::css::Side(corner.ownerSide), |
michael@0 | 6007 | corner.subWidth, |
michael@0 | 6008 | false); |
michael@0 | 6009 | } |
michael@0 | 6010 | } |
michael@0 | 6011 | // update lastBottomBorders and see if a new segment starts |
michael@0 | 6012 | startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder); |
michael@0 | 6013 | if (!startSeg) { |
michael@0 | 6014 | // make sure that we did not compare apples to oranges i.e. the |
michael@0 | 6015 | // current border should be a continuation of the lastBottomBorder, |
michael@0 | 6016 | // as it is a bottom border |
michael@0 | 6017 | // add 1 to the info.GetCellEndRowIndex() |
michael@0 | 6018 | startSeg = (lastBottomBorder.rowIndex != |
michael@0 | 6019 | info.GetCellEndRowIndex() + 1); |
michael@0 | 6020 | } |
michael@0 | 6021 | lastBottomBorder.rowIndex = info.GetCellEndRowIndex() + 1; |
michael@0 | 6022 | lastBottomBorder.rowSpan = info.mRowSpan; |
michael@0 | 6023 | for (int32_t cX = colX; cX < colX + segLength; cX++) { |
michael@0 | 6024 | lastBottomBorders[cX] = lastBottomBorder; |
michael@0 | 6025 | } |
michael@0 | 6026 | |
michael@0 | 6027 | // store the border segment the cell map and update cellBorders |
michael@0 | 6028 | if (info.GetCellEndRowIndex() < damageArea.YMost() && |
michael@0 | 6029 | (colX >= damageArea.x) && |
michael@0 | 6030 | (colX < damageArea.XMost())) { |
michael@0 | 6031 | tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *iter.mCellMap, |
michael@0 | 6032 | iter.mRowGroupStart, |
michael@0 | 6033 | info.GetCellEndRowIndex(), |
michael@0 | 6034 | colX, segLength, currentBorder.owner, |
michael@0 | 6035 | currentBorder.width, startSeg); |
michael@0 | 6036 | info.SetBottomBorderWidths(currentBorder.width); |
michael@0 | 6037 | ajaInfo.SetTopBorderWidths(currentBorder.width); |
michael@0 | 6038 | } |
michael@0 | 6039 | // update bottom right corner |
michael@0 | 6040 | BCCornerInfo& brCorner = bottomCorners[colX + segLength]; |
michael@0 | 6041 | brCorner.Update(NS_SIDE_LEFT, currentBorder); |
michael@0 | 6042 | } |
michael@0 | 6043 | if (!gotRowBorder && 1 == info.mRowSpan && |
michael@0 | 6044 | (ajaInfo.mTopRow || info.mRgAtBottom)) { |
michael@0 | 6045 | //get continuous row/row group border |
michael@0 | 6046 | //we need to check the row group's bottom border if this is |
michael@0 | 6047 | //the last row in the row group, but only a cell with rowspan=1 |
michael@0 | 6048 | //will know whether *this* row is at the bottom |
michael@0 | 6049 | const nsIFrame* nextRowGroup = (ajaInfo.mRgAtTop) ? ajaInfo.mRowGroup : |
michael@0 | 6050 | nullptr; |
michael@0 | 6051 | info.SetInnerRowGroupBottomContBCBorder(nextRowGroup, ajaInfo.mTopRow); |
michael@0 | 6052 | gotRowBorder = true; |
michael@0 | 6053 | } |
michael@0 | 6054 | } |
michael@0 | 6055 | |
michael@0 | 6056 | // see if the cell to the right had a rowspan and its lower left border |
michael@0 | 6057 | // needs be joined with this one's bottom |
michael@0 | 6058 | // if there is a cell to the right and the cell to right was a rowspan |
michael@0 | 6059 | if ((info.mNumTableCols != info.GetCellEndColIndex() + 1) && |
michael@0 | 6060 | (lastBottomBorders[info.GetCellEndColIndex() + 1].rowSpan > 1)) { |
michael@0 | 6061 | BCCornerInfo& corner = bottomCorners[info.GetCellEndColIndex() + 1]; |
michael@0 | 6062 | if ((NS_SIDE_TOP != corner.ownerSide) && |
michael@0 | 6063 | (NS_SIDE_BOTTOM != corner.ownerSide)) { |
michael@0 | 6064 | // not a vertical owner |
michael@0 | 6065 | BCCellBorder& thisBorder = lastBottomBorder; |
michael@0 | 6066 | BCCellBorder& nextBorder = lastBottomBorders[info.mColIndex + 1]; |
michael@0 | 6067 | if ((thisBorder.color == nextBorder.color) && |
michael@0 | 6068 | (thisBorder.width == nextBorder.width) && |
michael@0 | 6069 | (thisBorder.style == nextBorder.style)) { |
michael@0 | 6070 | // set the flag on the next border indicating it is not the start of a |
michael@0 | 6071 | // new segment |
michael@0 | 6072 | if (iter.mCellMap) { |
michael@0 | 6073 | tableCellMap->ResetTopStart(NS_SIDE_BOTTOM, *iter.mCellMap, |
michael@0 | 6074 | info.GetCellEndRowIndex(), |
michael@0 | 6075 | info.GetCellEndColIndex() + 1); |
michael@0 | 6076 | } |
michael@0 | 6077 | } |
michael@0 | 6078 | } |
michael@0 | 6079 | } |
michael@0 | 6080 | } // for (iter.First(info); info.mCell; iter.Next(info)) { |
michael@0 | 6081 | // reset the bc flag and damage area |
michael@0 | 6082 | SetNeedToCalcBCBorders(false); |
michael@0 | 6083 | propData->mDamageArea = nsIntRect(0,0,0,0); |
michael@0 | 6084 | #ifdef DEBUG_TABLE_CELLMAP |
michael@0 | 6085 | mCellMap->Dump(); |
michael@0 | 6086 | #endif |
michael@0 | 6087 | } |
michael@0 | 6088 | |
michael@0 | 6089 | class BCPaintBorderIterator; |
michael@0 | 6090 | |
michael@0 | 6091 | struct BCVerticalSeg |
michael@0 | 6092 | { |
michael@0 | 6093 | BCVerticalSeg(); |
michael@0 | 6094 | |
michael@0 | 6095 | void Start(BCPaintBorderIterator& aIter, |
michael@0 | 6096 | BCBorderOwner aBorderOwner, |
michael@0 | 6097 | BCPixelSize aVerSegWidth, |
michael@0 | 6098 | BCPixelSize aHorSegHeight); |
michael@0 | 6099 | |
michael@0 | 6100 | void Initialize(BCPaintBorderIterator& aIter); |
michael@0 | 6101 | void GetBottomCorner(BCPaintBorderIterator& aIter, |
michael@0 | 6102 | BCPixelSize aHorSegHeight); |
michael@0 | 6103 | |
michael@0 | 6104 | |
michael@0 | 6105 | void Paint(BCPaintBorderIterator& aIter, |
michael@0 | 6106 | nsRenderingContext& aRenderingContext, |
michael@0 | 6107 | BCPixelSize aHorSegHeight); |
michael@0 | 6108 | void AdvanceOffsetY(); |
michael@0 | 6109 | void IncludeCurrentBorder(BCPaintBorderIterator& aIter); |
michael@0 | 6110 | |
michael@0 | 6111 | |
michael@0 | 6112 | union { |
michael@0 | 6113 | nsTableColFrame* mCol; |
michael@0 | 6114 | int32_t mColWidth; |
michael@0 | 6115 | }; |
michael@0 | 6116 | nscoord mOffsetX; // x-offset with respect to the table edge |
michael@0 | 6117 | nscoord mOffsetY; // y-offset with respect to the table edge |
michael@0 | 6118 | nscoord mLength; // vertical length including corners |
michael@0 | 6119 | BCPixelSize mWidth; // width in pixels |
michael@0 | 6120 | |
michael@0 | 6121 | nsTableCellFrame* mAjaCell; // previous sibling to the first cell |
michael@0 | 6122 | // where the segment starts, it can be |
michael@0 | 6123 | // the owner of a segment |
michael@0 | 6124 | nsTableCellFrame* mFirstCell; // cell at the start of the segment |
michael@0 | 6125 | nsTableRowGroupFrame* mFirstRowGroup; // row group at the start of the segment |
michael@0 | 6126 | nsTableRowFrame* mFirstRow; // row at the start of the segment |
michael@0 | 6127 | nsTableCellFrame* mLastCell; // cell at the current end of the |
michael@0 | 6128 | // segment |
michael@0 | 6129 | |
michael@0 | 6130 | |
michael@0 | 6131 | uint8_t mOwner; // owner of the border, defines the |
michael@0 | 6132 | // style |
michael@0 | 6133 | mozilla::css::Side mTopBevelSide; // direction to bevel at the top |
michael@0 | 6134 | nscoord mTopBevelOffset; // how much to bevel at the top |
michael@0 | 6135 | BCPixelSize mBottomHorSegHeight; // height of the crossing |
michael@0 | 6136 | //horizontal border |
michael@0 | 6137 | nscoord mBottomOffset; // how much longer is the segment due |
michael@0 | 6138 | // to the horizontal border, by this |
michael@0 | 6139 | // amount the next segment needs to be |
michael@0 | 6140 | // shifted. |
michael@0 | 6141 | bool mIsBottomBevel; // should we bevel at the bottom |
michael@0 | 6142 | }; |
michael@0 | 6143 | |
michael@0 | 6144 | struct BCHorizontalSeg |
michael@0 | 6145 | { |
michael@0 | 6146 | BCHorizontalSeg(); |
michael@0 | 6147 | |
michael@0 | 6148 | void Start(BCPaintBorderIterator& aIter, |
michael@0 | 6149 | BCBorderOwner aBorderOwner, |
michael@0 | 6150 | BCPixelSize aBottomVerSegWidth, |
michael@0 | 6151 | BCPixelSize aHorSegHeight); |
michael@0 | 6152 | void GetRightCorner(BCPaintBorderIterator& aIter, |
michael@0 | 6153 | BCPixelSize aLeftSegWidth); |
michael@0 | 6154 | void AdvanceOffsetX(int32_t aIncrement); |
michael@0 | 6155 | void IncludeCurrentBorder(BCPaintBorderIterator& aIter); |
michael@0 | 6156 | void Paint(BCPaintBorderIterator& aIter, |
michael@0 | 6157 | nsRenderingContext& aRenderingContext); |
michael@0 | 6158 | |
michael@0 | 6159 | nscoord mOffsetX; // x-offset with respect to the table edge |
michael@0 | 6160 | nscoord mOffsetY; // y-offset with respect to the table edge |
michael@0 | 6161 | nscoord mLength; // horizontal length including corners |
michael@0 | 6162 | BCPixelSize mWidth; // border width in pixels |
michael@0 | 6163 | nscoord mLeftBevelOffset; // how much to bevel at the left |
michael@0 | 6164 | mozilla::css::Side mLeftBevelSide; // direction to bevel at the left |
michael@0 | 6165 | bool mIsRightBevel; // should we bevel at the right end |
michael@0 | 6166 | nscoord mRightBevelOffset; // how much to bevel at the right |
michael@0 | 6167 | mozilla::css::Side mRightBevelSide; // direction to bevel at the right |
michael@0 | 6168 | nscoord mEndOffset; // how much longer is the segment due |
michael@0 | 6169 | // to the vertical border, by this |
michael@0 | 6170 | // amount the next segment needs to be |
michael@0 | 6171 | // shifted. |
michael@0 | 6172 | uint8_t mOwner; // owner of the border, defines the |
michael@0 | 6173 | // style |
michael@0 | 6174 | nsTableCellFrame* mFirstCell; // cell at the start of the segment |
michael@0 | 6175 | nsTableCellFrame* mAjaCell; // neighboring cell to the first cell |
michael@0 | 6176 | // where the segment starts, it can be |
michael@0 | 6177 | // the owner of a segment |
michael@0 | 6178 | }; |
michael@0 | 6179 | |
michael@0 | 6180 | // Iterates over borders (left border, corner, top border) in the cell map within a damage area |
michael@0 | 6181 | // from left to right, top to bottom. All members are in terms of the 1st in flow frames, except |
michael@0 | 6182 | // where suffixed by InFlow. |
michael@0 | 6183 | class BCPaintBorderIterator |
michael@0 | 6184 | { |
michael@0 | 6185 | public: |
michael@0 | 6186 | |
michael@0 | 6187 | |
michael@0 | 6188 | BCPaintBorderIterator(nsTableFrame* aTable); |
michael@0 | 6189 | ~BCPaintBorderIterator() { if (mVerInfo) { |
michael@0 | 6190 | delete [] mVerInfo; |
michael@0 | 6191 | }} |
michael@0 | 6192 | void Reset(); |
michael@0 | 6193 | |
michael@0 | 6194 | /** |
michael@0 | 6195 | * Determine the damage area in terms of rows and columns and finalize |
michael@0 | 6196 | * mInitialOffsetX and mInitialOffsetY. |
michael@0 | 6197 | * @param aDirtyRect - dirty rect in table coordinates |
michael@0 | 6198 | * @return - true if we need to paint something given dirty rect |
michael@0 | 6199 | */ |
michael@0 | 6200 | bool SetDamageArea(const nsRect& aDamageRect); |
michael@0 | 6201 | void First(); |
michael@0 | 6202 | void Next(); |
michael@0 | 6203 | void AccumulateOrPaintHorizontalSegment(nsRenderingContext& aRenderingContext); |
michael@0 | 6204 | void AccumulateOrPaintVerticalSegment(nsRenderingContext& aRenderingContext); |
michael@0 | 6205 | void ResetVerInfo(); |
michael@0 | 6206 | void StoreColumnWidth(int32_t aIndex); |
michael@0 | 6207 | bool VerticalSegmentOwnsCorner(); |
michael@0 | 6208 | |
michael@0 | 6209 | nsTableFrame* mTable; |
michael@0 | 6210 | nsTableFrame* mTableFirstInFlow; |
michael@0 | 6211 | nsTableCellMap* mTableCellMap; |
michael@0 | 6212 | nsCellMap* mCellMap; |
michael@0 | 6213 | bool mTableIsLTR; |
michael@0 | 6214 | int32_t mColInc; // +1 for ltr -1 for rtl |
michael@0 | 6215 | const nsStyleBackground* mTableBgColor; |
michael@0 | 6216 | nsTableFrame::RowGroupArray mRowGroups; |
michael@0 | 6217 | |
michael@0 | 6218 | nsTableRowGroupFrame* mPrevRg; |
michael@0 | 6219 | nsTableRowGroupFrame* mRg; |
michael@0 | 6220 | bool mIsRepeatedHeader; |
michael@0 | 6221 | bool mIsRepeatedFooter; |
michael@0 | 6222 | nsTableRowGroupFrame* mStartRg; // first row group in the damagearea |
michael@0 | 6223 | int32_t mRgIndex; // current row group index in the |
michael@0 | 6224 | // mRowgroups array |
michael@0 | 6225 | int32_t mFifRgFirstRowIndex; // start row index of the first in |
michael@0 | 6226 | // flow of the row group |
michael@0 | 6227 | int32_t mRgFirstRowIndex; // row index of the first row in the |
michael@0 | 6228 | // row group |
michael@0 | 6229 | int32_t mRgLastRowIndex; // row index of the last row in the row |
michael@0 | 6230 | // group |
michael@0 | 6231 | int32_t mNumTableRows; // number of rows in the table and all |
michael@0 | 6232 | // continuations |
michael@0 | 6233 | int32_t mNumTableCols; // number of columns in the table |
michael@0 | 6234 | int32_t mColIndex; // with respect to the table |
michael@0 | 6235 | int32_t mRowIndex; // with respect to the table |
michael@0 | 6236 | int32_t mRepeatedHeaderRowIndex; // row index in a repeated |
michael@0 | 6237 | //header, it's equivalent to |
michael@0 | 6238 | // mRowIndex when we're in a repeated |
michael@0 | 6239 | // header, and set to the last row |
michael@0 | 6240 | // index of a repeated header when |
michael@0 | 6241 | // we're not |
michael@0 | 6242 | bool mIsNewRow; |
michael@0 | 6243 | bool mAtEnd; // the iterator cycled over all |
michael@0 | 6244 | // borders |
michael@0 | 6245 | nsTableRowFrame* mPrevRow; |
michael@0 | 6246 | nsTableRowFrame* mRow; |
michael@0 | 6247 | nsTableRowFrame* mStartRow; //first row in a inside the damagearea |
michael@0 | 6248 | |
michael@0 | 6249 | |
michael@0 | 6250 | // cell properties |
michael@0 | 6251 | nsTableCellFrame* mPrevCell; |
michael@0 | 6252 | nsTableCellFrame* mCell; |
michael@0 | 6253 | BCCellData* mPrevCellData; |
michael@0 | 6254 | BCCellData* mCellData; |
michael@0 | 6255 | BCData* mBCData; |
michael@0 | 6256 | |
michael@0 | 6257 | bool IsTableTopMost() {return (mRowIndex == 0) && !mTable->GetPrevInFlow();} |
michael@0 | 6258 | bool IsTableRightMost() {return (mColIndex >= mNumTableCols);} |
michael@0 | 6259 | bool IsTableBottomMost() {return (mRowIndex >= mNumTableRows) && !mTable->GetNextInFlow();} |
michael@0 | 6260 | bool IsTableLeftMost() {return (mColIndex == 0);} |
michael@0 | 6261 | bool IsDamageAreaTopMost() {return (mRowIndex == mDamageArea.y);} |
michael@0 | 6262 | bool IsDamageAreaRightMost() {return (mColIndex >= mDamageArea.XMost());} |
michael@0 | 6263 | bool IsDamageAreaBottomMost() {return (mRowIndex >= mDamageArea.YMost());} |
michael@0 | 6264 | bool IsDamageAreaLeftMost() {return (mColIndex == mDamageArea.x);} |
michael@0 | 6265 | int32_t GetRelativeColIndex() {return (mColIndex - mDamageArea.x);} |
michael@0 | 6266 | |
michael@0 | 6267 | nsIntRect mDamageArea; // damageArea in cellmap coordinates |
michael@0 | 6268 | bool IsAfterRepeatedHeader() { return !mIsRepeatedHeader && (mRowIndex == (mRepeatedHeaderRowIndex + 1));} |
michael@0 | 6269 | bool StartRepeatedFooter() {return mIsRepeatedFooter && (mRowIndex == mRgFirstRowIndex) && (mRowIndex != mDamageArea.y);} |
michael@0 | 6270 | nscoord mInitialOffsetX; // offsetX of the first border with |
michael@0 | 6271 | // respect to the table |
michael@0 | 6272 | nscoord mInitialOffsetY; // offsetY of the first border with |
michael@0 | 6273 | // respect to the table |
michael@0 | 6274 | nscoord mNextOffsetY; // offsetY of the next segment |
michael@0 | 6275 | BCVerticalSeg* mVerInfo; // this array is used differently when |
michael@0 | 6276 | // horizontal and vertical borders are drawn |
michael@0 | 6277 | // When horizontal border are drawn we cache |
michael@0 | 6278 | // the column widths and the width of the |
michael@0 | 6279 | // vertical borders that arrive from top |
michael@0 | 6280 | // When we draw vertical borders we store |
michael@0 | 6281 | // lengths and width for vertical borders |
michael@0 | 6282 | // before they are drawn while we move over |
michael@0 | 6283 | // the columns in the damage area |
michael@0 | 6284 | // It has one more elements than columns are |
michael@0 | 6285 | //in the table. |
michael@0 | 6286 | BCHorizontalSeg mHorSeg; // the horizontal segment while we |
michael@0 | 6287 | // move over the colums |
michael@0 | 6288 | BCPixelSize mPrevHorSegHeight; // the height of the previous |
michael@0 | 6289 | // horizontal border |
michael@0 | 6290 | |
michael@0 | 6291 | private: |
michael@0 | 6292 | |
michael@0 | 6293 | bool SetNewRow(nsTableRowFrame* aRow = nullptr); |
michael@0 | 6294 | bool SetNewRowGroup(); |
michael@0 | 6295 | void SetNewData(int32_t aRowIndex, int32_t aColIndex); |
michael@0 | 6296 | |
michael@0 | 6297 | }; |
michael@0 | 6298 | |
michael@0 | 6299 | |
michael@0 | 6300 | |
michael@0 | 6301 | BCPaintBorderIterator::BCPaintBorderIterator(nsTableFrame* aTable) |
michael@0 | 6302 | { |
michael@0 | 6303 | mTable = aTable; |
michael@0 | 6304 | mVerInfo = nullptr; |
michael@0 | 6305 | nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr); |
michael@0 | 6306 | mTableFirstInFlow = static_cast<nsTableFrame*>(mTable->FirstInFlow()); |
michael@0 | 6307 | mTableCellMap = mTable->GetCellMap(); |
michael@0 | 6308 | // y position of first row in damage area |
michael@0 | 6309 | mInitialOffsetY = mTable->GetPrevInFlow() ? 0 : childAreaOffset.top; |
michael@0 | 6310 | mNumTableRows = mTable->GetRowCount(); |
michael@0 | 6311 | mNumTableCols = mTable->GetColCount(); |
michael@0 | 6312 | |
michael@0 | 6313 | // Get the ordered row groups |
michael@0 | 6314 | mTable->OrderRowGroups(mRowGroups); |
michael@0 | 6315 | // initialize to a non existing index |
michael@0 | 6316 | mRepeatedHeaderRowIndex = -99; |
michael@0 | 6317 | |
michael@0 | 6318 | mTableIsLTR = mTable->StyleVisibility()->mDirection == |
michael@0 | 6319 | NS_STYLE_DIRECTION_LTR; |
michael@0 | 6320 | mColInc = (mTableIsLTR) ? 1 : -1; |
michael@0 | 6321 | |
michael@0 | 6322 | nsIFrame* bgFrame = |
michael@0 | 6323 | nsCSSRendering::FindNonTransparentBackgroundFrame(aTable); |
michael@0 | 6324 | mTableBgColor = bgFrame->StyleBackground(); |
michael@0 | 6325 | } |
michael@0 | 6326 | |
michael@0 | 6327 | bool |
michael@0 | 6328 | BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect) |
michael@0 | 6329 | { |
michael@0 | 6330 | |
michael@0 | 6331 | uint32_t startRowIndex, endRowIndex, startColIndex, endColIndex; |
michael@0 | 6332 | startRowIndex = endRowIndex = startColIndex = endColIndex = 0; |
michael@0 | 6333 | bool done = false; |
michael@0 | 6334 | bool haveIntersect = false; |
michael@0 | 6335 | // find startRowIndex, endRowIndex |
michael@0 | 6336 | nscoord rowY = mInitialOffsetY; |
michael@0 | 6337 | for (uint32_t rgX = 0; rgX < mRowGroups.Length() && !done; rgX++) { |
michael@0 | 6338 | nsTableRowGroupFrame* rgFrame = mRowGroups[rgX]; |
michael@0 | 6339 | for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame; |
michael@0 | 6340 | rowFrame = rowFrame->GetNextRow()) { |
michael@0 | 6341 | // conservatively estimate the half border widths outside the row |
michael@0 | 6342 | nscoord topBorderHalf = (mTable->GetPrevInFlow()) ? 0 : |
michael@0 | 6343 | nsPresContext::CSSPixelsToAppUnits(rowFrame->GetTopBCBorderWidth() + 1); |
michael@0 | 6344 | nscoord bottomBorderHalf = (mTable->GetNextInFlow()) ? 0 : |
michael@0 | 6345 | nsPresContext::CSSPixelsToAppUnits(rowFrame->GetBottomBCBorderWidth() + 1); |
michael@0 | 6346 | // get the row rect relative to the table rather than the row group |
michael@0 | 6347 | nsSize rowSize = rowFrame->GetSize(); |
michael@0 | 6348 | if (haveIntersect) { |
michael@0 | 6349 | if (aDirtyRect.YMost() >= (rowY - topBorderHalf)) { |
michael@0 | 6350 | nsTableRowFrame* fifRow = |
michael@0 | 6351 | static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow()); |
michael@0 | 6352 | endRowIndex = fifRow->GetRowIndex(); |
michael@0 | 6353 | } |
michael@0 | 6354 | else done = true; |
michael@0 | 6355 | } |
michael@0 | 6356 | else { |
michael@0 | 6357 | if ((rowY + rowSize.height + bottomBorderHalf) >= aDirtyRect.y) { |
michael@0 | 6358 | mStartRg = rgFrame; |
michael@0 | 6359 | mStartRow = rowFrame; |
michael@0 | 6360 | nsTableRowFrame* fifRow = |
michael@0 | 6361 | static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow()); |
michael@0 | 6362 | startRowIndex = endRowIndex = fifRow->GetRowIndex(); |
michael@0 | 6363 | haveIntersect = true; |
michael@0 | 6364 | } |
michael@0 | 6365 | else { |
michael@0 | 6366 | mInitialOffsetY += rowSize.height; |
michael@0 | 6367 | } |
michael@0 | 6368 | } |
michael@0 | 6369 | rowY += rowSize.height; |
michael@0 | 6370 | } |
michael@0 | 6371 | } |
michael@0 | 6372 | mNextOffsetY = mInitialOffsetY; |
michael@0 | 6373 | |
michael@0 | 6374 | // XXX comment refers to the obsolete NS_FRAME_OUTSIDE_CHILDREN flag |
michael@0 | 6375 | // XXX but I don't understand it, so not changing it for now |
michael@0 | 6376 | // outer table borders overflow the table, so the table might be |
michael@0 | 6377 | // target to other areas as the NS_FRAME_OUTSIDE_CHILDREN is set |
michael@0 | 6378 | // on the table |
michael@0 | 6379 | if (!haveIntersect) |
michael@0 | 6380 | return false; |
michael@0 | 6381 | // find startColIndex, endColIndex, startColX |
michael@0 | 6382 | haveIntersect = false; |
michael@0 | 6383 | if (0 == mNumTableCols) |
michael@0 | 6384 | return false; |
michael@0 | 6385 | int32_t leftCol, rightCol; // columns are in the range [leftCol, rightCol) |
michael@0 | 6386 | |
michael@0 | 6387 | nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr); |
michael@0 | 6388 | if (mTableIsLTR) { |
michael@0 | 6389 | mInitialOffsetX = childAreaOffset.left; // x position of first col in |
michael@0 | 6390 | // damage area |
michael@0 | 6391 | leftCol = 0; |
michael@0 | 6392 | rightCol = mNumTableCols; |
michael@0 | 6393 | } else { |
michael@0 | 6394 | // x position of first col in damage area |
michael@0 | 6395 | mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right; |
michael@0 | 6396 | leftCol = mNumTableCols-1; |
michael@0 | 6397 | rightCol = -1; |
michael@0 | 6398 | } |
michael@0 | 6399 | nscoord x = 0; |
michael@0 | 6400 | int32_t colX; |
michael@0 | 6401 | for (colX = leftCol; colX != rightCol; colX += mColInc) { |
michael@0 | 6402 | nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(colX); |
michael@0 | 6403 | if (!colFrame) ABORT1(false); |
michael@0 | 6404 | // get the col rect relative to the table rather than the col group |
michael@0 | 6405 | nsSize size = colFrame->GetSize(); |
michael@0 | 6406 | if (haveIntersect) { |
michael@0 | 6407 | // conservatively estimate the left half border width outside the col |
michael@0 | 6408 | nscoord leftBorderHalf = |
michael@0 | 6409 | nsPresContext::CSSPixelsToAppUnits(colFrame->GetLeftBorderWidth() + 1); |
michael@0 | 6410 | if (aDirtyRect.XMost() >= (x - leftBorderHalf)) { |
michael@0 | 6411 | endColIndex = colX; |
michael@0 | 6412 | } |
michael@0 | 6413 | else break; |
michael@0 | 6414 | } |
michael@0 | 6415 | else { |
michael@0 | 6416 | // conservatively estimate the right half border width outside the col |
michael@0 | 6417 | nscoord rightBorderHalf = |
michael@0 | 6418 | nsPresContext::CSSPixelsToAppUnits(colFrame->GetRightBorderWidth() + 1); |
michael@0 | 6419 | if ((x + size.width + rightBorderHalf) >= aDirtyRect.x) { |
michael@0 | 6420 | startColIndex = endColIndex = colX; |
michael@0 | 6421 | haveIntersect = true; |
michael@0 | 6422 | } |
michael@0 | 6423 | else { |
michael@0 | 6424 | mInitialOffsetX += mColInc * size.width; |
michael@0 | 6425 | } |
michael@0 | 6426 | } |
michael@0 | 6427 | x += size.width; |
michael@0 | 6428 | } |
michael@0 | 6429 | if (!mTableIsLTR) { |
michael@0 | 6430 | uint32_t temp; |
michael@0 | 6431 | mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right; |
michael@0 | 6432 | temp = startColIndex; startColIndex = endColIndex; endColIndex = temp; |
michael@0 | 6433 | for (uint32_t column = 0; column < startColIndex; column++) { |
michael@0 | 6434 | nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(column); |
michael@0 | 6435 | if (!colFrame) ABORT1(false); |
michael@0 | 6436 | nsSize size = colFrame->GetSize(); |
michael@0 | 6437 | mInitialOffsetX += mColInc * size.width; |
michael@0 | 6438 | } |
michael@0 | 6439 | } |
michael@0 | 6440 | if (!haveIntersect) |
michael@0 | 6441 | return false; |
michael@0 | 6442 | mDamageArea = nsIntRect(startColIndex, startRowIndex, |
michael@0 | 6443 | 1 + DeprecatedAbs<int32_t>(endColIndex - startColIndex), |
michael@0 | 6444 | 1 + endRowIndex - startRowIndex); |
michael@0 | 6445 | |
michael@0 | 6446 | Reset(); |
michael@0 | 6447 | mVerInfo = new BCVerticalSeg[mDamageArea.width + 1]; |
michael@0 | 6448 | if (!mVerInfo) |
michael@0 | 6449 | return false; |
michael@0 | 6450 | return true; |
michael@0 | 6451 | } |
michael@0 | 6452 | |
michael@0 | 6453 | void |
michael@0 | 6454 | BCPaintBorderIterator::Reset() |
michael@0 | 6455 | { |
michael@0 | 6456 | mAtEnd = true; // gets reset when First() is called |
michael@0 | 6457 | mRg = mStartRg; |
michael@0 | 6458 | mPrevRow = nullptr; |
michael@0 | 6459 | mRow = mStartRow; |
michael@0 | 6460 | mRowIndex = 0; |
michael@0 | 6461 | mColIndex = 0; |
michael@0 | 6462 | mRgIndex = -1; |
michael@0 | 6463 | mPrevCell = nullptr; |
michael@0 | 6464 | mCell = nullptr; |
michael@0 | 6465 | mPrevCellData = nullptr; |
michael@0 | 6466 | mCellData = nullptr; |
michael@0 | 6467 | mBCData = nullptr; |
michael@0 | 6468 | ResetVerInfo(); |
michael@0 | 6469 | } |
michael@0 | 6470 | |
michael@0 | 6471 | /** |
michael@0 | 6472 | * Set the iterator data to a new cellmap coordinate |
michael@0 | 6473 | * @param aRowIndex - the row index |
michael@0 | 6474 | * @param aColIndex - the col index |
michael@0 | 6475 | */ |
michael@0 | 6476 | void |
michael@0 | 6477 | BCPaintBorderIterator::SetNewData(int32_t aY, |
michael@0 | 6478 | int32_t aX) |
michael@0 | 6479 | { |
michael@0 | 6480 | if (!mTableCellMap || !mTableCellMap->mBCInfo) ABORT0(); |
michael@0 | 6481 | |
michael@0 | 6482 | mColIndex = aX; |
michael@0 | 6483 | mRowIndex = aY; |
michael@0 | 6484 | mPrevCellData = mCellData; |
michael@0 | 6485 | if (IsTableRightMost() && IsTableBottomMost()) { |
michael@0 | 6486 | mCell = nullptr; |
michael@0 | 6487 | mBCData = &mTableCellMap->mBCInfo->mLowerRightCorner; |
michael@0 | 6488 | } |
michael@0 | 6489 | else if (IsTableRightMost()) { |
michael@0 | 6490 | mCellData = nullptr; |
michael@0 | 6491 | mBCData = &mTableCellMap->mBCInfo->mRightBorders.ElementAt(aY); |
michael@0 | 6492 | } |
michael@0 | 6493 | else if (IsTableBottomMost()) { |
michael@0 | 6494 | mCellData = nullptr; |
michael@0 | 6495 | mBCData = &mTableCellMap->mBCInfo->mBottomBorders.ElementAt(aX); |
michael@0 | 6496 | } |
michael@0 | 6497 | else { |
michael@0 | 6498 | if (uint32_t(mRowIndex - mFifRgFirstRowIndex) < mCellMap->mRows.Length()) { |
michael@0 | 6499 | mBCData = nullptr; |
michael@0 | 6500 | mCellData = |
michael@0 | 6501 | (BCCellData*)mCellMap->mRows[mRowIndex - mFifRgFirstRowIndex].SafeElementAt(mColIndex); |
michael@0 | 6502 | if (mCellData) { |
michael@0 | 6503 | mBCData = &mCellData->mData; |
michael@0 | 6504 | if (!mCellData->IsOrig()) { |
michael@0 | 6505 | if (mCellData->IsRowSpan()) { |
michael@0 | 6506 | aY -= mCellData->GetRowSpanOffset(); |
michael@0 | 6507 | } |
michael@0 | 6508 | if (mCellData->IsColSpan()) { |
michael@0 | 6509 | aX -= mCellData->GetColSpanOffset(); |
michael@0 | 6510 | } |
michael@0 | 6511 | if ((aX >= 0) && (aY >= 0)) { |
michael@0 | 6512 | mCellData = (BCCellData*)mCellMap->mRows[aY - mFifRgFirstRowIndex][aX]; |
michael@0 | 6513 | } |
michael@0 | 6514 | } |
michael@0 | 6515 | if (mCellData->IsOrig()) { |
michael@0 | 6516 | mPrevCell = mCell; |
michael@0 | 6517 | mCell = mCellData->GetCellFrame(); |
michael@0 | 6518 | } |
michael@0 | 6519 | } |
michael@0 | 6520 | } |
michael@0 | 6521 | } |
michael@0 | 6522 | } |
michael@0 | 6523 | |
michael@0 | 6524 | /** |
michael@0 | 6525 | * Set the iterator to a new row |
michael@0 | 6526 | * @param aRow - the new row frame, if null the iterator will advance to the |
michael@0 | 6527 | * next row |
michael@0 | 6528 | */ |
michael@0 | 6529 | bool |
michael@0 | 6530 | BCPaintBorderIterator::SetNewRow(nsTableRowFrame* aRow) |
michael@0 | 6531 | { |
michael@0 | 6532 | mPrevRow = mRow; |
michael@0 | 6533 | mRow = (aRow) ? aRow : mRow->GetNextRow(); |
michael@0 | 6534 | if (mRow) { |
michael@0 | 6535 | mIsNewRow = true; |
michael@0 | 6536 | mRowIndex = mRow->GetRowIndex(); |
michael@0 | 6537 | mColIndex = mDamageArea.x; |
michael@0 | 6538 | mPrevHorSegHeight = 0; |
michael@0 | 6539 | if (mIsRepeatedHeader) { |
michael@0 | 6540 | mRepeatedHeaderRowIndex = mRowIndex; |
michael@0 | 6541 | } |
michael@0 | 6542 | } |
michael@0 | 6543 | else { |
michael@0 | 6544 | mAtEnd = true; |
michael@0 | 6545 | } |
michael@0 | 6546 | return !mAtEnd; |
michael@0 | 6547 | } |
michael@0 | 6548 | |
michael@0 | 6549 | /** |
michael@0 | 6550 | * Advance the iterator to the next row group |
michael@0 | 6551 | */ |
michael@0 | 6552 | bool |
michael@0 | 6553 | BCPaintBorderIterator::SetNewRowGroup() |
michael@0 | 6554 | { |
michael@0 | 6555 | |
michael@0 | 6556 | mRgIndex++; |
michael@0 | 6557 | |
michael@0 | 6558 | mIsRepeatedHeader = false; |
michael@0 | 6559 | mIsRepeatedFooter = false; |
michael@0 | 6560 | |
michael@0 | 6561 | NS_ASSERTION(mRgIndex >= 0, "mRgIndex out of bounds"); |
michael@0 | 6562 | if (uint32_t(mRgIndex) < mRowGroups.Length()) { |
michael@0 | 6563 | mPrevRg = mRg; |
michael@0 | 6564 | mRg = mRowGroups[mRgIndex]; |
michael@0 | 6565 | nsTableRowGroupFrame* fifRg = |
michael@0 | 6566 | static_cast<nsTableRowGroupFrame*>(mRg->FirstInFlow()); |
michael@0 | 6567 | mFifRgFirstRowIndex = fifRg->GetStartRowIndex(); |
michael@0 | 6568 | mRgFirstRowIndex = mRg->GetStartRowIndex(); |
michael@0 | 6569 | mRgLastRowIndex = mRgFirstRowIndex + mRg->GetRowCount() - 1; |
michael@0 | 6570 | |
michael@0 | 6571 | if (SetNewRow(mRg->GetFirstRow())) { |
michael@0 | 6572 | mCellMap = mTableCellMap->GetMapFor(fifRg, nullptr); |
michael@0 | 6573 | if (!mCellMap) ABORT1(false); |
michael@0 | 6574 | } |
michael@0 | 6575 | if (mRg && mTable->GetPrevInFlow() && !mRg->GetPrevInFlow()) { |
michael@0 | 6576 | // if mRowGroup doesn't have a prev in flow, then it may be a repeated |
michael@0 | 6577 | // header or footer |
michael@0 | 6578 | const nsStyleDisplay* display = mRg->StyleDisplay(); |
michael@0 | 6579 | if (mRowIndex == mDamageArea.y) { |
michael@0 | 6580 | mIsRepeatedHeader = (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay); |
michael@0 | 6581 | } |
michael@0 | 6582 | else { |
michael@0 | 6583 | mIsRepeatedFooter = (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == display->mDisplay); |
michael@0 | 6584 | } |
michael@0 | 6585 | } |
michael@0 | 6586 | } |
michael@0 | 6587 | else { |
michael@0 | 6588 | mAtEnd = true; |
michael@0 | 6589 | } |
michael@0 | 6590 | return !mAtEnd; |
michael@0 | 6591 | } |
michael@0 | 6592 | |
michael@0 | 6593 | /** |
michael@0 | 6594 | * Move the iterator to the first position in the damageArea |
michael@0 | 6595 | */ |
michael@0 | 6596 | void |
michael@0 | 6597 | BCPaintBorderIterator::First() |
michael@0 | 6598 | { |
michael@0 | 6599 | if (!mTable || (mDamageArea.x >= mNumTableCols) || |
michael@0 | 6600 | (mDamageArea.y >= mNumTableRows)) ABORT0(); |
michael@0 | 6601 | |
michael@0 | 6602 | mAtEnd = false; |
michael@0 | 6603 | |
michael@0 | 6604 | uint32_t numRowGroups = mRowGroups.Length(); |
michael@0 | 6605 | for (uint32_t rgY = 0; rgY < numRowGroups; rgY++) { |
michael@0 | 6606 | nsTableRowGroupFrame* rowG = mRowGroups[rgY]; |
michael@0 | 6607 | int32_t start = rowG->GetStartRowIndex(); |
michael@0 | 6608 | int32_t end = start + rowG->GetRowCount() - 1; |
michael@0 | 6609 | if ((mDamageArea.y >= start) && (mDamageArea.y <= end)) { |
michael@0 | 6610 | mRgIndex = rgY - 1; // SetNewRowGroup increments rowGroupIndex |
michael@0 | 6611 | if (SetNewRowGroup()) { |
michael@0 | 6612 | while ((mRowIndex < mDamageArea.y) && !mAtEnd) { |
michael@0 | 6613 | SetNewRow(); |
michael@0 | 6614 | } |
michael@0 | 6615 | if (!mAtEnd) { |
michael@0 | 6616 | SetNewData(mDamageArea.y, mDamageArea.x); |
michael@0 | 6617 | } |
michael@0 | 6618 | } |
michael@0 | 6619 | return; |
michael@0 | 6620 | } |
michael@0 | 6621 | } |
michael@0 | 6622 | mAtEnd = true; |
michael@0 | 6623 | } |
michael@0 | 6624 | |
michael@0 | 6625 | /** |
michael@0 | 6626 | * Advance the iterator to the next position |
michael@0 | 6627 | */ |
michael@0 | 6628 | void |
michael@0 | 6629 | BCPaintBorderIterator::Next() |
michael@0 | 6630 | { |
michael@0 | 6631 | if (mAtEnd) ABORT0(); |
michael@0 | 6632 | mIsNewRow = false; |
michael@0 | 6633 | |
michael@0 | 6634 | mColIndex++; |
michael@0 | 6635 | if (mColIndex > mDamageArea.XMost()) { |
michael@0 | 6636 | mRowIndex++; |
michael@0 | 6637 | if (mRowIndex == mDamageArea.YMost()) { |
michael@0 | 6638 | mColIndex = mDamageArea.x; |
michael@0 | 6639 | } |
michael@0 | 6640 | else if (mRowIndex < mDamageArea.YMost()) { |
michael@0 | 6641 | if (mRowIndex <= mRgLastRowIndex) { |
michael@0 | 6642 | SetNewRow(); |
michael@0 | 6643 | } |
michael@0 | 6644 | else { |
michael@0 | 6645 | SetNewRowGroup(); |
michael@0 | 6646 | } |
michael@0 | 6647 | } |
michael@0 | 6648 | else { |
michael@0 | 6649 | mAtEnd = true; |
michael@0 | 6650 | } |
michael@0 | 6651 | } |
michael@0 | 6652 | if (!mAtEnd) { |
michael@0 | 6653 | SetNewData(mRowIndex, mColIndex); |
michael@0 | 6654 | } |
michael@0 | 6655 | } |
michael@0 | 6656 | |
michael@0 | 6657 | // XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine |
michael@0 | 6658 | // them |
michael@0 | 6659 | /** Compute the vertical offset of a vertical border segment |
michael@0 | 6660 | * @param aCornerOwnerSide - which side owns the corner |
michael@0 | 6661 | * @param aCornerSubWidth - how wide is the nonwinning side of the corner |
michael@0 | 6662 | * @param aHorWidth - how wide is the horizontal edge of the corner |
michael@0 | 6663 | * @param aIsStartOfSeg - does this corner start a new segment |
michael@0 | 6664 | * @param aIsBevel - is this corner beveled |
michael@0 | 6665 | * @return - offset in twips |
michael@0 | 6666 | */ |
michael@0 | 6667 | static nscoord |
michael@0 | 6668 | CalcVerCornerOffset(mozilla::css::Side aCornerOwnerSide, |
michael@0 | 6669 | BCPixelSize aCornerSubWidth, |
michael@0 | 6670 | BCPixelSize aHorWidth, |
michael@0 | 6671 | bool aIsStartOfSeg, |
michael@0 | 6672 | bool aIsBevel) |
michael@0 | 6673 | { |
michael@0 | 6674 | nscoord offset = 0; |
michael@0 | 6675 | // XXX These should be replaced with appropriate side-specific macros (which?) |
michael@0 | 6676 | BCPixelSize smallHalf, largeHalf; |
michael@0 | 6677 | if ((NS_SIDE_TOP == aCornerOwnerSide) || |
michael@0 | 6678 | (NS_SIDE_BOTTOM == aCornerOwnerSide)) { |
michael@0 | 6679 | DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf); |
michael@0 | 6680 | if (aIsBevel) { |
michael@0 | 6681 | offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; |
michael@0 | 6682 | } |
michael@0 | 6683 | else { |
michael@0 | 6684 | offset = (NS_SIDE_TOP == aCornerOwnerSide) ? smallHalf : -largeHalf; |
michael@0 | 6685 | } |
michael@0 | 6686 | } |
michael@0 | 6687 | else { |
michael@0 | 6688 | DivideBCBorderSize(aHorWidth, smallHalf, largeHalf); |
michael@0 | 6689 | if (aIsBevel) { |
michael@0 | 6690 | offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; |
michael@0 | 6691 | } |
michael@0 | 6692 | else { |
michael@0 | 6693 | offset = (aIsStartOfSeg) ? smallHalf : -largeHalf; |
michael@0 | 6694 | } |
michael@0 | 6695 | } |
michael@0 | 6696 | return nsPresContext::CSSPixelsToAppUnits(offset); |
michael@0 | 6697 | } |
michael@0 | 6698 | |
michael@0 | 6699 | /** Compute the horizontal offset of a horizontal border segment |
michael@0 | 6700 | * @param aCornerOwnerSide - which side owns the corner |
michael@0 | 6701 | * @param aCornerSubWidth - how wide is the nonwinning side of the corner |
michael@0 | 6702 | * @param aVerWidth - how wide is the vertical edge of the corner |
michael@0 | 6703 | * @param aIsStartOfSeg - does this corner start a new segment |
michael@0 | 6704 | * @param aIsBevel - is this corner beveled |
michael@0 | 6705 | * @param aTableIsLTR - direction, the computation depends on ltr or rtl |
michael@0 | 6706 | * @return - offset in twips |
michael@0 | 6707 | */ |
michael@0 | 6708 | static nscoord |
michael@0 | 6709 | CalcHorCornerOffset(mozilla::css::Side aCornerOwnerSide, |
michael@0 | 6710 | BCPixelSize aCornerSubWidth, |
michael@0 | 6711 | BCPixelSize aVerWidth, |
michael@0 | 6712 | bool aIsStartOfSeg, |
michael@0 | 6713 | bool aIsBevel, |
michael@0 | 6714 | bool aTableIsLTR) |
michael@0 | 6715 | { |
michael@0 | 6716 | nscoord offset = 0; |
michael@0 | 6717 | // XXX These should be replaced with appropriate side-specific macros (which?) |
michael@0 | 6718 | BCPixelSize smallHalf, largeHalf; |
michael@0 | 6719 | if ((NS_SIDE_LEFT == aCornerOwnerSide) || |
michael@0 | 6720 | (NS_SIDE_RIGHT == aCornerOwnerSide)) { |
michael@0 | 6721 | if (aTableIsLTR) { |
michael@0 | 6722 | DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf); |
michael@0 | 6723 | } |
michael@0 | 6724 | else { |
michael@0 | 6725 | DivideBCBorderSize(aCornerSubWidth, largeHalf, smallHalf); |
michael@0 | 6726 | } |
michael@0 | 6727 | if (aIsBevel) { |
michael@0 | 6728 | offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; |
michael@0 | 6729 | } |
michael@0 | 6730 | else { |
michael@0 | 6731 | offset = (NS_SIDE_LEFT == aCornerOwnerSide) ? smallHalf : -largeHalf; |
michael@0 | 6732 | } |
michael@0 | 6733 | } |
michael@0 | 6734 | else { |
michael@0 | 6735 | if (aTableIsLTR) { |
michael@0 | 6736 | DivideBCBorderSize(aVerWidth, smallHalf, largeHalf); |
michael@0 | 6737 | } |
michael@0 | 6738 | else { |
michael@0 | 6739 | DivideBCBorderSize(aVerWidth, largeHalf, smallHalf); |
michael@0 | 6740 | } |
michael@0 | 6741 | if (aIsBevel) { |
michael@0 | 6742 | offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; |
michael@0 | 6743 | } |
michael@0 | 6744 | else { |
michael@0 | 6745 | offset = (aIsStartOfSeg) ? smallHalf : -largeHalf; |
michael@0 | 6746 | } |
michael@0 | 6747 | } |
michael@0 | 6748 | return nsPresContext::CSSPixelsToAppUnits(offset); |
michael@0 | 6749 | } |
michael@0 | 6750 | |
michael@0 | 6751 | BCVerticalSeg::BCVerticalSeg() |
michael@0 | 6752 | { |
michael@0 | 6753 | mCol = nullptr; |
michael@0 | 6754 | mFirstCell = mLastCell = mAjaCell = nullptr; |
michael@0 | 6755 | mOffsetX = mOffsetY = mLength = mWidth = mTopBevelOffset = 0; |
michael@0 | 6756 | mTopBevelSide = NS_SIDE_TOP; |
michael@0 | 6757 | mOwner = eCellOwner; |
michael@0 | 6758 | } |
michael@0 | 6759 | |
michael@0 | 6760 | /** |
michael@0 | 6761 | * Start a new vertical segment |
michael@0 | 6762 | * @param aIter - iterator containing the structural information |
michael@0 | 6763 | * @param aBorderOwner - determines the border style |
michael@0 | 6764 | * @param aVerSegWidth - the width of segment in pixel |
michael@0 | 6765 | * @param aHorSegHeight - the width of the horizontal segment joining the corner |
michael@0 | 6766 | * at the start |
michael@0 | 6767 | */ |
michael@0 | 6768 | void |
michael@0 | 6769 | BCVerticalSeg::Start(BCPaintBorderIterator& aIter, |
michael@0 | 6770 | BCBorderOwner aBorderOwner, |
michael@0 | 6771 | BCPixelSize aVerSegWidth, |
michael@0 | 6772 | BCPixelSize aHorSegHeight) |
michael@0 | 6773 | { |
michael@0 | 6774 | mozilla::css::Side ownerSide = NS_SIDE_TOP; |
michael@0 | 6775 | bool bevel = false; |
michael@0 | 6776 | |
michael@0 | 6777 | |
michael@0 | 6778 | nscoord cornerSubWidth = (aIter.mBCData) ? |
michael@0 | 6779 | aIter.mBCData->GetCorner(ownerSide, bevel) : 0; |
michael@0 | 6780 | |
michael@0 | 6781 | bool topBevel = (aVerSegWidth > 0) ? bevel : false; |
michael@0 | 6782 | BCPixelSize maxHorSegHeight = std::max(aIter.mPrevHorSegHeight, aHorSegHeight); |
michael@0 | 6783 | nscoord offset = CalcVerCornerOffset(ownerSide, cornerSubWidth, |
michael@0 | 6784 | maxHorSegHeight, true, |
michael@0 | 6785 | topBevel); |
michael@0 | 6786 | |
michael@0 | 6787 | mTopBevelOffset = topBevel ? |
michael@0 | 6788 | nsPresContext::CSSPixelsToAppUnits(maxHorSegHeight): 0; |
michael@0 | 6789 | // XXX this assumes that only corners where 2 segments join can be beveled |
michael@0 | 6790 | mTopBevelSide = (aHorSegHeight > 0) ? NS_SIDE_RIGHT : NS_SIDE_LEFT; |
michael@0 | 6791 | mOffsetY += offset; |
michael@0 | 6792 | mLength = -offset; |
michael@0 | 6793 | mWidth = aVerSegWidth; |
michael@0 | 6794 | mOwner = aBorderOwner; |
michael@0 | 6795 | mFirstCell = aIter.mCell; |
michael@0 | 6796 | mFirstRowGroup = aIter.mRg; |
michael@0 | 6797 | mFirstRow = aIter.mRow; |
michael@0 | 6798 | if (aIter.GetRelativeColIndex() > 0) { |
michael@0 | 6799 | mAjaCell = aIter.mVerInfo[aIter.GetRelativeColIndex() - 1].mLastCell; |
michael@0 | 6800 | } |
michael@0 | 6801 | } |
michael@0 | 6802 | |
michael@0 | 6803 | /** |
michael@0 | 6804 | * Initialize the vertical segments with information that will persist for any |
michael@0 | 6805 | * vertical segment in this column |
michael@0 | 6806 | * @param aIter - iterator containing the structural information |
michael@0 | 6807 | */ |
michael@0 | 6808 | void |
michael@0 | 6809 | BCVerticalSeg::Initialize(BCPaintBorderIterator& aIter) |
michael@0 | 6810 | { |
michael@0 | 6811 | int32_t relColIndex = aIter.GetRelativeColIndex(); |
michael@0 | 6812 | mCol = aIter.IsTableRightMost() ? aIter.mVerInfo[relColIndex - 1].mCol : |
michael@0 | 6813 | aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex); |
michael@0 | 6814 | if (!mCol) ABORT0(); |
michael@0 | 6815 | if (0 == relColIndex) { |
michael@0 | 6816 | mOffsetX = aIter.mInitialOffsetX; |
michael@0 | 6817 | } |
michael@0 | 6818 | // set colX for the next column |
michael@0 | 6819 | if (!aIter.IsDamageAreaRightMost()) { |
michael@0 | 6820 | aIter.mVerInfo[relColIndex + 1].mOffsetX = mOffsetX + |
michael@0 | 6821 | aIter.mColInc * mCol->GetSize().width; |
michael@0 | 6822 | } |
michael@0 | 6823 | mOffsetY = aIter.mInitialOffsetY; |
michael@0 | 6824 | mLastCell = aIter.mCell; |
michael@0 | 6825 | } |
michael@0 | 6826 | |
michael@0 | 6827 | /** |
michael@0 | 6828 | * Compute the offsets for the bottom corner of a vertical segment |
michael@0 | 6829 | * @param aIter - iterator containing the structural information |
michael@0 | 6830 | * @param aHorSegHeight - the width of the horizontal segment joining the corner |
michael@0 | 6831 | * at the start |
michael@0 | 6832 | */ |
michael@0 | 6833 | void |
michael@0 | 6834 | BCVerticalSeg::GetBottomCorner(BCPaintBorderIterator& aIter, |
michael@0 | 6835 | BCPixelSize aHorSegHeight) |
michael@0 | 6836 | { |
michael@0 | 6837 | mozilla::css::Side ownerSide = NS_SIDE_TOP; |
michael@0 | 6838 | nscoord cornerSubWidth = 0; |
michael@0 | 6839 | bool bevel = false; |
michael@0 | 6840 | if (aIter.mBCData) { |
michael@0 | 6841 | cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel); |
michael@0 | 6842 | } |
michael@0 | 6843 | mIsBottomBevel = (mWidth > 0) ? bevel : false; |
michael@0 | 6844 | mBottomHorSegHeight = std::max(aIter.mPrevHorSegHeight, aHorSegHeight); |
michael@0 | 6845 | mBottomOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth, |
michael@0 | 6846 | mBottomHorSegHeight, |
michael@0 | 6847 | false, mIsBottomBevel); |
michael@0 | 6848 | mLength += mBottomOffset; |
michael@0 | 6849 | } |
michael@0 | 6850 | |
michael@0 | 6851 | /** |
michael@0 | 6852 | * Paint the vertical segment |
michael@0 | 6853 | * @param aIter - iterator containing the structural information |
michael@0 | 6854 | * @param aRenderingContext - the rendering context |
michael@0 | 6855 | * @param aHorSegHeight - the width of the horizontal segment joining the corner |
michael@0 | 6856 | * at the start |
michael@0 | 6857 | */ |
michael@0 | 6858 | void |
michael@0 | 6859 | BCVerticalSeg::Paint(BCPaintBorderIterator& aIter, |
michael@0 | 6860 | nsRenderingContext& aRenderingContext, |
michael@0 | 6861 | BCPixelSize aHorSegHeight) |
michael@0 | 6862 | { |
michael@0 | 6863 | // get the border style, color and paint the segment |
michael@0 | 6864 | mozilla::css::Side side = (aIter.IsDamageAreaRightMost()) ? NS_SIDE_RIGHT : |
michael@0 | 6865 | NS_SIDE_LEFT; |
michael@0 | 6866 | int32_t relColIndex = aIter.GetRelativeColIndex(); |
michael@0 | 6867 | nsTableColFrame* col = mCol; if (!col) ABORT0(); |
michael@0 | 6868 | nsTableCellFrame* cell = mFirstCell; // ??? |
michael@0 | 6869 | nsIFrame* owner = nullptr; |
michael@0 | 6870 | uint8_t style = NS_STYLE_BORDER_STYLE_SOLID; |
michael@0 | 6871 | nscolor color = 0xFFFFFFFF; |
michael@0 | 6872 | |
michael@0 | 6873 | switch (mOwner) { |
michael@0 | 6874 | case eTableOwner: |
michael@0 | 6875 | owner = aIter.mTable; |
michael@0 | 6876 | break; |
michael@0 | 6877 | case eAjaColGroupOwner: |
michael@0 | 6878 | side = NS_SIDE_RIGHT; |
michael@0 | 6879 | if (!aIter.IsTableRightMost() && (relColIndex > 0)) { |
michael@0 | 6880 | col = aIter.mVerInfo[relColIndex - 1].mCol; |
michael@0 | 6881 | } // and fall through |
michael@0 | 6882 | case eColGroupOwner: |
michael@0 | 6883 | if (col) { |
michael@0 | 6884 | owner = col->GetParent(); |
michael@0 | 6885 | } |
michael@0 | 6886 | break; |
michael@0 | 6887 | case eAjaColOwner: |
michael@0 | 6888 | side = NS_SIDE_RIGHT; |
michael@0 | 6889 | if (!aIter.IsTableRightMost() && (relColIndex > 0)) { |
michael@0 | 6890 | col = aIter.mVerInfo[relColIndex - 1].mCol; |
michael@0 | 6891 | } // and fall through |
michael@0 | 6892 | case eColOwner: |
michael@0 | 6893 | owner = col; |
michael@0 | 6894 | break; |
michael@0 | 6895 | case eAjaRowGroupOwner: |
michael@0 | 6896 | NS_ERROR("a neighboring rowgroup can never own a vertical border"); |
michael@0 | 6897 | // and fall through |
michael@0 | 6898 | case eRowGroupOwner: |
michael@0 | 6899 | NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(), |
michael@0 | 6900 | "row group can own border only at table edge"); |
michael@0 | 6901 | owner = mFirstRowGroup; |
michael@0 | 6902 | break; |
michael@0 | 6903 | case eAjaRowOwner: |
michael@0 | 6904 | NS_ASSERTION(false, "program error"); // and fall through |
michael@0 | 6905 | case eRowOwner: |
michael@0 | 6906 | NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(), |
michael@0 | 6907 | "row can own border only at table edge"); |
michael@0 | 6908 | owner = mFirstRow; |
michael@0 | 6909 | break; |
michael@0 | 6910 | case eAjaCellOwner: |
michael@0 | 6911 | side = NS_SIDE_RIGHT; |
michael@0 | 6912 | cell = mAjaCell; // and fall through |
michael@0 | 6913 | case eCellOwner: |
michael@0 | 6914 | owner = cell; |
michael@0 | 6915 | break; |
michael@0 | 6916 | } |
michael@0 | 6917 | if (owner) { |
michael@0 | 6918 | ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR); |
michael@0 | 6919 | } |
michael@0 | 6920 | BCPixelSize smallHalf, largeHalf; |
michael@0 | 6921 | DivideBCBorderSize(mWidth, smallHalf, largeHalf); |
michael@0 | 6922 | nsRect segRect(mOffsetX - nsPresContext::CSSPixelsToAppUnits(largeHalf), |
michael@0 | 6923 | mOffsetY, |
michael@0 | 6924 | nsPresContext::CSSPixelsToAppUnits(mWidth), mLength); |
michael@0 | 6925 | nscoord bottomBevelOffset = (mIsBottomBevel) ? |
michael@0 | 6926 | nsPresContext::CSSPixelsToAppUnits(mBottomHorSegHeight) : 0; |
michael@0 | 6927 | mozilla::css::Side bottomBevelSide = ((aHorSegHeight > 0) ^ !aIter.mTableIsLTR) ? |
michael@0 | 6928 | NS_SIDE_RIGHT : NS_SIDE_LEFT; |
michael@0 | 6929 | mozilla::css::Side topBevelSide = ((mTopBevelSide == NS_SIDE_RIGHT) ^ !aIter.mTableIsLTR)? |
michael@0 | 6930 | NS_SIDE_RIGHT : NS_SIDE_LEFT; |
michael@0 | 6931 | nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, |
michael@0 | 6932 | aIter.mTableBgColor, segRect, |
michael@0 | 6933 | nsPresContext::AppUnitsPerCSSPixel(), |
michael@0 | 6934 | topBevelSide, mTopBevelOffset, |
michael@0 | 6935 | bottomBevelSide, bottomBevelOffset); |
michael@0 | 6936 | } |
michael@0 | 6937 | |
michael@0 | 6938 | /** |
michael@0 | 6939 | * Advance the start point of a segment |
michael@0 | 6940 | */ |
michael@0 | 6941 | void |
michael@0 | 6942 | BCVerticalSeg::AdvanceOffsetY() |
michael@0 | 6943 | { |
michael@0 | 6944 | mOffsetY += mLength - mBottomOffset; |
michael@0 | 6945 | } |
michael@0 | 6946 | |
michael@0 | 6947 | /** |
michael@0 | 6948 | * Accumulate the current segment |
michael@0 | 6949 | */ |
michael@0 | 6950 | void |
michael@0 | 6951 | BCVerticalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter) |
michael@0 | 6952 | { |
michael@0 | 6953 | mLastCell = aIter.mCell; |
michael@0 | 6954 | mLength += aIter.mRow->GetRect().height; |
michael@0 | 6955 | } |
michael@0 | 6956 | |
michael@0 | 6957 | BCHorizontalSeg::BCHorizontalSeg() |
michael@0 | 6958 | { |
michael@0 | 6959 | mOffsetX = mOffsetY = mLength = mWidth = mLeftBevelOffset = 0; |
michael@0 | 6960 | mLeftBevelSide = NS_SIDE_TOP; |
michael@0 | 6961 | mFirstCell = mAjaCell = nullptr; |
michael@0 | 6962 | } |
michael@0 | 6963 | |
michael@0 | 6964 | /** Initialize a horizontal border segment for painting |
michael@0 | 6965 | * @param aIter - iterator storing the current and adjacent frames |
michael@0 | 6966 | * @param aBorderOwner - which frame owns the border |
michael@0 | 6967 | * @param aBottomVerSegWidth - vertical segment width coming from up |
michael@0 | 6968 | * @param aHorSegHeight - the height of the segment |
michael@0 | 6969 | + */ |
michael@0 | 6970 | void |
michael@0 | 6971 | BCHorizontalSeg::Start(BCPaintBorderIterator& aIter, |
michael@0 | 6972 | BCBorderOwner aBorderOwner, |
michael@0 | 6973 | BCPixelSize aBottomVerSegWidth, |
michael@0 | 6974 | BCPixelSize aHorSegHeight) |
michael@0 | 6975 | { |
michael@0 | 6976 | mozilla::css::Side cornerOwnerSide = NS_SIDE_TOP; |
michael@0 | 6977 | bool bevel = false; |
michael@0 | 6978 | |
michael@0 | 6979 | mOwner = aBorderOwner; |
michael@0 | 6980 | nscoord cornerSubWidth = (aIter.mBCData) ? |
michael@0 | 6981 | aIter.mBCData->GetCorner(cornerOwnerSide, |
michael@0 | 6982 | bevel) : 0; |
michael@0 | 6983 | |
michael@0 | 6984 | bool leftBevel = (aHorSegHeight > 0) ? bevel : false; |
michael@0 | 6985 | int32_t relColIndex = aIter.GetRelativeColIndex(); |
michael@0 | 6986 | nscoord maxVerSegWidth = std::max(aIter.mVerInfo[relColIndex].mWidth, |
michael@0 | 6987 | aBottomVerSegWidth); |
michael@0 | 6988 | nscoord offset = CalcHorCornerOffset(cornerOwnerSide, cornerSubWidth, |
michael@0 | 6989 | maxVerSegWidth, true, leftBevel, |
michael@0 | 6990 | aIter.mTableIsLTR); |
michael@0 | 6991 | mLeftBevelOffset = (leftBevel && (aHorSegHeight > 0)) ? maxVerSegWidth : 0; |
michael@0 | 6992 | // XXX this assumes that only corners where 2 segments join can be beveled |
michael@0 | 6993 | mLeftBevelSide = (aBottomVerSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; |
michael@0 | 6994 | if (aIter.mTableIsLTR) { |
michael@0 | 6995 | mOffsetX += offset; |
michael@0 | 6996 | } |
michael@0 | 6997 | else { |
michael@0 | 6998 | mOffsetX -= offset; |
michael@0 | 6999 | } |
michael@0 | 7000 | mLength = -offset; |
michael@0 | 7001 | mWidth = aHorSegHeight; |
michael@0 | 7002 | mFirstCell = aIter.mCell; |
michael@0 | 7003 | mAjaCell = (aIter.IsDamageAreaTopMost()) ? nullptr : |
michael@0 | 7004 | aIter.mVerInfo[relColIndex].mLastCell; |
michael@0 | 7005 | } |
michael@0 | 7006 | |
michael@0 | 7007 | /** |
michael@0 | 7008 | * Compute the offsets for the right corner of a horizontal segment |
michael@0 | 7009 | * @param aIter - iterator containing the structural information |
michael@0 | 7010 | * @param aLeftSegWidth - the width of the vertical segment joining the corner |
michael@0 | 7011 | * at the start |
michael@0 | 7012 | */ |
michael@0 | 7013 | void |
michael@0 | 7014 | BCHorizontalSeg::GetRightCorner(BCPaintBorderIterator& aIter, |
michael@0 | 7015 | BCPixelSize aLeftSegWidth) |
michael@0 | 7016 | { |
michael@0 | 7017 | mozilla::css::Side ownerSide = NS_SIDE_TOP; |
michael@0 | 7018 | nscoord cornerSubWidth = 0; |
michael@0 | 7019 | bool bevel = false; |
michael@0 | 7020 | if (aIter.mBCData) { |
michael@0 | 7021 | cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel); |
michael@0 | 7022 | } |
michael@0 | 7023 | |
michael@0 | 7024 | mIsRightBevel = (mWidth > 0) ? bevel : 0; |
michael@0 | 7025 | int32_t relColIndex = aIter.GetRelativeColIndex(); |
michael@0 | 7026 | nscoord verWidth = std::max(aIter.mVerInfo[relColIndex].mWidth, aLeftSegWidth); |
michael@0 | 7027 | mEndOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth, |
michael@0 | 7028 | false, mIsRightBevel, aIter.mTableIsLTR); |
michael@0 | 7029 | mLength += mEndOffset; |
michael@0 | 7030 | mRightBevelOffset = (mIsRightBevel) ? |
michael@0 | 7031 | nsPresContext::CSSPixelsToAppUnits(verWidth) : 0; |
michael@0 | 7032 | mRightBevelSide = (aLeftSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; |
michael@0 | 7033 | } |
michael@0 | 7034 | |
michael@0 | 7035 | /** |
michael@0 | 7036 | * Paint the horizontal segment |
michael@0 | 7037 | * @param aIter - iterator containing the structural information |
michael@0 | 7038 | * @param aRenderingContext - the rendering context |
michael@0 | 7039 | */ |
michael@0 | 7040 | void |
michael@0 | 7041 | BCHorizontalSeg::Paint(BCPaintBorderIterator& aIter, |
michael@0 | 7042 | nsRenderingContext& aRenderingContext) |
michael@0 | 7043 | { |
michael@0 | 7044 | // get the border style, color and paint the segment |
michael@0 | 7045 | mozilla::css::Side side = (aIter.IsDamageAreaBottomMost()) ? NS_SIDE_BOTTOM : |
michael@0 | 7046 | NS_SIDE_TOP; |
michael@0 | 7047 | nsIFrame* rg = aIter.mRg; if (!rg) ABORT0(); |
michael@0 | 7048 | nsIFrame* row = aIter.mRow; if (!row) ABORT0(); |
michael@0 | 7049 | nsIFrame* cell = mFirstCell; |
michael@0 | 7050 | nsIFrame* col; |
michael@0 | 7051 | nsIFrame* owner = nullptr; |
michael@0 | 7052 | |
michael@0 | 7053 | uint8_t style = NS_STYLE_BORDER_STYLE_SOLID; |
michael@0 | 7054 | nscolor color = 0xFFFFFFFF; |
michael@0 | 7055 | |
michael@0 | 7056 | |
michael@0 | 7057 | switch (mOwner) { |
michael@0 | 7058 | case eTableOwner: |
michael@0 | 7059 | owner = aIter.mTable; |
michael@0 | 7060 | break; |
michael@0 | 7061 | case eAjaColGroupOwner: |
michael@0 | 7062 | NS_ERROR("neighboring colgroups can never own a horizontal border"); |
michael@0 | 7063 | // and fall through |
michael@0 | 7064 | case eColGroupOwner: |
michael@0 | 7065 | NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(), |
michael@0 | 7066 | "col group can own border only at the table edge"); |
michael@0 | 7067 | col = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1); |
michael@0 | 7068 | if (!col) ABORT0(); |
michael@0 | 7069 | owner = col->GetParent(); |
michael@0 | 7070 | break; |
michael@0 | 7071 | case eAjaColOwner: |
michael@0 | 7072 | NS_ERROR("neighboring column can never own a horizontal border"); |
michael@0 | 7073 | // and fall through |
michael@0 | 7074 | case eColOwner: |
michael@0 | 7075 | NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(), |
michael@0 | 7076 | "col can own border only at the table edge"); |
michael@0 | 7077 | owner = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1); |
michael@0 | 7078 | break; |
michael@0 | 7079 | case eAjaRowGroupOwner: |
michael@0 | 7080 | side = NS_SIDE_BOTTOM; |
michael@0 | 7081 | rg = (aIter.IsTableBottomMost()) ? aIter.mRg : aIter.mPrevRg; |
michael@0 | 7082 | // and fall through |
michael@0 | 7083 | case eRowGroupOwner: |
michael@0 | 7084 | owner = rg; |
michael@0 | 7085 | break; |
michael@0 | 7086 | case eAjaRowOwner: |
michael@0 | 7087 | side = NS_SIDE_BOTTOM; |
michael@0 | 7088 | row = (aIter.IsTableBottomMost()) ? aIter.mRow : aIter.mPrevRow; |
michael@0 | 7089 | // and fall through |
michael@0 | 7090 | case eRowOwner: |
michael@0 | 7091 | owner = row; |
michael@0 | 7092 | break; |
michael@0 | 7093 | case eAjaCellOwner: |
michael@0 | 7094 | side = NS_SIDE_BOTTOM; |
michael@0 | 7095 | // if this is null due to the damage area origin-y > 0, then the border |
michael@0 | 7096 | // won't show up anyway |
michael@0 | 7097 | cell = mAjaCell; |
michael@0 | 7098 | // and fall through |
michael@0 | 7099 | case eCellOwner: |
michael@0 | 7100 | owner = cell; |
michael@0 | 7101 | break; |
michael@0 | 7102 | } |
michael@0 | 7103 | if (owner) { |
michael@0 | 7104 | ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR); |
michael@0 | 7105 | } |
michael@0 | 7106 | BCPixelSize smallHalf, largeHalf; |
michael@0 | 7107 | DivideBCBorderSize(mWidth, smallHalf, largeHalf); |
michael@0 | 7108 | nsRect segRect(mOffsetX, |
michael@0 | 7109 | mOffsetY - nsPresContext::CSSPixelsToAppUnits(largeHalf), |
michael@0 | 7110 | mLength, |
michael@0 | 7111 | nsPresContext::CSSPixelsToAppUnits(mWidth)); |
michael@0 | 7112 | if (aIter.mTableIsLTR) { |
michael@0 | 7113 | nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, |
michael@0 | 7114 | aIter.mTableBgColor, segRect, |
michael@0 | 7115 | nsPresContext::AppUnitsPerCSSPixel(), |
michael@0 | 7116 | mLeftBevelSide, |
michael@0 | 7117 | nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset), |
michael@0 | 7118 | mRightBevelSide, mRightBevelOffset); |
michael@0 | 7119 | } |
michael@0 | 7120 | else { |
michael@0 | 7121 | segRect.x -= segRect.width; |
michael@0 | 7122 | nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, |
michael@0 | 7123 | aIter.mTableBgColor, segRect, |
michael@0 | 7124 | nsPresContext::AppUnitsPerCSSPixel(), |
michael@0 | 7125 | mRightBevelSide, mRightBevelOffset, |
michael@0 | 7126 | mLeftBevelSide, |
michael@0 | 7127 | nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset)); |
michael@0 | 7128 | } |
michael@0 | 7129 | } |
michael@0 | 7130 | |
michael@0 | 7131 | /** |
michael@0 | 7132 | * Advance the start point of a segment |
michael@0 | 7133 | */ |
michael@0 | 7134 | void |
michael@0 | 7135 | BCHorizontalSeg::AdvanceOffsetX(int32_t aIncrement) |
michael@0 | 7136 | { |
michael@0 | 7137 | mOffsetX += aIncrement * (mLength - mEndOffset); |
michael@0 | 7138 | } |
michael@0 | 7139 | |
michael@0 | 7140 | /** |
michael@0 | 7141 | * Accumulate the current segment |
michael@0 | 7142 | */ |
michael@0 | 7143 | void |
michael@0 | 7144 | BCHorizontalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter) |
michael@0 | 7145 | { |
michael@0 | 7146 | mLength += aIter.mVerInfo[aIter.GetRelativeColIndex()].mColWidth; |
michael@0 | 7147 | } |
michael@0 | 7148 | |
michael@0 | 7149 | /** |
michael@0 | 7150 | * store the column width information while painting horizontal segment |
michael@0 | 7151 | */ |
michael@0 | 7152 | void |
michael@0 | 7153 | BCPaintBorderIterator::StoreColumnWidth(int32_t aIndex) |
michael@0 | 7154 | { |
michael@0 | 7155 | if (IsTableRightMost()) { |
michael@0 | 7156 | mVerInfo[aIndex].mColWidth = mVerInfo[aIndex - 1].mColWidth; |
michael@0 | 7157 | } |
michael@0 | 7158 | else { |
michael@0 | 7159 | nsTableColFrame* col = mTableFirstInFlow->GetColFrame(mColIndex); |
michael@0 | 7160 | if (!col) ABORT0(); |
michael@0 | 7161 | mVerInfo[aIndex].mColWidth = col->GetSize().width; |
michael@0 | 7162 | } |
michael@0 | 7163 | } |
michael@0 | 7164 | /** |
michael@0 | 7165 | * Determine if a vertical segment owns the corder |
michael@0 | 7166 | */ |
michael@0 | 7167 | bool |
michael@0 | 7168 | BCPaintBorderIterator::VerticalSegmentOwnsCorner() |
michael@0 | 7169 | { |
michael@0 | 7170 | mozilla::css::Side cornerOwnerSide = NS_SIDE_TOP; |
michael@0 | 7171 | bool bevel = false; |
michael@0 | 7172 | if (mBCData) { |
michael@0 | 7173 | mBCData->GetCorner(cornerOwnerSide, bevel); |
michael@0 | 7174 | } |
michael@0 | 7175 | // unitialized ownerside, bevel |
michael@0 | 7176 | return (NS_SIDE_TOP == cornerOwnerSide) || |
michael@0 | 7177 | (NS_SIDE_BOTTOM == cornerOwnerSide); |
michael@0 | 7178 | } |
michael@0 | 7179 | |
michael@0 | 7180 | /** |
michael@0 | 7181 | * Paint if necessary a horizontal segment, otherwise accumulate it |
michael@0 | 7182 | * @param aRenderingContext - the rendering context |
michael@0 | 7183 | */ |
michael@0 | 7184 | void |
michael@0 | 7185 | BCPaintBorderIterator::AccumulateOrPaintHorizontalSegment(nsRenderingContext& aRenderingContext) |
michael@0 | 7186 | { |
michael@0 | 7187 | |
michael@0 | 7188 | int32_t relColIndex = GetRelativeColIndex(); |
michael@0 | 7189 | // store the current col width if it hasn't been already |
michael@0 | 7190 | if (mVerInfo[relColIndex].mColWidth < 0) { |
michael@0 | 7191 | StoreColumnWidth(relColIndex); |
michael@0 | 7192 | } |
michael@0 | 7193 | |
michael@0 | 7194 | BCBorderOwner borderOwner = eCellOwner; |
michael@0 | 7195 | BCBorderOwner ignoreBorderOwner; |
michael@0 | 7196 | bool isSegStart = true; |
michael@0 | 7197 | bool ignoreSegStart; |
michael@0 | 7198 | |
michael@0 | 7199 | nscoord leftSegWidth = |
michael@0 | 7200 | mBCData ? mBCData->GetLeftEdge(ignoreBorderOwner, ignoreSegStart) : 0; |
michael@0 | 7201 | nscoord topSegHeight = |
michael@0 | 7202 | mBCData ? mBCData->GetTopEdge(borderOwner, isSegStart) : 0; |
michael@0 | 7203 | |
michael@0 | 7204 | if (mIsNewRow || (IsDamageAreaLeftMost() && IsDamageAreaBottomMost())) { |
michael@0 | 7205 | // reset for every new row and on the bottom of the last row |
michael@0 | 7206 | mHorSeg.mOffsetY = mNextOffsetY; |
michael@0 | 7207 | mNextOffsetY = mNextOffsetY + mRow->GetSize().height; |
michael@0 | 7208 | mHorSeg.mOffsetX = mInitialOffsetX; |
michael@0 | 7209 | mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight); |
michael@0 | 7210 | } |
michael@0 | 7211 | |
michael@0 | 7212 | if (!IsDamageAreaLeftMost() && (isSegStart || IsDamageAreaRightMost() || |
michael@0 | 7213 | VerticalSegmentOwnsCorner())) { |
michael@0 | 7214 | // paint the previous seg or the current one if IsDamageAreaRightMost() |
michael@0 | 7215 | if (mHorSeg.mLength > 0) { |
michael@0 | 7216 | mHorSeg.GetRightCorner(*this, leftSegWidth); |
michael@0 | 7217 | if (mHorSeg.mWidth > 0) { |
michael@0 | 7218 | mHorSeg.Paint(*this, aRenderingContext); |
michael@0 | 7219 | } |
michael@0 | 7220 | mHorSeg.AdvanceOffsetX(mColInc); |
michael@0 | 7221 | } |
michael@0 | 7222 | mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight); |
michael@0 | 7223 | } |
michael@0 | 7224 | mHorSeg.IncludeCurrentBorder(*this); |
michael@0 | 7225 | mVerInfo[relColIndex].mWidth = leftSegWidth; |
michael@0 | 7226 | mVerInfo[relColIndex].mLastCell = mCell; |
michael@0 | 7227 | } |
michael@0 | 7228 | /** |
michael@0 | 7229 | * Paint if necessary a vertical segment, otherwise it |
michael@0 | 7230 | * @param aRenderingContext - the rendering context |
michael@0 | 7231 | */ |
michael@0 | 7232 | void |
michael@0 | 7233 | BCPaintBorderIterator::AccumulateOrPaintVerticalSegment(nsRenderingContext& aRenderingContext) |
michael@0 | 7234 | { |
michael@0 | 7235 | BCBorderOwner borderOwner = eCellOwner; |
michael@0 | 7236 | BCBorderOwner ignoreBorderOwner; |
michael@0 | 7237 | bool isSegStart = true; |
michael@0 | 7238 | bool ignoreSegStart; |
michael@0 | 7239 | |
michael@0 | 7240 | nscoord verSegWidth = |
michael@0 | 7241 | mBCData ? mBCData->GetLeftEdge(borderOwner, isSegStart) : 0; |
michael@0 | 7242 | nscoord horSegHeight = |
michael@0 | 7243 | mBCData ? mBCData->GetTopEdge(ignoreBorderOwner, ignoreSegStart) : 0; |
michael@0 | 7244 | |
michael@0 | 7245 | int32_t relColIndex = GetRelativeColIndex(); |
michael@0 | 7246 | BCVerticalSeg& verSeg = mVerInfo[relColIndex]; |
michael@0 | 7247 | if (!verSeg.mCol) { // on the first damaged row and the first segment in the |
michael@0 | 7248 | // col |
michael@0 | 7249 | verSeg.Initialize(*this); |
michael@0 | 7250 | verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight); |
michael@0 | 7251 | } |
michael@0 | 7252 | |
michael@0 | 7253 | if (!IsDamageAreaTopMost() && (isSegStart || IsDamageAreaBottomMost() || |
michael@0 | 7254 | IsAfterRepeatedHeader() || |
michael@0 | 7255 | StartRepeatedFooter())) { |
michael@0 | 7256 | // paint the previous seg or the current one if IsDamageAreaBottomMost() |
michael@0 | 7257 | if (verSeg.mLength > 0) { |
michael@0 | 7258 | verSeg.GetBottomCorner(*this, horSegHeight); |
michael@0 | 7259 | if (verSeg.mWidth > 0) { |
michael@0 | 7260 | verSeg.Paint(*this, aRenderingContext, horSegHeight); |
michael@0 | 7261 | } |
michael@0 | 7262 | verSeg.AdvanceOffsetY(); |
michael@0 | 7263 | } |
michael@0 | 7264 | verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight); |
michael@0 | 7265 | } |
michael@0 | 7266 | verSeg.IncludeCurrentBorder(*this); |
michael@0 | 7267 | mPrevHorSegHeight = horSegHeight; |
michael@0 | 7268 | } |
michael@0 | 7269 | |
michael@0 | 7270 | /** |
michael@0 | 7271 | * Reset the vertical information cache |
michael@0 | 7272 | */ |
michael@0 | 7273 | void |
michael@0 | 7274 | BCPaintBorderIterator::ResetVerInfo() |
michael@0 | 7275 | { |
michael@0 | 7276 | if (mVerInfo) { |
michael@0 | 7277 | memset(mVerInfo, 0, mDamageArea.width * sizeof(BCVerticalSeg)); |
michael@0 | 7278 | // XXX reinitialize properly |
michael@0 | 7279 | for (int32_t xIndex = 0; xIndex < mDamageArea.width; xIndex++) { |
michael@0 | 7280 | mVerInfo[xIndex].mColWidth = -1; |
michael@0 | 7281 | } |
michael@0 | 7282 | } |
michael@0 | 7283 | } |
michael@0 | 7284 | |
michael@0 | 7285 | /** |
michael@0 | 7286 | * Method to paint BCBorders, this does not use currently display lists although |
michael@0 | 7287 | * it will do this in future |
michael@0 | 7288 | * @param aRenderingContext - the rendering context |
michael@0 | 7289 | * @param aDirtyRect - inside this rectangle the BC Borders will redrawn |
michael@0 | 7290 | */ |
michael@0 | 7291 | void |
michael@0 | 7292 | nsTableFrame::PaintBCBorders(nsRenderingContext& aRenderingContext, |
michael@0 | 7293 | const nsRect& aDirtyRect) |
michael@0 | 7294 | { |
michael@0 | 7295 | // We first transfer the aDirtyRect into cellmap coordinates to compute which |
michael@0 | 7296 | // cell borders need to be painted |
michael@0 | 7297 | BCPaintBorderIterator iter(this); |
michael@0 | 7298 | if (!iter.SetDamageArea(aDirtyRect)) |
michael@0 | 7299 | return; |
michael@0 | 7300 | |
michael@0 | 7301 | // First, paint all of the vertical borders from top to bottom and left to |
michael@0 | 7302 | // right as they become complete. They are painted first, since they are less |
michael@0 | 7303 | // efficient to paint than horizontal segments. They were stored with as few |
michael@0 | 7304 | // segments as possible (since horizontal borders are painted last and |
michael@0 | 7305 | // possibly over them). For every cell in a row that fails in the damage are |
michael@0 | 7306 | // we look up if the current border would start a new segment, if so we paint |
michael@0 | 7307 | // the previously stored vertical segment and start a new segment. After |
michael@0 | 7308 | // this we the now active segment with the current border. These |
michael@0 | 7309 | // segments are stored in mVerInfo to be used on the next row |
michael@0 | 7310 | for (iter.First(); !iter.mAtEnd; iter.Next()) { |
michael@0 | 7311 | iter.AccumulateOrPaintVerticalSegment(aRenderingContext); |
michael@0 | 7312 | } |
michael@0 | 7313 | |
michael@0 | 7314 | // Next, paint all of the horizontal border segments from top to bottom reuse |
michael@0 | 7315 | // the mVerInfo array to keep track of col widths and vertical segments for |
michael@0 | 7316 | // corner calculations |
michael@0 | 7317 | iter.Reset(); |
michael@0 | 7318 | for (iter.First(); !iter.mAtEnd; iter.Next()) { |
michael@0 | 7319 | iter.AccumulateOrPaintHorizontalSegment(aRenderingContext); |
michael@0 | 7320 | } |
michael@0 | 7321 | } |
michael@0 | 7322 | |
michael@0 | 7323 | bool nsTableFrame::RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols) |
michael@0 | 7324 | { |
michael@0 | 7325 | bool result = false; |
michael@0 | 7326 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 7327 | NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated."); |
michael@0 | 7328 | if (cellMap) { |
michael@0 | 7329 | result = cellMap->RowHasSpanningCells(aRowIndex, aNumEffCols); |
michael@0 | 7330 | } |
michael@0 | 7331 | return result; |
michael@0 | 7332 | } |
michael@0 | 7333 | |
michael@0 | 7334 | bool nsTableFrame::RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols) |
michael@0 | 7335 | { |
michael@0 | 7336 | bool result = false; |
michael@0 | 7337 | nsTableCellMap* cellMap = GetCellMap(); |
michael@0 | 7338 | NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated."); |
michael@0 | 7339 | if (cellMap) { |
michael@0 | 7340 | result = cellMap->RowIsSpannedInto(aRowIndex, aNumEffCols); |
michael@0 | 7341 | } |
michael@0 | 7342 | return result; |
michael@0 | 7343 | } |
michael@0 | 7344 | |
michael@0 | 7345 | /* static */ |
michael@0 | 7346 | void |
michael@0 | 7347 | nsTableFrame::InvalidateTableFrame(nsIFrame* aFrame, |
michael@0 | 7348 | const nsRect& aOrigRect, |
michael@0 | 7349 | const nsRect& aOrigVisualOverflow, |
michael@0 | 7350 | bool aIsFirstReflow) |
michael@0 | 7351 | { |
michael@0 | 7352 | nsIFrame* parent = aFrame->GetParent(); |
michael@0 | 7353 | NS_ASSERTION(parent, "What happened here?"); |
michael@0 | 7354 | |
michael@0 | 7355 | if (parent->GetStateBits() & NS_FRAME_FIRST_REFLOW) { |
michael@0 | 7356 | // Don't bother; we'll invalidate the parent's overflow rect when |
michael@0 | 7357 | // we finish reflowing it. |
michael@0 | 7358 | return; |
michael@0 | 7359 | } |
michael@0 | 7360 | |
michael@0 | 7361 | // The part that looks at both the rect and the overflow rect is a |
michael@0 | 7362 | // bit of a hack. See nsBlockFrame::ReflowLine for an eloquent |
michael@0 | 7363 | // description of its hackishness. |
michael@0 | 7364 | // |
michael@0 | 7365 | // This doesn't really make sense now that we have DLBI. |
michael@0 | 7366 | // This code can probably be simplified a fair bit. |
michael@0 | 7367 | nsRect visualOverflow = aFrame->GetVisualOverflowRect(); |
michael@0 | 7368 | if (aIsFirstReflow || |
michael@0 | 7369 | aOrigRect.TopLeft() != aFrame->GetPosition() || |
michael@0 | 7370 | aOrigVisualOverflow.TopLeft() != visualOverflow.TopLeft()) { |
michael@0 | 7371 | // Invalidate the old and new overflow rects. Note that if the |
michael@0 | 7372 | // frame moved, we can't just use aOrigVisualOverflow, since it's in |
michael@0 | 7373 | // coordinates relative to the old position. So invalidate via |
michael@0 | 7374 | // aFrame's parent, and reposition that overflow rect to the right |
michael@0 | 7375 | // place. |
michael@0 | 7376 | // XXXbz this doesn't handle outlines, does it? |
michael@0 | 7377 | aFrame->InvalidateFrame(); |
michael@0 | 7378 | parent->InvalidateFrameWithRect(aOrigVisualOverflow + aOrigRect.TopLeft()); |
michael@0 | 7379 | } else if (aOrigRect.Size() != aFrame->GetSize() || |
michael@0 | 7380 | aOrigVisualOverflow.Size() != visualOverflow.Size()){ |
michael@0 | 7381 | aFrame->InvalidateFrameWithRect(aOrigVisualOverflow); |
michael@0 | 7382 | aFrame->InvalidateFrame(); |
michael@0 | 7383 | parent->InvalidateFrameWithRect(aOrigRect);; |
michael@0 | 7384 | parent->InvalidateFrame(); |
michael@0 | 7385 | } |
michael@0 | 7386 | } |