layout/tables/nsTableFrame.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* 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 }

mercurial