1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/tables/nsTableFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,7386 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "mozilla/Likely.h" 1.11 +#include "mozilla/MathAlgorithms.h" 1.12 + 1.13 +#include "nsCOMPtr.h" 1.14 +#include "nsTableFrame.h" 1.15 +#include "nsRenderingContext.h" 1.16 +#include "nsStyleContext.h" 1.17 +#include "nsStyleConsts.h" 1.18 +#include "nsIContent.h" 1.19 +#include "nsCellMap.h" 1.20 +#include "nsTableCellFrame.h" 1.21 +#include "nsHTMLParts.h" 1.22 +#include "nsTableColFrame.h" 1.23 +#include "nsTableColGroupFrame.h" 1.24 +#include "nsTableRowFrame.h" 1.25 +#include "nsTableRowGroupFrame.h" 1.26 +#include "nsTableOuterFrame.h" 1.27 +#include "nsTablePainter.h" 1.28 + 1.29 +#include "BasicTableLayoutStrategy.h" 1.30 +#include "FixedTableLayoutStrategy.h" 1.31 + 1.32 +#include "nsPresContext.h" 1.33 +#include "nsContentUtils.h" 1.34 +#include "nsCSSRendering.h" 1.35 +#include "nsGkAtoms.h" 1.36 +#include "nsCSSAnonBoxes.h" 1.37 +#include "nsIPresShell.h" 1.38 +#include "nsIDOMElement.h" 1.39 +#include "nsIDOMHTMLElement.h" 1.40 +#include "nsIScriptError.h" 1.41 +#include "nsFrameManager.h" 1.42 +#include "nsError.h" 1.43 +#include "nsAutoPtr.h" 1.44 +#include "nsCSSFrameConstructor.h" 1.45 +#include "nsStyleSet.h" 1.46 +#include "nsDisplayList.h" 1.47 +#include "nsIScrollableFrame.h" 1.48 +#include "nsCSSProps.h" 1.49 +#include "RestyleTracker.h" 1.50 +#include <algorithm> 1.51 + 1.52 +using namespace mozilla; 1.53 +using namespace mozilla::layout; 1.54 + 1.55 +/******************************************************************************** 1.56 + ** nsTableReflowState ** 1.57 + ********************************************************************************/ 1.58 + 1.59 +struct nsTableReflowState { 1.60 + 1.61 + // the real reflow state 1.62 + const nsHTMLReflowState& reflowState; 1.63 + 1.64 + // The table's available size 1.65 + nsSize availSize; 1.66 + 1.67 + // Stationary x-offset 1.68 + nscoord x; 1.69 + 1.70 + // Running y-offset 1.71 + nscoord y; 1.72 + 1.73 + nsTableReflowState(nsPresContext& aPresContext, 1.74 + const nsHTMLReflowState& aReflowState, 1.75 + nsTableFrame& aTableFrame, 1.76 + nscoord aAvailWidth, 1.77 + nscoord aAvailHeight) 1.78 + : reflowState(aReflowState) 1.79 + { 1.80 + Init(aPresContext, aTableFrame, aAvailWidth, aAvailHeight); 1.81 + } 1.82 + 1.83 + void Init(nsPresContext& aPresContext, 1.84 + nsTableFrame& aTableFrame, 1.85 + nscoord aAvailWidth, 1.86 + nscoord aAvailHeight) 1.87 + { 1.88 + nsTableFrame* table = static_cast<nsTableFrame*>(aTableFrame.FirstInFlow()); 1.89 + nsMargin borderPadding = table->GetChildAreaOffset(&reflowState); 1.90 + nscoord cellSpacingX = table->GetCellSpacingX(); 1.91 + 1.92 + x = borderPadding.left + cellSpacingX; 1.93 + y = borderPadding.top; //cellspacing added during reflow 1.94 + 1.95 + availSize.width = aAvailWidth; 1.96 + if (NS_UNCONSTRAINEDSIZE != availSize.width) { 1.97 + availSize.width -= borderPadding.left + borderPadding.right 1.98 + + (2 * cellSpacingX); 1.99 + availSize.width = std::max(0, availSize.width); 1.100 + } 1.101 + 1.102 + availSize.height = aAvailHeight; 1.103 + if (NS_UNCONSTRAINEDSIZE != availSize.height) { 1.104 + availSize.height -= borderPadding.top + borderPadding.bottom 1.105 + + (2 * table->GetCellSpacingY()); 1.106 + availSize.height = std::max(0, availSize.height); 1.107 + } 1.108 + } 1.109 + 1.110 + nsTableReflowState(nsPresContext& aPresContext, 1.111 + const nsHTMLReflowState& aReflowState, 1.112 + nsTableFrame& aTableFrame) 1.113 + : reflowState(aReflowState) 1.114 + { 1.115 + Init(aPresContext, aTableFrame, aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); 1.116 + } 1.117 + 1.118 +}; 1.119 + 1.120 +/******************************************************************************** 1.121 + ** nsTableFrame ** 1.122 + ********************************************************************************/ 1.123 + 1.124 +struct BCPropertyData 1.125 +{ 1.126 + BCPropertyData() : mTopBorderWidth(0), mRightBorderWidth(0), 1.127 + mBottomBorderWidth(0), mLeftBorderWidth(0), 1.128 + mLeftCellBorderWidth(0), mRightCellBorderWidth(0) {} 1.129 + nsIntRect mDamageArea; 1.130 + BCPixelSize mTopBorderWidth; 1.131 + BCPixelSize mRightBorderWidth; 1.132 + BCPixelSize mBottomBorderWidth; 1.133 + BCPixelSize mLeftBorderWidth; 1.134 + BCPixelSize mLeftCellBorderWidth; 1.135 + BCPixelSize mRightCellBorderWidth; 1.136 +}; 1.137 + 1.138 +nsIFrame* 1.139 +nsTableFrame::GetParentStyleContextFrame() const 1.140 +{ 1.141 + // Since our parent, the table outer frame, returned this frame, we 1.142 + // must return whatever our parent would normally have returned. 1.143 + 1.144 + NS_PRECONDITION(mParent, "table constructed without outer table"); 1.145 + if (!mContent->GetParent() && !StyleContext()->GetPseudo()) { 1.146 + // We're the root. We have no style context parent. 1.147 + return nullptr; 1.148 + } 1.149 + 1.150 + return static_cast<nsFrame*>(GetParent())->DoGetParentStyleContextFrame(); 1.151 +} 1.152 + 1.153 + 1.154 +nsIAtom* 1.155 +nsTableFrame::GetType() const 1.156 +{ 1.157 + return nsGkAtoms::tableFrame; 1.158 +} 1.159 + 1.160 + 1.161 +nsTableFrame::nsTableFrame(nsStyleContext* aContext) 1.162 + : nsContainerFrame(aContext), 1.163 + mCellMap(nullptr), 1.164 + mTableLayoutStrategy(nullptr) 1.165 +{ 1.166 + memset(&mBits, 0, sizeof(mBits)); 1.167 +} 1.168 + 1.169 +void 1.170 +nsTableFrame::Init(nsIContent* aContent, 1.171 + nsIFrame* aParent, 1.172 + nsIFrame* aPrevInFlow) 1.173 +{ 1.174 + NS_PRECONDITION(!mCellMap, "Init called twice"); 1.175 + NS_PRECONDITION(!aPrevInFlow || 1.176 + aPrevInFlow->GetType() == nsGkAtoms::tableFrame, 1.177 + "prev-in-flow must be of same type"); 1.178 + 1.179 + // Let the base class do its processing 1.180 + nsContainerFrame::Init(aContent, aParent, aPrevInFlow); 1.181 + 1.182 + // see if border collapse is on, if so set it 1.183 + const nsStyleTableBorder* tableStyle = StyleTableBorder(); 1.184 + bool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse); 1.185 + SetBorderCollapse(borderCollapse); 1.186 + 1.187 + // Create the cell map if this frame is the first-in-flow. 1.188 + if (!aPrevInFlow) { 1.189 + mCellMap = new nsTableCellMap(*this, borderCollapse); 1.190 + } 1.191 + 1.192 + if (aPrevInFlow) { 1.193 + // set my width, because all frames in a table flow are the same width and 1.194 + // code in nsTableOuterFrame depends on this being set 1.195 + mRect.width = aPrevInFlow->GetSize().width; 1.196 + } 1.197 + else { 1.198 + NS_ASSERTION(!mTableLayoutStrategy, "strategy was created before Init was called"); 1.199 + // create the strategy 1.200 + if (IsAutoLayout()) 1.201 + mTableLayoutStrategy = new BasicTableLayoutStrategy(this); 1.202 + else 1.203 + mTableLayoutStrategy = new FixedTableLayoutStrategy(this); 1.204 + } 1.205 +} 1.206 + 1.207 +nsTableFrame::~nsTableFrame() 1.208 +{ 1.209 + delete mCellMap; 1.210 + delete mTableLayoutStrategy; 1.211 +} 1.212 + 1.213 +void 1.214 +nsTableFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.215 +{ 1.216 + mColGroups.DestroyFramesFrom(aDestructRoot); 1.217 + nsContainerFrame::DestroyFrom(aDestructRoot); 1.218 +} 1.219 + 1.220 +// Make sure any views are positioned properly 1.221 +void 1.222 +nsTableFrame::RePositionViews(nsIFrame* aFrame) 1.223 +{ 1.224 + nsContainerFrame::PositionFrameView(aFrame); 1.225 + nsContainerFrame::PositionChildViews(aFrame); 1.226 +} 1.227 + 1.228 +static bool 1.229 +IsRepeatedFrame(nsIFrame* kidFrame) 1.230 +{ 1.231 + return (kidFrame->GetType() == nsGkAtoms::tableRowFrame || 1.232 + kidFrame->GetType() == nsGkAtoms::tableRowGroupFrame) && 1.233 + (kidFrame->GetStateBits() & NS_REPEATED_ROW_OR_ROWGROUP); 1.234 +} 1.235 + 1.236 +bool 1.237 +nsTableFrame::PageBreakAfter(nsIFrame* aSourceFrame, 1.238 + nsIFrame* aNextFrame) 1.239 +{ 1.240 + const nsStyleDisplay* display = aSourceFrame->StyleDisplay(); 1.241 + nsTableRowGroupFrame* prevRg = do_QueryFrame(aSourceFrame); 1.242 + // don't allow a page break after a repeated element ... 1.243 + if ((display->mBreakAfter || (prevRg && prevRg->HasInternalBreakAfter())) && 1.244 + !IsRepeatedFrame(aSourceFrame)) { 1.245 + return !(aNextFrame && IsRepeatedFrame(aNextFrame)); // or before 1.246 + } 1.247 + 1.248 + if (aNextFrame) { 1.249 + display = aNextFrame->StyleDisplay(); 1.250 + // don't allow a page break before a repeated element ... 1.251 + nsTableRowGroupFrame* nextRg = do_QueryFrame(aNextFrame); 1.252 + if ((display->mBreakBefore || 1.253 + (nextRg && nextRg->HasInternalBreakBefore())) && 1.254 + !IsRepeatedFrame(aNextFrame)) { 1.255 + return !IsRepeatedFrame(aSourceFrame); // or after 1.256 + } 1.257 + } 1.258 + return false; 1.259 +} 1.260 + 1.261 +typedef nsTArray<nsIFrame*> FrameTArray; 1.262 + 1.263 +/* static */ void 1.264 +nsTableFrame::DestroyPositionedTablePartArray(void* aPropertyValue) 1.265 +{ 1.266 + auto positionedObjs = static_cast<FrameTArray*>(aPropertyValue); 1.267 + delete positionedObjs; 1.268 +} 1.269 + 1.270 +/* static */ void 1.271 +nsTableFrame::RegisterPositionedTablePart(nsIFrame* aFrame) 1.272 +{ 1.273 + // Supporting relative positioning for table parts other than table cells has 1.274 + // the potential to break sites that apply 'position: relative' to those 1.275 + // parts, expecting nothing to happen. We warn at the console to make tracking 1.276 + // down the issue easy. 1.277 + if (nsGkAtoms::tableCellFrame != aFrame->GetType()) { 1.278 + nsIContent* content = aFrame->GetContent(); 1.279 + nsPresContext* presContext = aFrame->PresContext(); 1.280 + if (content && !presContext->HasWarnedAboutPositionedTableParts()) { 1.281 + presContext->SetHasWarnedAboutPositionedTableParts(); 1.282 + nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, 1.283 + NS_LITERAL_CSTRING("Layout: Tables"), 1.284 + content->OwnerDoc(), 1.285 + nsContentUtils::eLAYOUT_PROPERTIES, 1.286 + "TablePartRelPosWarning"); 1.287 + } 1.288 + } 1.289 + 1.290 + nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(aFrame); 1.291 + MOZ_ASSERT(tableFrame, "Should have a table frame here"); 1.292 + tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation()); 1.293 + 1.294 + // Retrieve the positioned parts array for this table. 1.295 + FrameProperties props = tableFrame->Properties(); 1.296 + auto positionedParts = 1.297 + static_cast<FrameTArray*>(props.Get(PositionedTablePartArray())); 1.298 + 1.299 + // Lazily create the array if it doesn't exist yet. 1.300 + if (!positionedParts) { 1.301 + positionedParts = new FrameTArray; 1.302 + props.Set(PositionedTablePartArray(), positionedParts); 1.303 + } 1.304 + 1.305 + // Add this frame to the list. 1.306 + positionedParts->AppendElement(aFrame); 1.307 +} 1.308 + 1.309 +/* static */ void 1.310 +nsTableFrame::UnregisterPositionedTablePart(nsIFrame* aFrame, 1.311 + nsIFrame* aDestructRoot) 1.312 +{ 1.313 + // Retrieve the table frame, and ensure that we hit aDestructRoot on the way. 1.314 + // If we don't, that means that the table frame will be destroyed, so we don't 1.315 + // need to bother with unregistering this frame. 1.316 + nsTableFrame* tableFrame = GetTableFramePassingThrough(aDestructRoot, aFrame); 1.317 + if (!tableFrame) { 1.318 + return; 1.319 + } 1.320 + tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation()); 1.321 + 1.322 + // Retrieve the positioned parts array for this table. 1.323 + FrameProperties props = tableFrame->Properties(); 1.324 + auto positionedParts = 1.325 + static_cast<FrameTArray*>(props.Get(PositionedTablePartArray())); 1.326 + 1.327 + // Remove the frame. 1.328 + MOZ_ASSERT(positionedParts && 1.329 + positionedParts->IndexOf(aFrame) != FrameTArray::NoIndex, 1.330 + "Asked to unregister a positioned table part that wasn't registered"); 1.331 + if (positionedParts) { 1.332 + positionedParts->RemoveElement(aFrame); 1.333 + } 1.334 +} 1.335 + 1.336 +// XXX this needs to be cleaned up so that the frame constructor breaks out col group 1.337 +// frames into a separate child list, bug 343048. 1.338 +nsresult 1.339 +nsTableFrame::SetInitialChildList(ChildListID aListID, 1.340 + nsFrameList& aChildList) 1.341 +{ 1.342 + 1.343 + if (!mFrames.IsEmpty() || !mColGroups.IsEmpty()) { 1.344 + // We already have child frames which means we've already been 1.345 + // initialized 1.346 + NS_NOTREACHED("unexpected second call to SetInitialChildList"); 1.347 + return NS_ERROR_UNEXPECTED; 1.348 + } 1.349 + if (aListID != kPrincipalList) { 1.350 + // All we know about is the principal child list. 1.351 + NS_NOTREACHED("unknown frame list"); 1.352 + return NS_ERROR_INVALID_ARG; 1.353 + } 1.354 + 1.355 + // XXXbz the below code is an icky cesspit that's only needed in its current 1.356 + // form for two reasons: 1.357 + // 1) Both rowgroups and column groups come in on the principal child list. 1.358 + while (aChildList.NotEmpty()) { 1.359 + nsIFrame* childFrame = aChildList.FirstChild(); 1.360 + aChildList.RemoveFirstChild(); 1.361 + const nsStyleDisplay* childDisplay = childFrame->StyleDisplay(); 1.362 + 1.363 + if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == childDisplay->mDisplay) { 1.364 + NS_ASSERTION(nsGkAtoms::tableColGroupFrame == childFrame->GetType(), 1.365 + "This is not a colgroup"); 1.366 + mColGroups.AppendFrame(nullptr, childFrame); 1.367 + } 1.368 + else { // row groups and unknown frames go on the main list for now 1.369 + mFrames.AppendFrame(nullptr, childFrame); 1.370 + } 1.371 + } 1.372 + 1.373 + // If we have a prev-in-flow, then we're a table that has been split and 1.374 + // so don't treat this like an append 1.375 + if (!GetPrevInFlow()) { 1.376 + // process col groups first so that real cols get constructed before 1.377 + // anonymous ones due to cells in rows. 1.378 + InsertColGroups(0, mColGroups); 1.379 + InsertRowGroups(mFrames); 1.380 + // calc collapsing borders 1.381 + if (IsBorderCollapse()) { 1.382 + SetFullBCDamageArea(); 1.383 + } 1.384 + } 1.385 + 1.386 + return NS_OK; 1.387 +} 1.388 + 1.389 +void nsTableFrame::AttributeChangedFor(nsIFrame* aFrame, 1.390 + nsIContent* aContent, 1.391 + nsIAtom* aAttribute) 1.392 +{ 1.393 + nsTableCellFrame *cellFrame = do_QueryFrame(aFrame); 1.394 + if (cellFrame) { 1.395 + if ((nsGkAtoms::rowspan == aAttribute) || 1.396 + (nsGkAtoms::colspan == aAttribute)) { 1.397 + nsTableCellMap* cellMap = GetCellMap(); 1.398 + if (cellMap) { 1.399 + // for now just remove the cell from the map and reinsert it 1.400 + int32_t rowIndex, colIndex; 1.401 + cellFrame->GetRowIndex(rowIndex); 1.402 + cellFrame->GetColIndex(colIndex); 1.403 + RemoveCell(cellFrame, rowIndex); 1.404 + nsAutoTArray<nsTableCellFrame*, 1> cells; 1.405 + cells.AppendElement(cellFrame); 1.406 + InsertCells(cells, rowIndex, colIndex - 1); 1.407 + 1.408 + // XXX Should this use eStyleChange? It currently doesn't need 1.409 + // to, but it might given more optimization. 1.410 + PresContext()->PresShell()-> 1.411 + FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY); 1.412 + } 1.413 + } 1.414 + } 1.415 +} 1.416 + 1.417 + 1.418 +/* ****** CellMap methods ******* */ 1.419 + 1.420 +/* return the effective col count */ 1.421 +int32_t nsTableFrame::GetEffectiveColCount() const 1.422 +{ 1.423 + int32_t colCount = GetColCount(); 1.424 + if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto) { 1.425 + nsTableCellMap* cellMap = GetCellMap(); 1.426 + if (!cellMap) { 1.427 + return 0; 1.428 + } 1.429 + // don't count cols at the end that don't have originating cells 1.430 + for (int32_t colX = colCount - 1; colX >= 0; colX--) { 1.431 + if (cellMap->GetNumCellsOriginatingInCol(colX) > 0) { 1.432 + break; 1.433 + } 1.434 + colCount--; 1.435 + } 1.436 + } 1.437 + return colCount; 1.438 +} 1.439 + 1.440 +int32_t nsTableFrame::GetIndexOfLastRealCol() 1.441 +{ 1.442 + int32_t numCols = mColFrames.Length(); 1.443 + if (numCols > 0) { 1.444 + for (int32_t colX = numCols - 1; colX >= 0; colX--) { 1.445 + nsTableColFrame* colFrame = GetColFrame(colX); 1.446 + if (colFrame) { 1.447 + if (eColAnonymousCell != colFrame->GetColType()) { 1.448 + return colX; 1.449 + } 1.450 + } 1.451 + } 1.452 + } 1.453 + return -1; 1.454 +} 1.455 + 1.456 +nsTableColFrame* 1.457 +nsTableFrame::GetColFrame(int32_t aColIndex) const 1.458 +{ 1.459 + NS_ASSERTION(!GetPrevInFlow(), "GetColFrame called on next in flow"); 1.460 + int32_t numCols = mColFrames.Length(); 1.461 + if ((aColIndex >= 0) && (aColIndex < numCols)) { 1.462 + return mColFrames.ElementAt(aColIndex); 1.463 + } 1.464 + else { 1.465 + NS_ERROR("invalid col index"); 1.466 + return nullptr; 1.467 + } 1.468 +} 1.469 + 1.470 +int32_t nsTableFrame::GetEffectiveRowSpan(int32_t aRowIndex, 1.471 + const nsTableCellFrame& aCell) const 1.472 +{ 1.473 + nsTableCellMap* cellMap = GetCellMap(); 1.474 + NS_PRECONDITION (nullptr != cellMap, "bad call, cellMap not yet allocated."); 1.475 + 1.476 + int32_t colIndex; 1.477 + aCell.GetColIndex(colIndex); 1.478 + return cellMap->GetEffectiveRowSpan(aRowIndex, colIndex); 1.479 +} 1.480 + 1.481 +int32_t nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell, 1.482 + nsCellMap* aCellMap) 1.483 +{ 1.484 + nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1); 1.485 + 1.486 + int32_t colIndex, rowIndex; 1.487 + aCell.GetColIndex(colIndex); 1.488 + aCell.GetRowIndex(rowIndex); 1.489 + 1.490 + if (aCellMap) 1.491 + return aCellMap->GetRowSpan(rowIndex, colIndex, true); 1.492 + else 1.493 + return tableCellMap->GetEffectiveRowSpan(rowIndex, colIndex); 1.494 +} 1.495 + 1.496 +int32_t nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell, 1.497 + nsCellMap* aCellMap) const 1.498 +{ 1.499 + nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1); 1.500 + 1.501 + int32_t colIndex, rowIndex; 1.502 + aCell.GetColIndex(colIndex); 1.503 + aCell.GetRowIndex(rowIndex); 1.504 + bool ignore; 1.505 + 1.506 + if (aCellMap) 1.507 + return aCellMap->GetEffectiveColSpan(*tableCellMap, rowIndex, colIndex, ignore); 1.508 + else 1.509 + return tableCellMap->GetEffectiveColSpan(rowIndex, colIndex); 1.510 +} 1.511 + 1.512 +bool nsTableFrame::HasMoreThanOneCell(int32_t aRowIndex) const 1.513 +{ 1.514 + nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1); 1.515 + return tableCellMap->HasMoreThanOneCell(aRowIndex); 1.516 +} 1.517 + 1.518 +void nsTableFrame::AdjustRowIndices(int32_t aRowIndex, 1.519 + int32_t aAdjustment) 1.520 +{ 1.521 + // Iterate over the row groups and adjust the row indices of all rows 1.522 + // whose index is >= aRowIndex. 1.523 + RowGroupArray rowGroups; 1.524 + OrderRowGroups(rowGroups); 1.525 + 1.526 + for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { 1.527 + rowGroups[rgX]->AdjustRowIndices(aRowIndex, aAdjustment); 1.528 + } 1.529 +} 1.530 + 1.531 + 1.532 +void nsTableFrame::ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude) 1.533 +{ 1.534 + // Iterate over the row groups and adjust the row indices of all rows 1.535 + // omit the rowgroups that will be inserted later 1.536 + RowGroupArray rowGroups; 1.537 + OrderRowGroups(rowGroups); 1.538 + 1.539 + int32_t rowIndex = 0; 1.540 + nsTHashtable<nsPtrHashKey<nsTableRowGroupFrame> > excludeRowGroups; 1.541 + nsFrameList::Enumerator excludeRowGroupsEnumerator(aRowGroupsToExclude); 1.542 + while (!excludeRowGroupsEnumerator.AtEnd()) { 1.543 + excludeRowGroups.PutEntry(static_cast<nsTableRowGroupFrame*>(excludeRowGroupsEnumerator.get())); 1.544 + excludeRowGroupsEnumerator.Next(); 1.545 + } 1.546 + 1.547 + for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { 1.548 + nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; 1.549 + if (!excludeRowGroups.GetEntry(rgFrame)) { 1.550 + const nsFrameList& rowFrames = rgFrame->PrincipalChildList(); 1.551 + for (nsFrameList::Enumerator rows(rowFrames); !rows.AtEnd(); rows.Next()) { 1.552 + if (NS_STYLE_DISPLAY_TABLE_ROW==rows.get()->StyleDisplay()->mDisplay) { 1.553 + ((nsTableRowFrame *)rows.get())->SetRowIndex(rowIndex); 1.554 + rowIndex++; 1.555 + } 1.556 + } 1.557 + } 1.558 + } 1.559 +} 1.560 +void nsTableFrame::InsertColGroups(int32_t aStartColIndex, 1.561 + const nsFrameList::Slice& aColGroups) 1.562 +{ 1.563 + int32_t colIndex = aStartColIndex; 1.564 + nsFrameList::Enumerator colGroups(aColGroups); 1.565 + for (; !colGroups.AtEnd(); colGroups.Next()) { 1.566 + MOZ_ASSERT(colGroups.get()->GetType() == nsGkAtoms::tableColGroupFrame); 1.567 + nsTableColGroupFrame* cgFrame = 1.568 + static_cast<nsTableColGroupFrame*>(colGroups.get()); 1.569 + cgFrame->SetStartColumnIndex(colIndex); 1.570 + // XXXbz this sucks. AddColsToTable will actually remove colgroups from 1.571 + // the list we're traversing! Need to fix things here. :( I guess this is 1.572 + // why the old code used pointer-to-last-frame as opposed to 1.573 + // pointer-to-frame-after-last.... 1.574 + 1.575 + // How about dealing with this by storing a const reference to the 1.576 + // mNextSibling of the framelist's last frame, instead of storing a pointer 1.577 + // to the first-after-next frame? Will involve making nsFrameList friend 1.578 + // of nsIFrame, but it's time for that anyway. 1.579 + cgFrame->AddColsToTable(colIndex, false, 1.580 + colGroups.get()->PrincipalChildList()); 1.581 + int32_t numCols = cgFrame->GetColCount(); 1.582 + colIndex += numCols; 1.583 + } 1.584 + 1.585 + nsFrameList::Enumerator remainingColgroups = colGroups.GetUnlimitedEnumerator(); 1.586 + if (!remainingColgroups.AtEnd()) { 1.587 + nsTableColGroupFrame::ResetColIndices( 1.588 + static_cast<nsTableColGroupFrame*>(remainingColgroups.get()), colIndex); 1.589 + } 1.590 +} 1.591 + 1.592 +void nsTableFrame::InsertCol(nsTableColFrame& aColFrame, 1.593 + int32_t aColIndex) 1.594 +{ 1.595 + mColFrames.InsertElementAt(aColIndex, &aColFrame); 1.596 + nsTableColType insertedColType = aColFrame.GetColType(); 1.597 + int32_t numCacheCols = mColFrames.Length(); 1.598 + nsTableCellMap* cellMap = GetCellMap(); 1.599 + if (cellMap) { 1.600 + int32_t numMapCols = cellMap->GetColCount(); 1.601 + if (numCacheCols > numMapCols) { 1.602 + bool removedFromCache = false; 1.603 + if (eColAnonymousCell != insertedColType) { 1.604 + nsTableColFrame* lastCol = mColFrames.ElementAt(numCacheCols - 1); 1.605 + if (lastCol) { 1.606 + nsTableColType lastColType = lastCol->GetColType(); 1.607 + if (eColAnonymousCell == lastColType) { 1.608 + // remove the col from the cache 1.609 + mColFrames.RemoveElementAt(numCacheCols - 1); 1.610 + // remove the col from the eColGroupAnonymousCell col group 1.611 + nsTableColGroupFrame* lastColGroup = (nsTableColGroupFrame *)mColGroups.LastChild(); 1.612 + if (lastColGroup) { 1.613 + lastColGroup->RemoveChild(*lastCol, false); 1.614 + 1.615 + // remove the col group if it is empty 1.616 + if (lastColGroup->GetColCount() <= 0) { 1.617 + mColGroups.DestroyFrame((nsIFrame*)lastColGroup); 1.618 + } 1.619 + } 1.620 + removedFromCache = true; 1.621 + } 1.622 + } 1.623 + } 1.624 + if (!removedFromCache) { 1.625 + cellMap->AddColsAtEnd(1); 1.626 + } 1.627 + } 1.628 + } 1.629 + // for now, just bail and recalc all of the collapsing borders 1.630 + if (IsBorderCollapse()) { 1.631 + nsIntRect damageArea(aColIndex, 0, 1, GetRowCount()); 1.632 + AddBCDamageArea(damageArea); 1.633 + } 1.634 +} 1.635 + 1.636 +void nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame, 1.637 + int32_t aColIndex, 1.638 + bool aRemoveFromCache, 1.639 + bool aRemoveFromCellMap) 1.640 +{ 1.641 + if (aRemoveFromCache) { 1.642 + mColFrames.RemoveElementAt(aColIndex); 1.643 + } 1.644 + if (aRemoveFromCellMap) { 1.645 + nsTableCellMap* cellMap = GetCellMap(); 1.646 + if (cellMap) { 1.647 + AppendAnonymousColFrames(1); 1.648 + } 1.649 + } 1.650 + // for now, just bail and recalc all of the collapsing borders 1.651 + if (IsBorderCollapse()) { 1.652 + nsIntRect damageArea(0, 0, GetColCount(), GetRowCount()); 1.653 + AddBCDamageArea(damageArea); 1.654 + } 1.655 +} 1.656 + 1.657 +/** Get the cell map for this table frame. It is not always mCellMap. 1.658 + * Only the first-in-flow has a legit cell map. 1.659 + */ 1.660 +nsTableCellMap* 1.661 +nsTableFrame::GetCellMap() const 1.662 +{ 1.663 + return static_cast<nsTableFrame*>(FirstInFlow())->mCellMap; 1.664 +} 1.665 + 1.666 +// XXX this needs to be moved to nsCSSFrameConstructor 1.667 +nsTableColGroupFrame* 1.668 +nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType) 1.669 +{ 1.670 + nsIContent* colGroupContent = GetContent(); 1.671 + nsPresContext* presContext = PresContext(); 1.672 + nsIPresShell *shell = presContext->PresShell(); 1.673 + 1.674 + nsRefPtr<nsStyleContext> colGroupStyle; 1.675 + colGroupStyle = shell->StyleSet()-> 1.676 + ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup, mStyleContext); 1.677 + // Create a col group frame 1.678 + nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle); 1.679 + ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType); 1.680 + newFrame->Init(colGroupContent, this, nullptr); 1.681 + return (nsTableColGroupFrame *)newFrame; 1.682 +} 1.683 + 1.684 +void 1.685 +nsTableFrame::AppendAnonymousColFrames(int32_t aNumColsToAdd) 1.686 +{ 1.687 + // get the last col group frame 1.688 + nsTableColGroupFrame* colGroupFrame = 1.689 + static_cast<nsTableColGroupFrame*>(mColGroups.LastChild()); 1.690 + 1.691 + if (!colGroupFrame || 1.692 + (colGroupFrame->GetColType() != eColGroupAnonymousCell)) { 1.693 + int32_t colIndex = (colGroupFrame) ? 1.694 + colGroupFrame->GetStartColumnIndex() + 1.695 + colGroupFrame->GetColCount() : 0; 1.696 + colGroupFrame = CreateAnonymousColGroupFrame(eColGroupAnonymousCell); 1.697 + if (!colGroupFrame) { 1.698 + return; 1.699 + } 1.700 + // add the new frame to the child list 1.701 + mColGroups.AppendFrame(this, colGroupFrame); 1.702 + colGroupFrame->SetStartColumnIndex(colIndex); 1.703 + } 1.704 + AppendAnonymousColFrames(colGroupFrame, aNumColsToAdd, eColAnonymousCell, 1.705 + true); 1.706 + 1.707 +} 1.708 + 1.709 +// XXX this needs to be moved to nsCSSFrameConstructor 1.710 +// Right now it only creates the col frames at the end 1.711 +void 1.712 +nsTableFrame::AppendAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame, 1.713 + int32_t aNumColsToAdd, 1.714 + nsTableColType aColType, 1.715 + bool aAddToTable) 1.716 +{ 1.717 + NS_PRECONDITION(aColGroupFrame, "null frame"); 1.718 + NS_PRECONDITION(aColType != eColAnonymousCol, "Shouldn't happen"); 1.719 + 1.720 + nsIPresShell *shell = PresContext()->PresShell(); 1.721 + 1.722 + // Get the last col frame 1.723 + nsFrameList newColFrames; 1.724 + 1.725 + int32_t startIndex = mColFrames.Length(); 1.726 + int32_t lastIndex = startIndex + aNumColsToAdd - 1; 1.727 + 1.728 + for (int32_t childX = startIndex; childX <= lastIndex; childX++) { 1.729 + nsIContent* iContent; 1.730 + nsRefPtr<nsStyleContext> styleContext; 1.731 + nsStyleContext* parentStyleContext; 1.732 + 1.733 + // all anonymous cols that we create here use a pseudo style context of the 1.734 + // col group 1.735 + iContent = aColGroupFrame->GetContent(); 1.736 + parentStyleContext = aColGroupFrame->StyleContext(); 1.737 + styleContext = shell->StyleSet()-> 1.738 + ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableCol, parentStyleContext); 1.739 + // ASSERTION to check for bug 54454 sneaking back in... 1.740 + NS_ASSERTION(iContent, "null content in CreateAnonymousColFrames"); 1.741 + 1.742 + // create the new col frame 1.743 + nsIFrame* colFrame = NS_NewTableColFrame(shell, styleContext); 1.744 + ((nsTableColFrame *) colFrame)->SetColType(aColType); 1.745 + colFrame->Init(iContent, aColGroupFrame, nullptr); 1.746 + 1.747 + newColFrames.AppendFrame(nullptr, colFrame); 1.748 + } 1.749 + nsFrameList& cols = aColGroupFrame->GetWritableChildList(); 1.750 + nsIFrame* oldLastCol = cols.LastChild(); 1.751 + const nsFrameList::Slice& newCols = 1.752 + cols.InsertFrames(nullptr, oldLastCol, newColFrames); 1.753 + if (aAddToTable) { 1.754 + // get the starting col index in the cache 1.755 + int32_t startColIndex; 1.756 + if (oldLastCol) { 1.757 + startColIndex = 1.758 + static_cast<nsTableColFrame*>(oldLastCol)->GetColIndex() + 1; 1.759 + } else { 1.760 + startColIndex = aColGroupFrame->GetStartColumnIndex(); 1.761 + } 1.762 + 1.763 + aColGroupFrame->AddColsToTable(startColIndex, true, newCols); 1.764 + } 1.765 +} 1.766 + 1.767 +void 1.768 +nsTableFrame::MatchCellMapToColCache(nsTableCellMap* aCellMap) 1.769 +{ 1.770 + int32_t numColsInMap = GetColCount(); 1.771 + int32_t numColsInCache = mColFrames.Length(); 1.772 + int32_t numColsToAdd = numColsInMap - numColsInCache; 1.773 + if (numColsToAdd > 0) { 1.774 + // this sets the child list, updates the col cache and cell map 1.775 + AppendAnonymousColFrames(numColsToAdd); 1.776 + } 1.777 + if (numColsToAdd < 0) { 1.778 + int32_t numColsNotRemoved = DestroyAnonymousColFrames(-numColsToAdd); 1.779 + // if the cell map has fewer cols than the cache, correct it 1.780 + if (numColsNotRemoved > 0) { 1.781 + aCellMap->AddColsAtEnd(numColsNotRemoved); 1.782 + } 1.783 + } 1.784 + if (numColsToAdd && HasZeroColSpans()) { 1.785 + SetNeedColSpanExpansion(true); 1.786 + } 1.787 + if (NeedColSpanExpansion()) { 1.788 + // This flag can be set in two ways -- either by changing 1.789 + // the number of columns (that happens in the block above), 1.790 + // or by adding a cell with colspan="0" to the cellmap. To 1.791 + // handle the latter case we need to explicitly check the 1.792 + // flag here -- it may be set even if the number of columns 1.793 + // did not change. 1.794 + // 1.795 + // @see nsCellMap::AppendCell 1.796 + 1.797 + aCellMap->ExpandZeroColSpans(); 1.798 + } 1.799 +} 1.800 + 1.801 +void 1.802 +nsTableFrame::DidResizeColumns() 1.803 +{ 1.804 + NS_PRECONDITION(!GetPrevInFlow(), 1.805 + "should only be called on first-in-flow"); 1.806 + if (mBits.mResizedColumns) 1.807 + return; // already marked 1.808 + 1.809 + for (nsTableFrame *f = this; f; 1.810 + f = static_cast<nsTableFrame*>(f->GetNextInFlow())) 1.811 + f->mBits.mResizedColumns = true; 1.812 +} 1.813 + 1.814 +void 1.815 +nsTableFrame::AppendCell(nsTableCellFrame& aCellFrame, 1.816 + int32_t aRowIndex) 1.817 +{ 1.818 + nsTableCellMap* cellMap = GetCellMap(); 1.819 + if (cellMap) { 1.820 + nsIntRect damageArea(0,0,0,0); 1.821 + cellMap->AppendCell(aCellFrame, aRowIndex, true, damageArea); 1.822 + MatchCellMapToColCache(cellMap); 1.823 + if (IsBorderCollapse()) { 1.824 + AddBCDamageArea(damageArea); 1.825 + } 1.826 + } 1.827 +} 1.828 + 1.829 +void nsTableFrame::InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames, 1.830 + int32_t aRowIndex, 1.831 + int32_t aColIndexBefore) 1.832 +{ 1.833 + nsTableCellMap* cellMap = GetCellMap(); 1.834 + if (cellMap) { 1.835 + nsIntRect damageArea(0,0,0,0); 1.836 + cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea); 1.837 + MatchCellMapToColCache(cellMap); 1.838 + if (IsBorderCollapse()) { 1.839 + AddBCDamageArea(damageArea); 1.840 + } 1.841 + } 1.842 +} 1.843 + 1.844 +// this removes the frames from the col group and table, but not the cell map 1.845 +int32_t 1.846 +nsTableFrame::DestroyAnonymousColFrames(int32_t aNumFrames) 1.847 +{ 1.848 + // only remove cols that are of type eTypeAnonymous cell (they are at the end) 1.849 + int32_t endIndex = mColFrames.Length() - 1; 1.850 + int32_t startIndex = (endIndex - aNumFrames) + 1; 1.851 + int32_t numColsRemoved = 0; 1.852 + for (int32_t colX = endIndex; colX >= startIndex; colX--) { 1.853 + nsTableColFrame* colFrame = GetColFrame(colX); 1.854 + if (colFrame && (eColAnonymousCell == colFrame->GetColType())) { 1.855 + nsTableColGroupFrame* cgFrame = 1.856 + static_cast<nsTableColGroupFrame*>(colFrame->GetParent()); 1.857 + // remove the frame from the colgroup 1.858 + cgFrame->RemoveChild(*colFrame, false); 1.859 + // remove the frame from the cache, but not the cell map 1.860 + RemoveCol(nullptr, colX, true, false); 1.861 + numColsRemoved++; 1.862 + } 1.863 + else { 1.864 + break; 1.865 + } 1.866 + } 1.867 + return (aNumFrames - numColsRemoved); 1.868 +} 1.869 + 1.870 +void nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame, 1.871 + int32_t aRowIndex) 1.872 +{ 1.873 + nsTableCellMap* cellMap = GetCellMap(); 1.874 + if (cellMap) { 1.875 + nsIntRect damageArea(0,0,0,0); 1.876 + cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea); 1.877 + MatchCellMapToColCache(cellMap); 1.878 + if (IsBorderCollapse()) { 1.879 + AddBCDamageArea(damageArea); 1.880 + } 1.881 + } 1.882 +} 1.883 + 1.884 +int32_t 1.885 +nsTableFrame::GetStartRowIndex(nsTableRowGroupFrame* aRowGroupFrame) 1.886 +{ 1.887 + RowGroupArray orderedRowGroups; 1.888 + OrderRowGroups(orderedRowGroups); 1.889 + 1.890 + int32_t rowIndex = 0; 1.891 + for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { 1.892 + nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex]; 1.893 + if (rgFrame == aRowGroupFrame) { 1.894 + break; 1.895 + } 1.896 + int32_t numRows = rgFrame->GetRowCount(); 1.897 + rowIndex += numRows; 1.898 + } 1.899 + return rowIndex; 1.900 +} 1.901 + 1.902 +// this cannot extend beyond a single row group 1.903 +void nsTableFrame::AppendRows(nsTableRowGroupFrame* aRowGroupFrame, 1.904 + int32_t aRowIndex, 1.905 + nsTArray<nsTableRowFrame*>& aRowFrames) 1.906 +{ 1.907 + nsTableCellMap* cellMap = GetCellMap(); 1.908 + if (cellMap) { 1.909 + int32_t absRowIndex = GetStartRowIndex(aRowGroupFrame) + aRowIndex; 1.910 + InsertRows(aRowGroupFrame, aRowFrames, absRowIndex, true); 1.911 + } 1.912 +} 1.913 + 1.914 +// this cannot extend beyond a single row group 1.915 +int32_t 1.916 +nsTableFrame::InsertRows(nsTableRowGroupFrame* aRowGroupFrame, 1.917 + nsTArray<nsTableRowFrame*>& aRowFrames, 1.918 + int32_t aRowIndex, 1.919 + bool aConsiderSpans) 1.920 +{ 1.921 +#ifdef DEBUG_TABLE_CELLMAP 1.922 + printf("=== insertRowsBefore firstRow=%d \n", aRowIndex); 1.923 + Dump(true, false, true); 1.924 +#endif 1.925 + 1.926 + int32_t numColsToAdd = 0; 1.927 + nsTableCellMap* cellMap = GetCellMap(); 1.928 + if (cellMap) { 1.929 + nsIntRect damageArea(0,0,0,0); 1.930 + int32_t origNumRows = cellMap->GetRowCount(); 1.931 + int32_t numNewRows = aRowFrames.Length(); 1.932 + cellMap->InsertRows(aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea); 1.933 + MatchCellMapToColCache(cellMap); 1.934 + if (aRowIndex < origNumRows) { 1.935 + AdjustRowIndices(aRowIndex, numNewRows); 1.936 + } 1.937 + // assign the correct row indices to the new rows. If they were adjusted above 1.938 + // it may not have been done correctly because each row is constructed with index 0 1.939 + for (int32_t rowY = 0; rowY < numNewRows; rowY++) { 1.940 + nsTableRowFrame* rowFrame = aRowFrames.ElementAt(rowY); 1.941 + rowFrame->SetRowIndex(aRowIndex + rowY); 1.942 + } 1.943 + if (IsBorderCollapse()) { 1.944 + AddBCDamageArea(damageArea); 1.945 + } 1.946 + } 1.947 +#ifdef DEBUG_TABLE_CELLMAP 1.948 + printf("=== insertRowsAfter \n"); 1.949 + Dump(true, false, true); 1.950 +#endif 1.951 + 1.952 + return numColsToAdd; 1.953 +} 1.954 + 1.955 +// this cannot extend beyond a single row group 1.956 +void nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame, 1.957 + int32_t aNumRowsToRemove, 1.958 + bool aConsiderSpans) 1.959 +{ 1.960 +#ifdef TBD_OPTIMIZATION 1.961 + // decide if we need to rebalance. we have to do this here because the row group 1.962 + // cannot do it when it gets the dirty reflow corresponding to the frame being destroyed 1.963 + bool stopTelling = false; 1.964 + for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking); 1.965 + kidFrame = kidFrame->GetNextSibling()) { 1.966 + nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame); 1.967 + if (cellFrame) { 1.968 + stopTelling = tableFrame->CellChangedWidth(*cellFrame, cellFrame->GetPass1MaxElementWidth(), 1.969 + cellFrame->GetMaximumWidth(), true); 1.970 + } 1.971 + } 1.972 + // XXX need to consider what happens if there are cells that have rowspans 1.973 + // into the deleted row. Need to consider moving rows if a rebalance doesn't happen 1.974 +#endif 1.975 + 1.976 + int32_t firstRowIndex = aFirstRowFrame.GetRowIndex(); 1.977 +#ifdef DEBUG_TABLE_CELLMAP 1.978 + printf("=== removeRowsBefore firstRow=%d numRows=%d\n", firstRowIndex, aNumRowsToRemove); 1.979 + Dump(true, false, true); 1.980 +#endif 1.981 + nsTableCellMap* cellMap = GetCellMap(); 1.982 + if (cellMap) { 1.983 + nsIntRect damageArea(0,0,0,0); 1.984 + cellMap->RemoveRows(firstRowIndex, aNumRowsToRemove, aConsiderSpans, damageArea); 1.985 + MatchCellMapToColCache(cellMap); 1.986 + if (IsBorderCollapse()) { 1.987 + AddBCDamageArea(damageArea); 1.988 + } 1.989 + } 1.990 + AdjustRowIndices(firstRowIndex, -aNumRowsToRemove); 1.991 +#ifdef DEBUG_TABLE_CELLMAP 1.992 + printf("=== removeRowsAfter\n"); 1.993 + Dump(true, true, true); 1.994 +#endif 1.995 +} 1.996 + 1.997 +// collect the rows ancestors of aFrame 1.998 +int32_t 1.999 +nsTableFrame::CollectRows(nsIFrame* aFrame, 1.1000 + nsTArray<nsTableRowFrame*>& aCollection) 1.1001 +{ 1.1002 + NS_PRECONDITION(aFrame, "null frame"); 1.1003 + int32_t numRows = 0; 1.1004 + nsIFrame* childFrame = aFrame->GetFirstPrincipalChild(); 1.1005 + while (childFrame) { 1.1006 + aCollection.AppendElement(static_cast<nsTableRowFrame*>(childFrame)); 1.1007 + numRows++; 1.1008 + childFrame = childFrame->GetNextSibling(); 1.1009 + } 1.1010 + return numRows; 1.1011 +} 1.1012 + 1.1013 +void 1.1014 +nsTableFrame::InsertRowGroups(const nsFrameList::Slice& aRowGroups) 1.1015 +{ 1.1016 +#ifdef DEBUG_TABLE_CELLMAP 1.1017 + printf("=== insertRowGroupsBefore\n"); 1.1018 + Dump(true, false, true); 1.1019 +#endif 1.1020 + nsTableCellMap* cellMap = GetCellMap(); 1.1021 + if (cellMap) { 1.1022 + RowGroupArray orderedRowGroups; 1.1023 + OrderRowGroups(orderedRowGroups); 1.1024 + 1.1025 + nsAutoTArray<nsTableRowFrame*, 8> rows; 1.1026 + // Loop over the rowgroups and check if some of them are new, if they are 1.1027 + // insert cellmaps in the order that is predefined by OrderRowGroups, 1.1028 + // XXXbz this code is O(N*M) where N is number of new rowgroups 1.1029 + // and M is number of rowgroups we have! 1.1030 + uint32_t rgIndex; 1.1031 + for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { 1.1032 + for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd(); 1.1033 + rowgroups.Next()) { 1.1034 + if (orderedRowGroups[rgIndex] == rowgroups.get()) { 1.1035 + nsTableRowGroupFrame* priorRG = 1.1036 + (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1]; 1.1037 + // create and add the cell map for the row group 1.1038 + cellMap->InsertGroupCellMap(orderedRowGroups[rgIndex], priorRG); 1.1039 + 1.1040 + break; 1.1041 + } 1.1042 + } 1.1043 + } 1.1044 + cellMap->Synchronize(this); 1.1045 + ResetRowIndices(aRowGroups); 1.1046 + 1.1047 + //now that the cellmaps are reordered too insert the rows 1.1048 + for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { 1.1049 + for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd(); 1.1050 + rowgroups.Next()) { 1.1051 + if (orderedRowGroups[rgIndex] == rowgroups.get()) { 1.1052 + nsTableRowGroupFrame* priorRG = 1.1053 + (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1]; 1.1054 + // collect the new row frames in an array and add them to the table 1.1055 + int32_t numRows = CollectRows(rowgroups.get(), rows); 1.1056 + if (numRows > 0) { 1.1057 + int32_t rowIndex = 0; 1.1058 + if (priorRG) { 1.1059 + int32_t priorNumRows = priorRG->GetRowCount(); 1.1060 + rowIndex = priorRG->GetStartRowIndex() + priorNumRows; 1.1061 + } 1.1062 + InsertRows(orderedRowGroups[rgIndex], rows, rowIndex, true); 1.1063 + rows.Clear(); 1.1064 + } 1.1065 + break; 1.1066 + } 1.1067 + } 1.1068 + } 1.1069 + 1.1070 + } 1.1071 +#ifdef DEBUG_TABLE_CELLMAP 1.1072 + printf("=== insertRowGroupsAfter\n"); 1.1073 + Dump(true, true, true); 1.1074 +#endif 1.1075 +} 1.1076 + 1.1077 + 1.1078 +///////////////////////////////////////////////////////////////////////////// 1.1079 +// Child frame enumeration 1.1080 + 1.1081 +const nsFrameList& 1.1082 +nsTableFrame::GetChildList(ChildListID aListID) const 1.1083 +{ 1.1084 + if (aListID == kColGroupList) { 1.1085 + return mColGroups; 1.1086 + } 1.1087 + return nsContainerFrame::GetChildList(aListID); 1.1088 +} 1.1089 + 1.1090 +void 1.1091 +nsTableFrame::GetChildLists(nsTArray<ChildList>* aLists) const 1.1092 +{ 1.1093 + nsContainerFrame::GetChildLists(aLists); 1.1094 + mColGroups.AppendIfNonempty(aLists, kColGroupList); 1.1095 +} 1.1096 + 1.1097 +nsRect 1.1098 +nsDisplayTableItem::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { 1.1099 + *aSnap = false; 1.1100 + return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); 1.1101 +} 1.1102 + 1.1103 +bool 1.1104 +nsDisplayTableItem::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder, 1.1105 + nsIFrame* aFrame) 1.1106 +{ 1.1107 + if (!mPartHasFixedBackground) 1.1108 + return false; 1.1109 + 1.1110 + // If aFrame is mFrame or an ancestor in this document, and aFrame is 1.1111 + // not the viewport frame, then moving aFrame will move mFrame 1.1112 + // relative to the viewport, so our fixed-pos background will change. 1.1113 + return mFrame == aFrame || 1.1114 + nsLayoutUtils::IsProperAncestorFrame(aFrame, mFrame); 1.1115 +} 1.1116 + 1.1117 +/* static */ void 1.1118 +nsDisplayTableItem::UpdateForFrameBackground(nsIFrame* aFrame) 1.1119 +{ 1.1120 + nsStyleContext *bgSC; 1.1121 + if (!nsCSSRendering::FindBackground(aFrame, &bgSC)) 1.1122 + return; 1.1123 + if (!bgSC->StyleBackground()->HasFixedBackground()) 1.1124 + return; 1.1125 + 1.1126 + mPartHasFixedBackground = true; 1.1127 +} 1.1128 + 1.1129 +class nsDisplayTableBorderBackground : public nsDisplayTableItem { 1.1130 +public: 1.1131 + nsDisplayTableBorderBackground(nsDisplayListBuilder* aBuilder, 1.1132 + nsTableFrame* aFrame) : 1.1133 + nsDisplayTableItem(aBuilder, aFrame) { 1.1134 + MOZ_COUNT_CTOR(nsDisplayTableBorderBackground); 1.1135 + } 1.1136 +#ifdef NS_BUILD_REFCNT_LOGGING 1.1137 + virtual ~nsDisplayTableBorderBackground() { 1.1138 + MOZ_COUNT_DTOR(nsDisplayTableBorderBackground); 1.1139 + } 1.1140 +#endif 1.1141 + 1.1142 + virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, 1.1143 + const nsDisplayItemGeometry* aGeometry, 1.1144 + nsRegion *aInvalidRegion) MOZ_OVERRIDE; 1.1145 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.1146 + nsRenderingContext* aCtx) MOZ_OVERRIDE; 1.1147 + NS_DISPLAY_DECL_NAME("TableBorderBackground", TYPE_TABLE_BORDER_BACKGROUND) 1.1148 +}; 1.1149 + 1.1150 +#ifdef DEBUG 1.1151 +static bool 1.1152 +IsFrameAllowedInTable(nsIAtom* aType) 1.1153 +{ 1.1154 + return IS_TABLE_CELL(aType) || 1.1155 + nsGkAtoms::tableRowFrame == aType || 1.1156 + nsGkAtoms::tableRowGroupFrame == aType || 1.1157 + nsGkAtoms::scrollFrame == aType || 1.1158 + nsGkAtoms::tableFrame == aType || 1.1159 + nsGkAtoms::tableColFrame == aType || 1.1160 + nsGkAtoms::tableColGroupFrame == aType; 1.1161 +} 1.1162 +#endif 1.1163 + 1.1164 +/* static */ bool 1.1165 +nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(nsIFrame* aStart, 1.1166 + nsIFrame* aEnd) 1.1167 +{ 1.1168 + for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) { 1.1169 + NS_ASSERTION(IsFrameAllowedInTable(f->GetType()), "unexpected frame type"); 1.1170 + 1.1171 + if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(f)) 1.1172 + return true; 1.1173 + 1.1174 + nsTableCellFrame *cellFrame = do_QueryFrame(f); 1.1175 + if (cellFrame) 1.1176 + continue; 1.1177 + 1.1178 + if (AnyTablePartHasUndecodedBackgroundImage(f->PrincipalChildList().FirstChild(), nullptr)) 1.1179 + return true; 1.1180 + } 1.1181 + 1.1182 + return false; 1.1183 +} 1.1184 + 1.1185 +void 1.1186 +nsDisplayTableBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, 1.1187 + const nsDisplayItemGeometry* aGeometry, 1.1188 + nsRegion *aInvalidRegion) 1.1189 +{ 1.1190 + if (aBuilder->ShouldSyncDecodeImages()) { 1.1191 + if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling()) || 1.1192 + nsTableFrame::AnyTablePartHasUndecodedBackgroundImage( 1.1193 + mFrame->GetChildList(nsIFrame::kColGroupList).FirstChild(), nullptr)) { 1.1194 + bool snap; 1.1195 + aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); 1.1196 + } 1.1197 + } 1.1198 + 1.1199 + nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); 1.1200 +} 1.1201 + 1.1202 +void 1.1203 +nsDisplayTableBorderBackground::Paint(nsDisplayListBuilder* aBuilder, 1.1204 + nsRenderingContext* aCtx) 1.1205 +{ 1.1206 + static_cast<nsTableFrame*>(mFrame)-> 1.1207 + PaintTableBorderBackground(*aCtx, mVisibleRect, 1.1208 + ToReferenceFrame(), 1.1209 + aBuilder->GetBackgroundPaintFlags()); 1.1210 +} 1.1211 + 1.1212 +static int32_t GetTablePartRank(nsDisplayItem* aItem) 1.1213 +{ 1.1214 + nsIAtom* type = aItem->Frame()->GetType(); 1.1215 + if (type == nsGkAtoms::tableFrame) 1.1216 + return 0; 1.1217 + if (type == nsGkAtoms::tableRowGroupFrame) 1.1218 + return 1; 1.1219 + if (type == nsGkAtoms::tableRowFrame) 1.1220 + return 2; 1.1221 + return 3; 1.1222 +} 1.1223 + 1.1224 +static bool CompareByTablePartRank(nsDisplayItem* aItem1, nsDisplayItem* aItem2, 1.1225 + void* aClosure) 1.1226 +{ 1.1227 + return GetTablePartRank(aItem1) <= GetTablePartRank(aItem2); 1.1228 +} 1.1229 + 1.1230 +/* static */ void 1.1231 +nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, 1.1232 + const nsRect& aDirtyRect, const nsDisplayListSet& aLists) 1.1233 +{ 1.1234 + // This is similar to what nsContainerFrame::BuildDisplayListForNonBlockChildren 1.1235 + // does, except that we allow the children's background and borders to go 1.1236 + // in our BorderBackground list. This doesn't really affect background 1.1237 + // painting --- the children won't actually draw their own backgrounds 1.1238 + // because the nsTableFrame already drew them, unless a child has its own 1.1239 + // stacking context, in which case the child won't use its passed-in 1.1240 + // BorderBackground list anyway. It does affect cell borders though; this 1.1241 + // lets us get cell borders into the nsTableFrame's BorderBackground list. 1.1242 + nsIFrame* kid = aFrame->GetFirstPrincipalChild(); 1.1243 + while (kid) { 1.1244 + aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); 1.1245 + kid = kid->GetNextSibling(); 1.1246 + } 1.1247 +} 1.1248 + 1.1249 +/* static */ void 1.1250 +nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder, 1.1251 + nsFrame* aFrame, 1.1252 + const nsRect& aDirtyRect, 1.1253 + const nsDisplayListSet& aLists, 1.1254 + nsDisplayTableItem* aDisplayItem, 1.1255 + DisplayGenericTablePartTraversal aTraversal) 1.1256 +{ 1.1257 + nsDisplayList eventsBorderBackground; 1.1258 + // If we need to sort the event backgrounds, then we'll put descendants' 1.1259 + // display items into their own set of lists. 1.1260 + bool sortEventBackgrounds = aDisplayItem && aBuilder->IsForEventDelivery(); 1.1261 + nsDisplayListCollection separatedCollection; 1.1262 + const nsDisplayListSet* lists = sortEventBackgrounds ? &separatedCollection : &aLists; 1.1263 + 1.1264 + nsAutoPushCurrentTableItem pushTableItem; 1.1265 + if (aDisplayItem) { 1.1266 + pushTableItem.Push(aBuilder, aDisplayItem); 1.1267 + } 1.1268 + 1.1269 + if (aFrame->IsVisibleForPainting(aBuilder)) { 1.1270 + nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem(); 1.1271 + // currentItem may be null, when none of the table parts have a 1.1272 + // background or border 1.1273 + if (currentItem) { 1.1274 + currentItem->UpdateForFrameBackground(aFrame); 1.1275 + } 1.1276 + 1.1277 + // Paint the outset box-shadows for the table frames 1.1278 + bool hasBoxShadow = aFrame->StyleBorder()->mBoxShadow != nullptr; 1.1279 + if (hasBoxShadow) { 1.1280 + lists->BorderBackground()->AppendNewToTop( 1.1281 + new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, aFrame)); 1.1282 + } 1.1283 + 1.1284 + // Create dedicated background display items per-frame when we're 1.1285 + // handling events. 1.1286 + // XXX how to handle collapsed borders? 1.1287 + if (aBuilder->IsForEventDelivery()) { 1.1288 + nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, 1.1289 + lists->BorderBackground()); 1.1290 + } 1.1291 + 1.1292 + // Paint the inset box-shadows for the table frames 1.1293 + if (hasBoxShadow) { 1.1294 + lists->BorderBackground()->AppendNewToTop( 1.1295 + new (aBuilder) nsDisplayBoxShadowInner(aBuilder, aFrame)); 1.1296 + } 1.1297 + } 1.1298 + 1.1299 + aTraversal(aBuilder, aFrame, aDirtyRect, *lists); 1.1300 + 1.1301 + if (sortEventBackgrounds) { 1.1302 + // Ensure that the table frame event background goes before the 1.1303 + // table rowgroups event backgrounds, before the table row event backgrounds, 1.1304 + // before everything else (cells and their blocks) 1.1305 + separatedCollection.BorderBackground()->Sort(aBuilder, CompareByTablePartRank, nullptr); 1.1306 + separatedCollection.MoveTo(aLists); 1.1307 + } 1.1308 + 1.1309 + aFrame->DisplayOutline(aBuilder, aLists); 1.1310 +} 1.1311 + 1.1312 +static bool 1.1313 +AnyTablePartHasBorderOrBackground(nsIFrame* aStart, nsIFrame* aEnd) 1.1314 +{ 1.1315 + for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) { 1.1316 + NS_ASSERTION(IsFrameAllowedInTable(f->GetType()), "unexpected frame type"); 1.1317 + 1.1318 + if (FrameHasBorderOrBackground(f)) 1.1319 + return true; 1.1320 + 1.1321 + nsTableCellFrame *cellFrame = do_QueryFrame(f); 1.1322 + if (cellFrame) 1.1323 + continue; 1.1324 + 1.1325 + if (AnyTablePartHasBorderOrBackground(f->PrincipalChildList().FirstChild(), nullptr)) 1.1326 + return true; 1.1327 + } 1.1328 + 1.1329 + return false; 1.1330 +} 1.1331 + 1.1332 +// table paint code is concerned primarily with borders and bg color 1.1333 +// SEC: TODO: adjust the rect for captions 1.1334 +void 1.1335 +nsTableFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.1336 + const nsRect& aDirtyRect, 1.1337 + const nsDisplayListSet& aLists) 1.1338 +{ 1.1339 + DO_GLOBAL_REFLOW_COUNT_DSP_COLOR("nsTableFrame", NS_RGB(255,128,255)); 1.1340 + 1.1341 + nsDisplayTableItem* item = nullptr; 1.1342 + if (IsVisibleInSelection(aBuilder)) { 1.1343 + if (StyleVisibility()->IsVisible()) { 1.1344 + nsMargin deflate = GetDeflationForBackground(PresContext()); 1.1345 + // If 'deflate' is (0,0,0,0) then we can paint the table background 1.1346 + // in its own display item, so do that to take advantage of 1.1347 + // opacity and visibility optimizations 1.1348 + if (deflate == nsMargin(0, 0, 0, 0)) { 1.1349 + DisplayBackgroundUnconditional(aBuilder, aLists, false); 1.1350 + } 1.1351 + } 1.1352 + 1.1353 + // This background is created if any of the table parts are visible, 1.1354 + // or if we're doing event handling (since DisplayGenericTablePart 1.1355 + // needs the item for the |sortEventBackgrounds|-dependent code). 1.1356 + // Specific visibility decisions are delegated to the table background 1.1357 + // painter, which handles borders and backgrounds for the table. 1.1358 + if (aBuilder->IsForEventDelivery() || 1.1359 + AnyTablePartHasBorderOrBackground(this, GetNextSibling()) || 1.1360 + AnyTablePartHasBorderOrBackground(mColGroups.FirstChild(), nullptr)) { 1.1361 + item = new (aBuilder) nsDisplayTableBorderBackground(aBuilder, this); 1.1362 + aLists.BorderBackground()->AppendNewToTop(item); 1.1363 + } 1.1364 + } 1.1365 + DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item); 1.1366 +} 1.1367 + 1.1368 +nsMargin 1.1369 +nsTableFrame::GetDeflationForBackground(nsPresContext* aPresContext) const 1.1370 +{ 1.1371 + if (eCompatibility_NavQuirks != aPresContext->CompatibilityMode() || 1.1372 + !IsBorderCollapse()) 1.1373 + return nsMargin(0,0,0,0); 1.1374 + 1.1375 + return GetOuterBCBorder(); 1.1376 +} 1.1377 + 1.1378 +// XXX We don't put the borders and backgrounds in tree order like we should. 1.1379 +// That requires some major surgery which we aren't going to do right now. 1.1380 +void 1.1381 +nsTableFrame::PaintTableBorderBackground(nsRenderingContext& aRenderingContext, 1.1382 + const nsRect& aDirtyRect, 1.1383 + nsPoint aPt, uint32_t aBGPaintFlags) 1.1384 +{ 1.1385 + nsPresContext* presContext = PresContext(); 1.1386 + 1.1387 + TableBackgroundPainter painter(this, TableBackgroundPainter::eOrigin_Table, 1.1388 + presContext, aRenderingContext, 1.1389 + aDirtyRect, aPt, aBGPaintFlags); 1.1390 + nsMargin deflate = GetDeflationForBackground(presContext); 1.1391 + // If 'deflate' is (0,0,0,0) then we'll paint the table background 1.1392 + // in a separate display item, so don't do it here. 1.1393 + nsresult rv = painter.PaintTable(this, deflate, deflate != nsMargin(0, 0, 0, 0)); 1.1394 + if (NS_FAILED(rv)) return; 1.1395 + 1.1396 + if (StyleVisibility()->IsVisible()) { 1.1397 + if (!IsBorderCollapse()) { 1.1398 + int skipSides = GetSkipSides(); 1.1399 + nsRect rect(aPt, mRect.Size()); 1.1400 + nsCSSRendering::PaintBorder(presContext, aRenderingContext, this, 1.1401 + aDirtyRect, rect, mStyleContext, skipSides); 1.1402 + } 1.1403 + else { 1.1404 + // XXX we should probably get rid of this translation at some stage 1.1405 + // But that would mean modifying PaintBCBorders, ugh 1.1406 + nsRenderingContext::AutoPushTranslation translate(&aRenderingContext, aPt); 1.1407 + PaintBCBorders(aRenderingContext, aDirtyRect - aPt); 1.1408 + } 1.1409 + } 1.1410 +} 1.1411 + 1.1412 +int 1.1413 +nsTableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const 1.1414 +{ 1.1415 + int skip = 0; 1.1416 + // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto 1.1417 + // account for pagination 1.1418 + if (nullptr != GetPrevInFlow()) { 1.1419 + skip |= LOGICAL_SIDE_B_START; 1.1420 + } 1.1421 + if (nullptr != GetNextInFlow()) { 1.1422 + skip |= LOGICAL_SIDE_B_END; 1.1423 + } 1.1424 + return skip; 1.1425 +} 1.1426 + 1.1427 +void 1.1428 +nsTableFrame::SetColumnDimensions(nscoord aHeight, 1.1429 + const nsMargin& aBorderPadding) 1.1430 +{ 1.1431 + nscoord cellSpacingX = GetCellSpacingX(); 1.1432 + nscoord cellSpacingY = GetCellSpacingY(); 1.1433 + nscoord colHeight = aHeight -= aBorderPadding.top + aBorderPadding.bottom + 1.1434 + 2* cellSpacingY; 1.1435 + 1.1436 + nsTableIterator iter(mColGroups); 1.1437 + nsIFrame* colGroupFrame = iter.First(); 1.1438 + bool tableIsLTR = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR; 1.1439 + int32_t colX =tableIsLTR ? 0 : std::max(0, GetColCount() - 1); 1.1440 + int32_t tableColIncr = tableIsLTR ? 1 : -1; 1.1441 + nsPoint colGroupOrigin(aBorderPadding.left + cellSpacingX, 1.1442 + aBorderPadding.top + cellSpacingY); 1.1443 + while (colGroupFrame) { 1.1444 + MOZ_ASSERT(colGroupFrame->GetType() == nsGkAtoms::tableColGroupFrame); 1.1445 + nscoord colGroupWidth = 0; 1.1446 + nsTableIterator iterCol(*colGroupFrame); 1.1447 + nsIFrame* colFrame = iterCol.First(); 1.1448 + nsPoint colOrigin(0,0); 1.1449 + while (colFrame) { 1.1450 + if (NS_STYLE_DISPLAY_TABLE_COLUMN == 1.1451 + colFrame->StyleDisplay()->mDisplay) { 1.1452 + NS_ASSERTION(colX < GetColCount(), "invalid number of columns"); 1.1453 + nscoord colWidth = GetColumnWidth(colX); 1.1454 + nsRect colRect(colOrigin.x, colOrigin.y, colWidth, colHeight); 1.1455 + colFrame->SetRect(colRect); 1.1456 + colOrigin.x += colWidth + cellSpacingX; 1.1457 + colGroupWidth += colWidth + cellSpacingX; 1.1458 + colX += tableColIncr; 1.1459 + } 1.1460 + colFrame = iterCol.Next(); 1.1461 + } 1.1462 + if (colGroupWidth) { 1.1463 + colGroupWidth -= cellSpacingX; 1.1464 + } 1.1465 + 1.1466 + nsRect colGroupRect(colGroupOrigin.x, colGroupOrigin.y, colGroupWidth, colHeight); 1.1467 + colGroupFrame->SetRect(colGroupRect); 1.1468 + colGroupFrame = iter.Next(); 1.1469 + colGroupOrigin.x += colGroupWidth + cellSpacingX; 1.1470 + } 1.1471 +} 1.1472 + 1.1473 +// SEC: TODO need to worry about continuing frames prev/next in flow for splitting across pages. 1.1474 + 1.1475 +// XXX this could be made more general to handle row modifications that change the 1.1476 +// table height, but first we need to scrutinize every Invalidate 1.1477 +void 1.1478 +nsTableFrame::ProcessRowInserted(nscoord aNewHeight) 1.1479 +{ 1.1480 + SetRowInserted(false); // reset the bit that got us here 1.1481 + nsTableFrame::RowGroupArray rowGroups; 1.1482 + OrderRowGroups(rowGroups); 1.1483 + // find the row group containing the inserted row 1.1484 + for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { 1.1485 + nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; 1.1486 + NS_ASSERTION(rgFrame, "Must have rgFrame here"); 1.1487 + nsIFrame* childFrame = rgFrame->GetFirstPrincipalChild(); 1.1488 + // find the row that was inserted first 1.1489 + while (childFrame) { 1.1490 + nsTableRowFrame *rowFrame = do_QueryFrame(childFrame); 1.1491 + if (rowFrame) { 1.1492 + if (rowFrame->IsFirstInserted()) { 1.1493 + rowFrame->SetFirstInserted(false); 1.1494 + // damage the table from the 1st row inserted to the end of the table 1.1495 + nsIFrame::InvalidateFrame(); 1.1496 + // XXXbz didn't we do this up front? Why do we need to do it again? 1.1497 + SetRowInserted(false); 1.1498 + return; // found it, so leave 1.1499 + } 1.1500 + } 1.1501 + childFrame = childFrame->GetNextSibling(); 1.1502 + } 1.1503 + } 1.1504 +} 1.1505 + 1.1506 +/* virtual */ void 1.1507 +nsTableFrame::MarkIntrinsicWidthsDirty() 1.1508 +{ 1.1509 + nsITableLayoutStrategy* tls = LayoutStrategy(); 1.1510 + if (MOZ_UNLIKELY(!tls)) { 1.1511 + // This is a FrameNeedsReflow() from nsBlockFrame::RemoveFrame() 1.1512 + // walking up the ancestor chain in a table next-in-flow. In this case 1.1513 + // our original first-in-flow (which owns the TableLayoutStrategy) has 1.1514 + // already been destroyed and unhooked from the flow chain and thusly 1.1515 + // LayoutStrategy() returns null. All the frames in the flow will be 1.1516 + // destroyed so no need to mark anything dirty here. See bug 595758. 1.1517 + return; 1.1518 + } 1.1519 + tls->MarkIntrinsicWidthsDirty(); 1.1520 + 1.1521 + // XXXldb Call SetBCDamageArea? 1.1522 + 1.1523 + nsContainerFrame::MarkIntrinsicWidthsDirty(); 1.1524 +} 1.1525 + 1.1526 +/* virtual */ nscoord 1.1527 +nsTableFrame::GetMinWidth(nsRenderingContext *aRenderingContext) 1.1528 +{ 1.1529 + if (NeedToCalcBCBorders()) 1.1530 + CalcBCBorders(); 1.1531 + 1.1532 + ReflowColGroups(aRenderingContext); 1.1533 + 1.1534 + return LayoutStrategy()->GetMinWidth(aRenderingContext); 1.1535 +} 1.1536 + 1.1537 +/* virtual */ nscoord 1.1538 +nsTableFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) 1.1539 +{ 1.1540 + if (NeedToCalcBCBorders()) 1.1541 + CalcBCBorders(); 1.1542 + 1.1543 + ReflowColGroups(aRenderingContext); 1.1544 + 1.1545 + return LayoutStrategy()->GetPrefWidth(aRenderingContext, false); 1.1546 +} 1.1547 + 1.1548 +/* virtual */ nsIFrame::IntrinsicWidthOffsetData 1.1549 +nsTableFrame::IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext) 1.1550 +{ 1.1551 + IntrinsicWidthOffsetData result = 1.1552 + nsContainerFrame::IntrinsicWidthOffsets(aRenderingContext); 1.1553 + 1.1554 + result.hMargin = 0; 1.1555 + result.hPctMargin = 0; 1.1556 + 1.1557 + if (IsBorderCollapse()) { 1.1558 + result.hPadding = 0; 1.1559 + result.hPctPadding = 0; 1.1560 + 1.1561 + nsMargin outerBC = GetIncludedOuterBCBorder(); 1.1562 + result.hBorder = outerBC.LeftRight(); 1.1563 + } 1.1564 + 1.1565 + return result; 1.1566 +} 1.1567 + 1.1568 +/* virtual */ nsSize 1.1569 +nsTableFrame::ComputeSize(nsRenderingContext *aRenderingContext, 1.1570 + nsSize aCBSize, nscoord aAvailableWidth, 1.1571 + nsSize aMargin, nsSize aBorder, nsSize aPadding, 1.1572 + uint32_t aFlags) 1.1573 +{ 1.1574 + nsSize result = 1.1575 + nsContainerFrame::ComputeSize(aRenderingContext, aCBSize, aAvailableWidth, 1.1576 + aMargin, aBorder, aPadding, aFlags); 1.1577 + 1.1578 + // If we're a container for font size inflation, then shrink 1.1579 + // wrapping inside of us should not apply font size inflation. 1.1580 + AutoMaybeDisableFontInflation an(this); 1.1581 + 1.1582 + // Tables never shrink below their min width. 1.1583 + nscoord minWidth = GetMinWidth(aRenderingContext); 1.1584 + if (minWidth > result.width) 1.1585 + result.width = minWidth; 1.1586 + 1.1587 + return result; 1.1588 +} 1.1589 + 1.1590 +nscoord 1.1591 +nsTableFrame::TableShrinkWidthToFit(nsRenderingContext *aRenderingContext, 1.1592 + nscoord aWidthInCB) 1.1593 +{ 1.1594 + // If we're a container for font size inflation, then shrink 1.1595 + // wrapping inside of us should not apply font size inflation. 1.1596 + AutoMaybeDisableFontInflation an(this); 1.1597 + 1.1598 + nscoord result; 1.1599 + nscoord minWidth = GetMinWidth(aRenderingContext); 1.1600 + if (minWidth > aWidthInCB) { 1.1601 + result = minWidth; 1.1602 + } else { 1.1603 + // Tables shrink width to fit with a slightly different algorithm 1.1604 + // from the one they use for their intrinsic widths (the difference 1.1605 + // relates to handling of percentage widths on columns). So this 1.1606 + // function differs from nsFrame::ShrinkWidthToFit by only the 1.1607 + // following line. 1.1608 + // Since we've already called GetMinWidth, we don't need to do any 1.1609 + // of the other stuff GetPrefWidth does. 1.1610 + nscoord prefWidth = 1.1611 + LayoutStrategy()->GetPrefWidth(aRenderingContext, true); 1.1612 + if (prefWidth > aWidthInCB) { 1.1613 + result = aWidthInCB; 1.1614 + } else { 1.1615 + result = prefWidth; 1.1616 + } 1.1617 + } 1.1618 + return result; 1.1619 +} 1.1620 + 1.1621 +/* virtual */ nsSize 1.1622 +nsTableFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, 1.1623 + nsSize aCBSize, nscoord aAvailableWidth, 1.1624 + nsSize aMargin, nsSize aBorder, nsSize aPadding, 1.1625 + bool aShrinkWrap) 1.1626 +{ 1.1627 + // Tables always shrink-wrap. 1.1628 + nscoord cbBased = aAvailableWidth - aMargin.width - aBorder.width - 1.1629 + aPadding.width; 1.1630 + return nsSize(TableShrinkWidthToFit(aRenderingContext, cbBased), 1.1631 + NS_UNCONSTRAINEDSIZE); 1.1632 +} 1.1633 + 1.1634 +// Return true if aParentReflowState.frame or any of its ancestors within 1.1635 +// the containing table have non-auto height. (e.g. pct or fixed height) 1.1636 +bool 1.1637 +nsTableFrame::AncestorsHaveStyleHeight(const nsHTMLReflowState& aParentReflowState) 1.1638 +{ 1.1639 + for (const nsHTMLReflowState* rs = &aParentReflowState; 1.1640 + rs && rs->frame; rs = rs->parentReflowState) { 1.1641 + nsIAtom* frameType = rs->frame->GetType(); 1.1642 + if (IS_TABLE_CELL(frameType) || 1.1643 + (nsGkAtoms::tableRowFrame == frameType) || 1.1644 + (nsGkAtoms::tableRowGroupFrame == frameType)) { 1.1645 + const nsStyleCoord &height = rs->mStylePosition->mHeight; 1.1646 + // calc() with percentages treated like 'auto' on internal table elements 1.1647 + if (height.GetUnit() != eStyleUnit_Auto && 1.1648 + (!height.IsCalcUnit() || !height.HasPercent())) { 1.1649 + return true; 1.1650 + } 1.1651 + } 1.1652 + else if (nsGkAtoms::tableFrame == frameType) { 1.1653 + // we reached the containing table, so always return 1.1654 + return rs->mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto; 1.1655 + } 1.1656 + } 1.1657 + return false; 1.1658 +} 1.1659 + 1.1660 +// See if a special height reflow needs to occur and if so, call RequestSpecialHeightReflow 1.1661 +void 1.1662 +nsTableFrame::CheckRequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState) 1.1663 +{ 1.1664 + NS_ASSERTION(IS_TABLE_CELL(aReflowState.frame->GetType()) || 1.1665 + aReflowState.frame->GetType() == nsGkAtoms::tableRowFrame || 1.1666 + aReflowState.frame->GetType() == nsGkAtoms::tableRowGroupFrame || 1.1667 + aReflowState.frame->GetType() == nsGkAtoms::tableFrame, 1.1668 + "unexpected frame type"); 1.1669 + if (!aReflowState.frame->GetPrevInFlow() && // 1st in flow 1.1670 + (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight() || // no computed height 1.1671 + 0 == aReflowState.ComputedHeight()) && 1.1672 + eStyleUnit_Percent == aReflowState.mStylePosition->mHeight.GetUnit() && // pct height 1.1673 + nsTableFrame::AncestorsHaveStyleHeight(*aReflowState.parentReflowState)) { 1.1674 + nsTableFrame::RequestSpecialHeightReflow(aReflowState); 1.1675 + } 1.1676 +} 1.1677 + 1.1678 +// Notify the frame and its ancestors (up to the containing table) that a special 1.1679 +// height reflow will occur. During a special height reflow, a table, row group, 1.1680 +// row, or cell returns the last size it was reflowed at. However, the table may 1.1681 +// change the height of row groups, rows, cells in DistributeHeightToRows after. 1.1682 +// And the row group can change the height of rows, cells in CalculateRowHeights. 1.1683 +void 1.1684 +nsTableFrame::RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState) 1.1685 +{ 1.1686 + // notify the frame and its ancestors of the special reflow, stopping at the containing table 1.1687 + for (const nsHTMLReflowState* rs = &aReflowState; rs && rs->frame; rs = rs->parentReflowState) { 1.1688 + nsIAtom* frameType = rs->frame->GetType(); 1.1689 + NS_ASSERTION(IS_TABLE_CELL(frameType) || 1.1690 + nsGkAtoms::tableRowFrame == frameType || 1.1691 + nsGkAtoms::tableRowGroupFrame == frameType || 1.1692 + nsGkAtoms::tableFrame == frameType, 1.1693 + "unexpected frame type"); 1.1694 + 1.1695 + rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); 1.1696 + if (nsGkAtoms::tableFrame == frameType) { 1.1697 + NS_ASSERTION(rs != &aReflowState, 1.1698 + "should not request special height reflow for table"); 1.1699 + // always stop when we reach a table 1.1700 + break; 1.1701 + } 1.1702 + } 1.1703 +} 1.1704 + 1.1705 +/****************************************************************************************** 1.1706 + * Before reflow, intrinsic width calculation is done using GetMinWidth 1.1707 + * and GetPrefWidth. This used to be known as pass 1 reflow. 1.1708 + * 1.1709 + * After the intrinsic width calculation, the table determines the 1.1710 + * column widths using BalanceColumnWidths() and 1.1711 + * then reflows each child again with a constrained avail width. This reflow is referred to 1.1712 + * as the pass 2 reflow. 1.1713 + * 1.1714 + * A special height reflow (pass 3 reflow) can occur during an initial or resize reflow 1.1715 + * if (a) a row group, row, cell, or a frame inside a cell has a percent height but no computed 1.1716 + * height or (b) in paginated mode, a table has a height. (a) supports percent nested tables 1.1717 + * contained inside cells whose heights aren't known until after the pass 2 reflow. (b) is 1.1718 + * necessary because the table cannot split until after the pass 2 reflow. The mechanics of 1.1719 + * the special height reflow (variety a) are as follows: 1.1720 + * 1.1721 + * 1) Each table related frame (table, row group, row, cell) implements NeedsSpecialReflow() 1.1722 + * to indicate that it should get the reflow. It does this when it has a percent height but 1.1723 + * no computed height by calling CheckRequestSpecialHeightReflow(). This method calls 1.1724 + * RequestSpecialHeightReflow() which calls SetNeedSpecialReflow() on its ancestors until 1.1725 + * it reaches the containing table and calls SetNeedToInitiateSpecialReflow() on it. For 1.1726 + * percent height frames inside cells, during DidReflow(), the cell's NotifyPercentHeight() 1.1727 + * is called (the cell is the reflow state's mPercentHeightObserver in this case). 1.1728 + * NotifyPercentHeight() calls RequestSpecialHeightReflow(). 1.1729 + * 1.1730 + * 2) After the pass 2 reflow, if the table's NeedToInitiateSpecialReflow(true) was called, it 1.1731 + * will do the special height reflow, setting the reflow state's mFlags.mSpecialHeightReflow 1.1732 + * to true and mSpecialHeightInitiator to itself. It won't do this if IsPrematureSpecialHeightReflow() 1.1733 + * returns true because in that case another special height reflow will be coming along with the 1.1734 + * containing table as the mSpecialHeightInitiator. It is only relevant to do the reflow when 1.1735 + * the mSpecialHeightInitiator is the containing table, because if it is a remote ancestor, then 1.1736 + * appropriate heights will not be known. 1.1737 + * 1.1738 + * 3) Since the heights of the table, row groups, rows, and cells was determined during the pass 2 1.1739 + * reflow, they return their last desired sizes during the special height reflow. The reflow only 1.1740 + * permits percent height frames inside the cells to resize based on the cells height and that height 1.1741 + * was determined during the pass 2 reflow. 1.1742 + * 1.1743 + * So, in the case of deeply nested tables, all of the tables that were told to initiate a special 1.1744 + * reflow will do so, but if a table is already in a special reflow, it won't inititate the reflow 1.1745 + * until the current initiator is its containing table. Since these reflows are only received by 1.1746 + * frames that need them and they don't cause any rebalancing of tables, the extra overhead is minimal. 1.1747 + * 1.1748 + * The type of special reflow that occurs during printing (variety b) follows the same mechanism except 1.1749 + * that all frames will receive the reflow even if they don't really need them. 1.1750 + * 1.1751 + * Open issues with the special height reflow: 1.1752 + * 1.1753 + * 1) At some point there should be 2 kinds of special height reflows because (a) and (b) above are 1.1754 + * really quite different. This would avoid unnecessary reflows during printing. 1.1755 + * 2) When a cell contains frames whose percent heights > 100%, there is data loss (see bug 115245). 1.1756 + * However, this can also occur if a cell has a fixed height and there is no special height reflow. 1.1757 + * 1.1758 + * XXXldb Special height reflow should really be its own method, not 1.1759 + * part of nsIFrame::Reflow. It should then call nsIFrame::Reflow on 1.1760 + * the contents of the cells to do the necessary vertical resizing. 1.1761 + * 1.1762 + ******************************************************************************************/ 1.1763 + 1.1764 +/* Layout the entire inner table. */ 1.1765 +nsresult nsTableFrame::Reflow(nsPresContext* aPresContext, 1.1766 + nsHTMLReflowMetrics& aDesiredSize, 1.1767 + const nsHTMLReflowState& aReflowState, 1.1768 + nsReflowStatus& aStatus) 1.1769 +{ 1.1770 + DO_GLOBAL_REFLOW_COUNT("nsTableFrame"); 1.1771 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); 1.1772 + bool isPaginated = aPresContext->IsPaginated(); 1.1773 + 1.1774 + aStatus = NS_FRAME_COMPLETE; 1.1775 + if (!GetPrevInFlow() && !mTableLayoutStrategy) { 1.1776 + NS_ASSERTION(false, "strategy should have been created in Init"); 1.1777 + return NS_ERROR_NULL_POINTER; 1.1778 + } 1.1779 + nsresult rv = NS_OK; 1.1780 + 1.1781 + // see if collapsing borders need to be calculated 1.1782 + if (!GetPrevInFlow() && IsBorderCollapse() && NeedToCalcBCBorders()) { 1.1783 + CalcBCBorders(); 1.1784 + } 1.1785 + 1.1786 + aDesiredSize.Width() = aReflowState.AvailableWidth(); 1.1787 + 1.1788 + // Check for an overflow list, and append any row group frames being pushed 1.1789 + MoveOverflowToChildList(); 1.1790 + 1.1791 + bool haveDesiredHeight = false; 1.1792 + SetHaveReflowedColGroups(false); 1.1793 + 1.1794 + // Reflow the entire table (pass 2 and possibly pass 3). This phase is necessary during a 1.1795 + // constrained initial reflow and other reflows which require either a strategy init or balance. 1.1796 + // This isn't done during an unconstrained reflow, because it will occur later when the parent 1.1797 + // reflows with a constrained width. 1.1798 + if (NS_SUBTREE_DIRTY(this) || 1.1799 + aReflowState.ShouldReflowAllKids() || 1.1800 + IsGeometryDirty() || 1.1801 + aReflowState.mFlags.mVResize) { 1.1802 + 1.1803 + if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE || 1.1804 + // Also check mVResize, to handle the first Reflow preceding a 1.1805 + // special height Reflow, when we've already had a special height 1.1806 + // Reflow (where mComputedHeight would not be 1.1807 + // NS_UNCONSTRAINEDSIZE, but without a style change in between). 1.1808 + aReflowState.mFlags.mVResize) { 1.1809 + // XXX Eventually, we should modify DistributeHeightToRows to use 1.1810 + // nsTableRowFrame::GetHeight instead of nsIFrame::GetSize().height. 1.1811 + // That way, it will make its calculations based on internal table 1.1812 + // frame heights as they are before they ever had any extra height 1.1813 + // distributed to them. In the meantime, this reflows all the 1.1814 + // internal table frames, which restores them to their state before 1.1815 + // DistributeHeightToRows was called. 1.1816 + SetGeometryDirty(); 1.1817 + } 1.1818 + 1.1819 + bool needToInitiateSpecialReflow = 1.1820 + !!(GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT); 1.1821 + // see if an extra reflow will be necessary in pagination mode when there is a specified table height 1.1822 + if (isPaginated && !GetPrevInFlow() && (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight())) { 1.1823 + nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); 1.1824 + if ((tableSpecifiedHeight > 0) && 1.1825 + (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) { 1.1826 + needToInitiateSpecialReflow = true; 1.1827 + } 1.1828 + } 1.1829 + nsIFrame* lastChildReflowed = nullptr; 1.1830 + 1.1831 + NS_ASSERTION(!aReflowState.mFlags.mSpecialHeightReflow, 1.1832 + "Shouldn't be in special height reflow here!"); 1.1833 + 1.1834 + // do the pass 2 reflow unless this is a special height reflow and we will be 1.1835 + // initiating a special height reflow 1.1836 + // XXXldb I changed this. Should I change it back? 1.1837 + 1.1838 + // if we need to initiate a special height reflow, then don't constrain the 1.1839 + // height of the reflow before that 1.1840 + nscoord availHeight = needToInitiateSpecialReflow 1.1841 + ? NS_UNCONSTRAINEDSIZE : aReflowState.AvailableHeight(); 1.1842 + 1.1843 + ReflowTable(aDesiredSize, aReflowState, availHeight, 1.1844 + lastChildReflowed, aStatus); 1.1845 + 1.1846 + // reevaluate special height reflow conditions 1.1847 + if (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) 1.1848 + needToInitiateSpecialReflow = true; 1.1849 + 1.1850 + // XXXldb Are all these conditions correct? 1.1851 + if (needToInitiateSpecialReflow && NS_FRAME_IS_COMPLETE(aStatus)) { 1.1852 + // XXXldb Do we need to set the mVResize flag on any reflow states? 1.1853 + 1.1854 + nsHTMLReflowState &mutable_rs = 1.1855 + const_cast<nsHTMLReflowState&>(aReflowState); 1.1856 + 1.1857 + // distribute extra vertical space to rows 1.1858 + CalcDesiredHeight(aReflowState, aDesiredSize); 1.1859 + mutable_rs.mFlags.mSpecialHeightReflow = true; 1.1860 + 1.1861 + ReflowTable(aDesiredSize, aReflowState, aReflowState.AvailableHeight(), 1.1862 + lastChildReflowed, aStatus); 1.1863 + 1.1864 + if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) { 1.1865 + // if there is an incomplete child, then set the desired height to include it but not the next one 1.1866 + nsMargin borderPadding = GetChildAreaOffset(&aReflowState); 1.1867 + aDesiredSize.Height() = borderPadding.bottom + GetCellSpacingY() + 1.1868 + lastChildReflowed->GetRect().YMost(); 1.1869 + } 1.1870 + haveDesiredHeight = true; 1.1871 + 1.1872 + mutable_rs.mFlags.mSpecialHeightReflow = false; 1.1873 + } 1.1874 + } 1.1875 + else { 1.1876 + // Calculate the overflow area contribution from our children. 1.1877 + for (nsIFrame* kid = GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) { 1.1878 + ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kid); 1.1879 + } 1.1880 + } 1.1881 + 1.1882 + aDesiredSize.Width() = aReflowState.ComputedWidth() + 1.1883 + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); 1.1884 + if (!haveDesiredHeight) { 1.1885 + CalcDesiredHeight(aReflowState, aDesiredSize); 1.1886 + } 1.1887 + if (IsRowInserted()) { 1.1888 + ProcessRowInserted(aDesiredSize.Height()); 1.1889 + } 1.1890 + 1.1891 + nsMargin borderPadding = GetChildAreaOffset(&aReflowState); 1.1892 + SetColumnDimensions(aDesiredSize.Height(), borderPadding); 1.1893 + if (NeedToCollapse() && 1.1894 + (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableWidth())) { 1.1895 + AdjustForCollapsingRowsCols(aDesiredSize, borderPadding); 1.1896 + } 1.1897 + 1.1898 + // If there are any relatively-positioned table parts, we need to reflow their 1.1899 + // absolutely-positioned descendants now that their dimensions are final. 1.1900 + FixupPositionedTableParts(aPresContext, aDesiredSize, aReflowState); 1.1901 + 1.1902 + // make sure the table overflow area does include the table rect. 1.1903 + nsRect tableRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()) ; 1.1904 + 1.1905 + if (!ShouldApplyOverflowClipping(this, aReflowState.mStyleDisplay)) { 1.1906 + // collapsed border may leak out 1.1907 + nsMargin bcMargin = GetExcludedOuterBCBorder(); 1.1908 + tableRect.Inflate(bcMargin); 1.1909 + } 1.1910 + aDesiredSize.mOverflowAreas.UnionAllWith(tableRect); 1.1911 + 1.1912 + if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) || 1.1913 + nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) { 1.1914 + nsIFrame::InvalidateFrame(); 1.1915 + } 1.1916 + 1.1917 + FinishAndStoreOverflow(&aDesiredSize); 1.1918 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 1.1919 + return rv; 1.1920 +} 1.1921 + 1.1922 +void 1.1923 +nsTableFrame::FixupPositionedTableParts(nsPresContext* aPresContext, 1.1924 + nsHTMLReflowMetrics& aDesiredSize, 1.1925 + const nsHTMLReflowState& aReflowState) 1.1926 +{ 1.1927 + auto positionedParts = 1.1928 + static_cast<FrameTArray*>(Properties().Get(PositionedTablePartArray())); 1.1929 + if (!positionedParts) { 1.1930 + return; 1.1931 + } 1.1932 + 1.1933 + OverflowChangedTracker overflowTracker; 1.1934 + overflowTracker.SetSubtreeRoot(this); 1.1935 + 1.1936 + for (size_t i = 0; i < positionedParts->Length(); ++i) { 1.1937 + nsIFrame* positionedPart = positionedParts->ElementAt(i); 1.1938 + 1.1939 + // As we've already finished reflow, positionedParts's size and overflow 1.1940 + // areas have already been assigned, so we just pull them back out. 1.1941 + nsSize size(positionedPart->GetSize()); 1.1942 + nsHTMLReflowMetrics desiredSize(aReflowState.GetWritingMode()); 1.1943 + desiredSize.Width() = size.width; 1.1944 + desiredSize.Height() = size.height; 1.1945 + desiredSize.mOverflowAreas = positionedPart->GetOverflowAreasRelativeToSelf(); 1.1946 + 1.1947 + // Construct a dummy reflow state and reflow status. 1.1948 + // XXX(seth): Note that the dummy reflow state doesn't have a correct 1.1949 + // chain of parent reflow states. It also doesn't necessarily have a 1.1950 + // correct containing block. 1.1951 + nsHTMLReflowState reflowState(aPresContext, positionedPart, 1.1952 + aReflowState.rendContext, 1.1953 + nsSize(size.width, NS_UNCONSTRAINEDSIZE), 1.1954 + nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE); 1.1955 + nsReflowStatus reflowStatus = NS_FRAME_COMPLETE; 1.1956 + 1.1957 + // Reflow absolutely-positioned descendants of the positioned part. 1.1958 + // FIXME: Unconditionally using NS_UNCONSTRAINEDSIZE for the height and 1.1959 + // ignoring any change to the reflow status aren't correct. We'll never 1.1960 + // paginate absolutely positioned frames. 1.1961 + overflowTracker.AddFrame(positionedPart, 1.1962 + OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED); 1.1963 + nsFrame* positionedFrame = static_cast<nsFrame*>(positionedPart); 1.1964 + positionedFrame->FinishReflowWithAbsoluteFrames(PresContext(), 1.1965 + desiredSize, 1.1966 + reflowState, 1.1967 + reflowStatus, 1.1968 + true); 1.1969 + } 1.1970 + 1.1971 + // Propagate updated overflow areas up the tree. 1.1972 + overflowTracker.Flush(); 1.1973 + 1.1974 + // Update our own overflow areas. (OverflowChangedTracker doesn't update the 1.1975 + // subtree root itself.) 1.1976 + aDesiredSize.SetOverflowAreasToDesiredBounds(); 1.1977 + nsLayoutUtils::UnionChildOverflow(this, aDesiredSize.mOverflowAreas); 1.1978 +} 1.1979 + 1.1980 +bool 1.1981 +nsTableFrame::UpdateOverflow() 1.1982 +{ 1.1983 + nsRect bounds(nsPoint(0, 0), GetSize()); 1.1984 + 1.1985 + // As above in Reflow, make sure the table overflow area includes the table 1.1986 + // rect, and check for collapsed borders leaking out. 1.1987 + if (!ShouldApplyOverflowClipping(this, StyleDisplay())) { 1.1988 + nsMargin bcMargin = GetExcludedOuterBCBorder(); 1.1989 + bounds.Inflate(bcMargin); 1.1990 + } 1.1991 + 1.1992 + nsOverflowAreas overflowAreas(bounds, bounds); 1.1993 + nsLayoutUtils::UnionChildOverflow(this, overflowAreas); 1.1994 + 1.1995 + return FinishAndStoreOverflow(overflowAreas, GetSize()); 1.1996 +} 1.1997 + 1.1998 +nsresult 1.1999 +nsTableFrame::ReflowTable(nsHTMLReflowMetrics& aDesiredSize, 1.2000 + const nsHTMLReflowState& aReflowState, 1.2001 + nscoord aAvailHeight, 1.2002 + nsIFrame*& aLastChildReflowed, 1.2003 + nsReflowStatus& aStatus) 1.2004 +{ 1.2005 + nsresult rv = NS_OK; 1.2006 + aLastChildReflowed = nullptr; 1.2007 + 1.2008 + if (!GetPrevInFlow()) { 1.2009 + mTableLayoutStrategy->ComputeColumnWidths(aReflowState); 1.2010 + } 1.2011 + // Constrain our reflow width to the computed table width (of the 1st in flow). 1.2012 + // and our reflow height to our avail height minus border, padding, cellspacing 1.2013 + aDesiredSize.Width() = aReflowState.ComputedWidth() + 1.2014 + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); 1.2015 + nsTableReflowState reflowState(*PresContext(), aReflowState, *this, 1.2016 + aDesiredSize.Width(), aAvailHeight); 1.2017 + ReflowChildren(reflowState, aStatus, aLastChildReflowed, 1.2018 + aDesiredSize.mOverflowAreas); 1.2019 + 1.2020 + ReflowColGroups(aReflowState.rendContext); 1.2021 + return rv; 1.2022 +} 1.2023 + 1.2024 +nsIFrame* 1.2025 +nsTableFrame::GetFirstBodyRowGroupFrame() 1.2026 +{ 1.2027 + nsIFrame* headerFrame = nullptr; 1.2028 + nsIFrame* footerFrame = nullptr; 1.2029 + 1.2030 + for (nsIFrame* kidFrame = mFrames.FirstChild(); nullptr != kidFrame; ) { 1.2031 + const nsStyleDisplay* childDisplay = kidFrame->StyleDisplay(); 1.2032 + 1.2033 + // We expect the header and footer row group frames to be first, and we only 1.2034 + // allow one header and one footer 1.2035 + if (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == childDisplay->mDisplay) { 1.2036 + if (headerFrame) { 1.2037 + // We already have a header frame and so this header frame is treated 1.2038 + // like an ordinary body row group frame 1.2039 + return kidFrame; 1.2040 + } 1.2041 + headerFrame = kidFrame; 1.2042 + 1.2043 + } else if (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == childDisplay->mDisplay) { 1.2044 + if (footerFrame) { 1.2045 + // We already have a footer frame and so this footer frame is treated 1.2046 + // like an ordinary body row group frame 1.2047 + return kidFrame; 1.2048 + } 1.2049 + footerFrame = kidFrame; 1.2050 + 1.2051 + } else if (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == childDisplay->mDisplay) { 1.2052 + return kidFrame; 1.2053 + } 1.2054 + 1.2055 + // Get the next child 1.2056 + kidFrame = kidFrame->GetNextSibling(); 1.2057 + } 1.2058 + 1.2059 + return nullptr; 1.2060 +} 1.2061 + 1.2062 +// Table specific version that takes into account repeated header and footer 1.2063 +// frames when continuing table frames 1.2064 +void 1.2065 +nsTableFrame::PushChildren(const RowGroupArray& aRowGroups, 1.2066 + int32_t aPushFrom) 1.2067 +{ 1.2068 + NS_PRECONDITION(aPushFrom > 0, "pushing first child"); 1.2069 + 1.2070 + // extract the frames from the array into a sibling list 1.2071 + nsFrameList frames; 1.2072 + uint32_t childX; 1.2073 + for (childX = aPushFrom; childX < aRowGroups.Length(); ++childX) { 1.2074 + nsTableRowGroupFrame* rgFrame = aRowGroups[childX]; 1.2075 + if (!rgFrame->IsRepeatable()) { 1.2076 + mFrames.RemoveFrame(rgFrame); 1.2077 + frames.AppendFrame(nullptr, rgFrame); 1.2078 + } 1.2079 + } 1.2080 + 1.2081 + if (frames.IsEmpty()) { 1.2082 + return; 1.2083 + } 1.2084 + 1.2085 + nsTableFrame* nextInFlow = static_cast<nsTableFrame*>(GetNextInFlow()); 1.2086 + if (nextInFlow) { 1.2087 + // Insert the frames after any repeated header and footer frames. 1.2088 + nsIFrame* firstBodyFrame = nextInFlow->GetFirstBodyRowGroupFrame(); 1.2089 + nsIFrame* prevSibling = nullptr; 1.2090 + if (firstBodyFrame) { 1.2091 + prevSibling = firstBodyFrame->GetPrevSibling(); 1.2092 + } 1.2093 + // When pushing and pulling frames we need to check for whether any 1.2094 + // views need to be reparented. 1.2095 + ReparentFrameViewList(frames, this, nextInFlow); 1.2096 + nextInFlow->mFrames.InsertFrames(nextInFlow, prevSibling, 1.2097 + frames); 1.2098 + } 1.2099 + else { 1.2100 + // Add the frames to our overflow list. 1.2101 + SetOverflowFrames(frames); 1.2102 + } 1.2103 +} 1.2104 + 1.2105 +// collapsing row groups, rows, col groups and cols are accounted for after both passes of 1.2106 +// reflow so that it has no effect on the calculations of reflow. 1.2107 +void 1.2108 +nsTableFrame::AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize, 1.2109 + nsMargin aBorderPadding) 1.2110 +{ 1.2111 + nscoord yTotalOffset = 0; // total offset among all rows in all row groups 1.2112 + 1.2113 + // reset the bit, it will be set again if row/rowgroup or col/colgroup are 1.2114 + // collapsed 1.2115 + SetNeedToCollapse(false); 1.2116 + 1.2117 + // collapse the rows and/or row groups as necessary 1.2118 + // Get the ordered children 1.2119 + RowGroupArray rowGroups; 1.2120 + OrderRowGroups(rowGroups); 1.2121 + 1.2122 + nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow()); 1.2123 + nscoord width = firstInFlow->GetCollapsedWidth(aBorderPadding); 1.2124 + nscoord rgWidth = width - 2 * GetCellSpacingX(); 1.2125 + nsOverflowAreas overflow; 1.2126 + // Walk the list of children 1.2127 + for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) { 1.2128 + nsTableRowGroupFrame* rgFrame = rowGroups[childX]; 1.2129 + NS_ASSERTION(rgFrame, "Must have row group frame here"); 1.2130 + yTotalOffset += rgFrame->CollapseRowGroupIfNecessary(yTotalOffset, rgWidth); 1.2131 + ConsiderChildOverflow(overflow, rgFrame); 1.2132 + } 1.2133 + 1.2134 + aDesiredSize.Height() -= yTotalOffset; 1.2135 + aDesiredSize.Width() = width; 1.2136 + overflow.UnionAllWith(nsRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height())); 1.2137 + FinishAndStoreOverflow(overflow, 1.2138 + nsSize(aDesiredSize.Width(), aDesiredSize.Height())); 1.2139 +} 1.2140 + 1.2141 + 1.2142 +nscoord 1.2143 +nsTableFrame::GetCollapsedWidth(nsMargin aBorderPadding) 1.2144 +{ 1.2145 + NS_ASSERTION(!GetPrevInFlow(), "GetCollapsedWidth called on next in flow"); 1.2146 + nscoord cellSpacingX = GetCellSpacingX(); 1.2147 + nscoord width = cellSpacingX; 1.2148 + width += aBorderPadding.left + aBorderPadding.right; 1.2149 + for (nsIFrame* groupFrame = mColGroups.FirstChild(); groupFrame; 1.2150 + groupFrame = groupFrame->GetNextSibling()) { 1.2151 + const nsStyleVisibility* groupVis = groupFrame->StyleVisibility(); 1.2152 + bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible); 1.2153 + nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame; 1.2154 + for (nsTableColFrame* colFrame = cgFrame->GetFirstColumn(); colFrame; 1.2155 + colFrame = colFrame->GetNextCol()) { 1.2156 + const nsStyleDisplay* colDisplay = colFrame->StyleDisplay(); 1.2157 + int32_t colX = colFrame->GetColIndex(); 1.2158 + if (NS_STYLE_DISPLAY_TABLE_COLUMN == colDisplay->mDisplay) { 1.2159 + const nsStyleVisibility* colVis = colFrame->StyleVisibility(); 1.2160 + bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible); 1.2161 + int32_t colWidth = GetColumnWidth(colX); 1.2162 + if (!collapseGroup && !collapseCol) { 1.2163 + width += colWidth; 1.2164 + if (ColumnHasCellSpacingBefore(colX)) 1.2165 + width += cellSpacingX; 1.2166 + } 1.2167 + else { 1.2168 + SetNeedToCollapse(true); 1.2169 + } 1.2170 + } 1.2171 + } 1.2172 + } 1.2173 + return width; 1.2174 +} 1.2175 + 1.2176 +/* virtual */ void 1.2177 +nsTableFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) 1.2178 +{ 1.2179 + nsContainerFrame::DidSetStyleContext(aOldStyleContext); 1.2180 + 1.2181 + if (!aOldStyleContext) //avoid this on init 1.2182 + return; 1.2183 + 1.2184 + if (IsBorderCollapse() && 1.2185 + BCRecalcNeeded(aOldStyleContext, StyleContext())) { 1.2186 + SetFullBCDamageArea(); 1.2187 + } 1.2188 + 1.2189 + //avoid this on init or nextinflow 1.2190 + if (!mTableLayoutStrategy || GetPrevInFlow()) 1.2191 + return; 1.2192 + 1.2193 + bool isAuto = IsAutoLayout(); 1.2194 + if (isAuto != (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto)) { 1.2195 + nsITableLayoutStrategy* temp; 1.2196 + if (isAuto) 1.2197 + temp = new BasicTableLayoutStrategy(this); 1.2198 + else 1.2199 + temp = new FixedTableLayoutStrategy(this); 1.2200 + 1.2201 + if (temp) { 1.2202 + delete mTableLayoutStrategy; 1.2203 + mTableLayoutStrategy = temp; 1.2204 + } 1.2205 + } 1.2206 +} 1.2207 + 1.2208 + 1.2209 + 1.2210 +nsresult 1.2211 +nsTableFrame::AppendFrames(ChildListID aListID, 1.2212 + nsFrameList& aFrameList) 1.2213 +{ 1.2214 + NS_ASSERTION(aListID == kPrincipalList || aListID == kColGroupList, 1.2215 + "unexpected child list"); 1.2216 + 1.2217 + // Because we actually have two child lists, one for col group frames and one 1.2218 + // for everything else, we need to look at each frame individually 1.2219 + // XXX The frame construction code should be separating out child frames 1.2220 + // based on the type, bug 343048. 1.2221 + while (!aFrameList.IsEmpty()) { 1.2222 + nsIFrame* f = aFrameList.FirstChild(); 1.2223 + aFrameList.RemoveFrame(f); 1.2224 + 1.2225 + // See what kind of frame we have 1.2226 + const nsStyleDisplay* display = f->StyleDisplay(); 1.2227 + 1.2228 + if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) { 1.2229 + nsTableColGroupFrame* lastColGroup = 1.2230 + nsTableColGroupFrame::GetLastRealColGroup(this); 1.2231 + int32_t startColIndex = (lastColGroup) 1.2232 + ? lastColGroup->GetStartColumnIndex() + lastColGroup->GetColCount() : 0; 1.2233 + mColGroups.InsertFrame(nullptr, lastColGroup, f); 1.2234 + // Insert the colgroup and its cols into the table 1.2235 + InsertColGroups(startColIndex, 1.2236 + nsFrameList::Slice(mColGroups, f, f->GetNextSibling())); 1.2237 + } else if (IsRowGroup(display->mDisplay)) { 1.2238 + // Append the new row group frame to the sibling chain 1.2239 + mFrames.AppendFrame(nullptr, f); 1.2240 + 1.2241 + // insert the row group and its rows into the table 1.2242 + InsertRowGroups(nsFrameList::Slice(mFrames, f, nullptr)); 1.2243 + } else { 1.2244 + // Nothing special to do, just add the frame to our child list 1.2245 + NS_NOTREACHED("How did we get here? Frame construction screwed up"); 1.2246 + mFrames.AppendFrame(nullptr, f); 1.2247 + } 1.2248 + } 1.2249 + 1.2250 +#ifdef DEBUG_TABLE_CELLMAP 1.2251 + printf("=== TableFrame::AppendFrames\n"); 1.2252 + Dump(true, true, true); 1.2253 +#endif 1.2254 + PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange, 1.2255 + NS_FRAME_HAS_DIRTY_CHILDREN); 1.2256 + SetGeometryDirty(); 1.2257 + 1.2258 + return NS_OK; 1.2259 +} 1.2260 + 1.2261 +// Needs to be at file scope or ArrayLength fails to compile. 1.2262 +struct ChildListInsertions { 1.2263 + nsIFrame::ChildListID mID; 1.2264 + nsFrameList mList; 1.2265 +}; 1.2266 + 1.2267 +nsresult 1.2268 +nsTableFrame::InsertFrames(ChildListID aListID, 1.2269 + nsIFrame* aPrevFrame, 1.2270 + nsFrameList& aFrameList) 1.2271 +{ 1.2272 + // The frames in aFrameList can be a mix of row group frames and col group 1.2273 + // frames. The problem is that they should go in separate child lists so 1.2274 + // we need to deal with that here... 1.2275 + // XXX The frame construction code should be separating out child frames 1.2276 + // based on the type, bug 343048. 1.2277 + 1.2278 + NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, 1.2279 + "inserting after sibling frame with different parent"); 1.2280 + 1.2281 + if ((aPrevFrame && !aPrevFrame->GetNextSibling()) || 1.2282 + (!aPrevFrame && GetChildList(aListID).IsEmpty())) { 1.2283 + // Treat this like an append; still a workaround for bug 343048. 1.2284 + return AppendFrames(aListID, aFrameList); 1.2285 + } 1.2286 + 1.2287 + // Collect ColGroupFrames into a separate list and insert those separately 1.2288 + // from the other frames (bug 759249). 1.2289 + ChildListInsertions insertions[2]; // ColGroup, other 1.2290 + const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay(); 1.2291 + nsFrameList::FrameLinkEnumerator e(aFrameList); 1.2292 + for (; !aFrameList.IsEmpty(); e.Next()) { 1.2293 + nsIFrame* next = e.NextFrame(); 1.2294 + if (!next || next->StyleDisplay()->mDisplay != display->mDisplay) { 1.2295 + nsFrameList head = aFrameList.ExtractHead(e); 1.2296 + if (display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) { 1.2297 + insertions[0].mID = kColGroupList; 1.2298 + insertions[0].mList.AppendFrames(nullptr, head); 1.2299 + } else { 1.2300 + insertions[1].mID = kPrincipalList; 1.2301 + insertions[1].mList.AppendFrames(nullptr, head); 1.2302 + } 1.2303 + if (!next) { 1.2304 + break; 1.2305 + } 1.2306 + display = next->StyleDisplay(); 1.2307 + } 1.2308 + } 1.2309 + for (uint32_t i = 0; i < ArrayLength(insertions); ++i) { 1.2310 + // We pass aPrevFrame for both ColGroup and other frames since 1.2311 + // HomogenousInsertFrames will only use it if it's a suitable 1.2312 + // prev-sibling for the frames in the frame list. 1.2313 + if (!insertions[i].mList.IsEmpty()) { 1.2314 + HomogenousInsertFrames(insertions[i].mID, aPrevFrame, 1.2315 + insertions[i].mList); 1.2316 + } 1.2317 + } 1.2318 + return NS_OK; 1.2319 +} 1.2320 + 1.2321 +void 1.2322 +nsTableFrame::HomogenousInsertFrames(ChildListID aListID, 1.2323 + nsIFrame* aPrevFrame, 1.2324 + nsFrameList& aFrameList) 1.2325 +{ 1.2326 + // See what kind of frame we have 1.2327 + const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay(); 1.2328 +#ifdef DEBUG 1.2329 + // Verify that either all siblings have display:table-column-group, or they 1.2330 + // all have display values different from table-column-group. 1.2331 + for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { 1.2332 + const nsStyleDisplay* nextDisplay = e.get()->StyleDisplay(); 1.2333 + MOZ_ASSERT((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) == 1.2334 + (nextDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP), 1.2335 + "heterogenous childlist"); 1.2336 + } 1.2337 +#endif 1.2338 + if (aPrevFrame) { 1.2339 + const nsStyleDisplay* prevDisplay = aPrevFrame->StyleDisplay(); 1.2340 + // Make sure they belong on the same frame list 1.2341 + if ((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) != 1.2342 + (prevDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP)) { 1.2343 + // the previous frame is not valid, see comment at ::AppendFrames 1.2344 + // XXXbz Using content indices here means XBL will get screwed 1.2345 + // over... Oh, well. 1.2346 + nsIFrame* pseudoFrame = aFrameList.FirstChild(); 1.2347 + nsIContent* parentContent = GetContent(); 1.2348 + nsIContent* content; 1.2349 + aPrevFrame = nullptr; 1.2350 + while (pseudoFrame && (parentContent == 1.2351 + (content = pseudoFrame->GetContent()))) { 1.2352 + pseudoFrame = pseudoFrame->GetFirstPrincipalChild(); 1.2353 + } 1.2354 + nsCOMPtr<nsIContent> container = content->GetParent(); 1.2355 + if (MOZ_LIKELY(container)) { // XXX need this null-check, see bug 411823. 1.2356 + int32_t newIndex = container->IndexOf(content); 1.2357 + nsIFrame* kidFrame; 1.2358 + bool isColGroup = (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == 1.2359 + display->mDisplay); 1.2360 + nsTableColGroupFrame* lastColGroup; 1.2361 + if (isColGroup) { 1.2362 + kidFrame = mColGroups.FirstChild(); 1.2363 + lastColGroup = nsTableColGroupFrame::GetLastRealColGroup(this); 1.2364 + } 1.2365 + else { 1.2366 + kidFrame = mFrames.FirstChild(); 1.2367 + } 1.2368 + // Important: need to start at a value smaller than all valid indices 1.2369 + int32_t lastIndex = -1; 1.2370 + while (kidFrame) { 1.2371 + if (isColGroup) { 1.2372 + if (kidFrame == lastColGroup) { 1.2373 + aPrevFrame = kidFrame; // there is no real colgroup after this one 1.2374 + break; 1.2375 + } 1.2376 + } 1.2377 + pseudoFrame = kidFrame; 1.2378 + while (pseudoFrame && (parentContent == 1.2379 + (content = pseudoFrame->GetContent()))) { 1.2380 + pseudoFrame = pseudoFrame->GetFirstPrincipalChild(); 1.2381 + } 1.2382 + int32_t index = container->IndexOf(content); 1.2383 + if (index > lastIndex && index < newIndex) { 1.2384 + lastIndex = index; 1.2385 + aPrevFrame = kidFrame; 1.2386 + } 1.2387 + kidFrame = kidFrame->GetNextSibling(); 1.2388 + } 1.2389 + } 1.2390 + } 1.2391 + } 1.2392 + if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) { 1.2393 + NS_ASSERTION(aListID == kColGroupList, "unexpected child list"); 1.2394 + // Insert the column group frames 1.2395 + const nsFrameList::Slice& newColgroups = 1.2396 + mColGroups.InsertFrames(nullptr, aPrevFrame, aFrameList); 1.2397 + // find the starting col index for the first new col group 1.2398 + int32_t startColIndex = 0; 1.2399 + if (aPrevFrame) { 1.2400 + nsTableColGroupFrame* prevColGroup = 1.2401 + (nsTableColGroupFrame*)GetFrameAtOrBefore(this, aPrevFrame, 1.2402 + nsGkAtoms::tableColGroupFrame); 1.2403 + if (prevColGroup) { 1.2404 + startColIndex = prevColGroup->GetStartColumnIndex() + prevColGroup->GetColCount(); 1.2405 + } 1.2406 + } 1.2407 + InsertColGroups(startColIndex, newColgroups); 1.2408 + } else if (IsRowGroup(display->mDisplay)) { 1.2409 + NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); 1.2410 + // Insert the frames in the sibling chain 1.2411 + const nsFrameList::Slice& newRowGroups = 1.2412 + mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList); 1.2413 + 1.2414 + InsertRowGroups(newRowGroups); 1.2415 + } else { 1.2416 + NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); 1.2417 + NS_NOTREACHED("How did we even get here?"); 1.2418 + // Just insert the frame and don't worry about reflowing it 1.2419 + mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList); 1.2420 + return; 1.2421 + } 1.2422 + 1.2423 + PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange, 1.2424 + NS_FRAME_HAS_DIRTY_CHILDREN); 1.2425 + SetGeometryDirty(); 1.2426 +#ifdef DEBUG_TABLE_CELLMAP 1.2427 + printf("=== TableFrame::InsertFrames\n"); 1.2428 + Dump(true, true, true); 1.2429 +#endif 1.2430 + return; 1.2431 +} 1.2432 + 1.2433 +void 1.2434 +nsTableFrame::DoRemoveFrame(ChildListID aListID, 1.2435 + nsIFrame* aOldFrame) 1.2436 +{ 1.2437 + if (aListID == kColGroupList) { 1.2438 + nsIFrame* nextColGroupFrame = aOldFrame->GetNextSibling(); 1.2439 + nsTableColGroupFrame* colGroup = (nsTableColGroupFrame*)aOldFrame; 1.2440 + int32_t firstColIndex = colGroup->GetStartColumnIndex(); 1.2441 + int32_t lastColIndex = firstColIndex + colGroup->GetColCount() - 1; 1.2442 + mColGroups.DestroyFrame(aOldFrame); 1.2443 + nsTableColGroupFrame::ResetColIndices(nextColGroupFrame, firstColIndex); 1.2444 + // remove the cols from the table 1.2445 + int32_t colX; 1.2446 + for (colX = lastColIndex; colX >= firstColIndex; colX--) { 1.2447 + nsTableColFrame* colFrame = mColFrames.SafeElementAt(colX); 1.2448 + if (colFrame) { 1.2449 + RemoveCol(colGroup, colX, true, false); 1.2450 + } 1.2451 + } 1.2452 + 1.2453 + int32_t numAnonymousColsToAdd = GetColCount() - mColFrames.Length(); 1.2454 + if (numAnonymousColsToAdd > 0) { 1.2455 + // this sets the child list, updates the col cache and cell map 1.2456 + AppendAnonymousColFrames(numAnonymousColsToAdd); 1.2457 + } 1.2458 + 1.2459 + } else { 1.2460 + NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); 1.2461 + nsTableRowGroupFrame* rgFrame = 1.2462 + static_cast<nsTableRowGroupFrame*>(aOldFrame); 1.2463 + // remove the row group from the cell map 1.2464 + nsTableCellMap* cellMap = GetCellMap(); 1.2465 + if (cellMap) { 1.2466 + cellMap->RemoveGroupCellMap(rgFrame); 1.2467 + } 1.2468 + 1.2469 + // remove the row group frame from the sibling chain 1.2470 + mFrames.DestroyFrame(aOldFrame); 1.2471 + 1.2472 + // the removal of a row group changes the cellmap, the columns might change 1.2473 + if (cellMap) { 1.2474 + cellMap->Synchronize(this); 1.2475 + // Create an empty slice 1.2476 + ResetRowIndices(nsFrameList::Slice(mFrames, nullptr, nullptr)); 1.2477 + nsIntRect damageArea; 1.2478 + cellMap->RebuildConsideringCells(nullptr, nullptr, 0, 0, false, damageArea); 1.2479 + 1.2480 + static_cast<nsTableFrame*>(FirstInFlow())->MatchCellMapToColCache(cellMap); 1.2481 + } 1.2482 + } 1.2483 +} 1.2484 + 1.2485 +nsresult 1.2486 +nsTableFrame::RemoveFrame(ChildListID aListID, 1.2487 + nsIFrame* aOldFrame) 1.2488 +{ 1.2489 + NS_ASSERTION(aListID == kColGroupList || 1.2490 + NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP != 1.2491 + aOldFrame->StyleDisplay()->mDisplay, 1.2492 + "Wrong list name; use kColGroupList iff colgroup"); 1.2493 + nsIPresShell* shell = PresContext()->PresShell(); 1.2494 + nsTableFrame* lastParent = nullptr; 1.2495 + while (aOldFrame) { 1.2496 + nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation(); 1.2497 + nsTableFrame* parent = static_cast<nsTableFrame*>(aOldFrame->GetParent()); 1.2498 + if (parent != lastParent) { 1.2499 + parent->DrainSelfOverflowList(); 1.2500 + } 1.2501 + parent->DoRemoveFrame(aListID, aOldFrame); 1.2502 + aOldFrame = oldFrameNextContinuation; 1.2503 + if (parent != lastParent) { 1.2504 + // for now, just bail and recalc all of the collapsing borders 1.2505 + // as the cellmap changes we need to recalc 1.2506 + if (parent->IsBorderCollapse()) { 1.2507 + parent->SetFullBCDamageArea(); 1.2508 + } 1.2509 + parent->SetGeometryDirty(); 1.2510 + shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange, 1.2511 + NS_FRAME_HAS_DIRTY_CHILDREN); 1.2512 + lastParent = parent; 1.2513 + } 1.2514 + } 1.2515 +#ifdef DEBUG_TABLE_CELLMAP 1.2516 + printf("=== TableFrame::RemoveFrame\n"); 1.2517 + Dump(true, true, true); 1.2518 +#endif 1.2519 + return NS_OK; 1.2520 +} 1.2521 + 1.2522 +/* virtual */ nsMargin 1.2523 +nsTableFrame::GetUsedBorder() const 1.2524 +{ 1.2525 + if (!IsBorderCollapse()) 1.2526 + return nsContainerFrame::GetUsedBorder(); 1.2527 + 1.2528 + return GetIncludedOuterBCBorder(); 1.2529 +} 1.2530 + 1.2531 +/* virtual */ nsMargin 1.2532 +nsTableFrame::GetUsedPadding() const 1.2533 +{ 1.2534 + if (!IsBorderCollapse()) 1.2535 + return nsContainerFrame::GetUsedPadding(); 1.2536 + 1.2537 + return nsMargin(0,0,0,0); 1.2538 +} 1.2539 + 1.2540 +/* virtual */ nsMargin 1.2541 +nsTableFrame::GetUsedMargin() const 1.2542 +{ 1.2543 + // The margin is inherited to the outer table frame via 1.2544 + // the ::-moz-table-outer rule in ua.css. 1.2545 + return nsMargin(0, 0, 0, 0); 1.2546 +} 1.2547 + 1.2548 +// Destructor function for BCPropertyData properties 1.2549 +static void 1.2550 +DestroyBCProperty(void* aPropertyValue) 1.2551 +{ 1.2552 + delete static_cast<BCPropertyData*>(aPropertyValue); 1.2553 +} 1.2554 + 1.2555 +NS_DECLARE_FRAME_PROPERTY(TableBCProperty, DestroyBCProperty) 1.2556 + 1.2557 +BCPropertyData* 1.2558 +nsTableFrame::GetBCProperty(bool aCreateIfNecessary) const 1.2559 +{ 1.2560 + FrameProperties props = Properties(); 1.2561 + BCPropertyData* value = static_cast<BCPropertyData*> 1.2562 + (props.Get(TableBCProperty())); 1.2563 + if (!value && aCreateIfNecessary) { 1.2564 + value = new BCPropertyData(); 1.2565 + props.Set(TableBCProperty(), value); 1.2566 + } 1.2567 + 1.2568 + return value; 1.2569 +} 1.2570 + 1.2571 +static void 1.2572 +DivideBCBorderSize(BCPixelSize aPixelSize, 1.2573 + BCPixelSize& aSmallHalf, 1.2574 + BCPixelSize& aLargeHalf) 1.2575 +{ 1.2576 + aSmallHalf = aPixelSize / 2; 1.2577 + aLargeHalf = aPixelSize - aSmallHalf; 1.2578 +} 1.2579 + 1.2580 +nsMargin 1.2581 +nsTableFrame::GetOuterBCBorder() const 1.2582 +{ 1.2583 + if (NeedToCalcBCBorders()) 1.2584 + const_cast<nsTableFrame*>(this)->CalcBCBorders(); 1.2585 + 1.2586 + nsMargin border(0, 0, 0, 0); 1.2587 + int32_t p2t = nsPresContext::AppUnitsPerCSSPixel(); 1.2588 + BCPropertyData* propData = GetBCProperty(); 1.2589 + if (propData) { 1.2590 + border.top = BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth); 1.2591 + border.right = BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightBorderWidth); 1.2592 + border.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth); 1.2593 + border.left = BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftBorderWidth); 1.2594 + } 1.2595 + return border; 1.2596 +} 1.2597 + 1.2598 +nsMargin 1.2599 +nsTableFrame::GetIncludedOuterBCBorder() const 1.2600 +{ 1.2601 + if (NeedToCalcBCBorders()) 1.2602 + const_cast<nsTableFrame*>(this)->CalcBCBorders(); 1.2603 + 1.2604 + nsMargin border(0, 0, 0, 0); 1.2605 + int32_t p2t = nsPresContext::AppUnitsPerCSSPixel(); 1.2606 + BCPropertyData* propData = GetBCProperty(); 1.2607 + if (propData) { 1.2608 + border.top += BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth); 1.2609 + border.right += BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightCellBorderWidth); 1.2610 + border.bottom += BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth); 1.2611 + border.left += BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftCellBorderWidth); 1.2612 + } 1.2613 + return border; 1.2614 +} 1.2615 + 1.2616 +nsMargin 1.2617 +nsTableFrame::GetExcludedOuterBCBorder() const 1.2618 +{ 1.2619 + return GetOuterBCBorder() - GetIncludedOuterBCBorder(); 1.2620 +} 1.2621 + 1.2622 +static 1.2623 +void GetSeparateModelBorderPadding(const nsHTMLReflowState* aReflowState, 1.2624 + nsStyleContext& aStyleContext, 1.2625 + nsMargin& aBorderPadding) 1.2626 +{ 1.2627 + // XXXbz Either we _do_ have a reflow state and then we can use its 1.2628 + // mComputedBorderPadding or we don't and then we get the padding 1.2629 + // wrong! 1.2630 + const nsStyleBorder* border = aStyleContext.StyleBorder(); 1.2631 + aBorderPadding = border->GetComputedBorder(); 1.2632 + if (aReflowState) { 1.2633 + aBorderPadding += aReflowState->ComputedPhysicalPadding(); 1.2634 + } 1.2635 +} 1.2636 + 1.2637 +nsMargin 1.2638 +nsTableFrame::GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const 1.2639 +{ 1.2640 + nsMargin offset(0,0,0,0); 1.2641 + if (IsBorderCollapse()) { 1.2642 + offset = GetIncludedOuterBCBorder(); 1.2643 + } 1.2644 + else { 1.2645 + GetSeparateModelBorderPadding(aReflowState, *mStyleContext, offset); 1.2646 + } 1.2647 + return offset; 1.2648 +} 1.2649 + 1.2650 +void 1.2651 +nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState) 1.2652 +{ 1.2653 + nsMargin collapseBorder; 1.2654 + nsMargin padding(0,0,0,0); 1.2655 + nsMargin* pCollapseBorder = nullptr; 1.2656 + nsPresContext* presContext = PresContext(); 1.2657 + if (IsBorderCollapse()) { 1.2658 + nsTableRowGroupFrame* rgFrame = 1.2659 + static_cast<nsTableRowGroupFrame*>(aReflowState.frame); 1.2660 + pCollapseBorder = rgFrame->GetBCBorderWidth(collapseBorder); 1.2661 + } 1.2662 + aReflowState.Init(presContext, -1, -1, pCollapseBorder, &padding); 1.2663 + 1.2664 + NS_ASSERTION(!mBits.mResizedColumns || 1.2665 + !aReflowState.parentReflowState->mFlags.mSpecialHeightReflow, 1.2666 + "should not resize columns on special height reflow"); 1.2667 + if (mBits.mResizedColumns) { 1.2668 + aReflowState.mFlags.mHResize = true; 1.2669 + } 1.2670 +} 1.2671 + 1.2672 +// Position and size aKidFrame and update our reflow state. The origin of 1.2673 +// aKidRect is relative to the upper-left origin of our frame 1.2674 +void nsTableFrame::PlaceChild(nsTableReflowState& aReflowState, 1.2675 + nsIFrame* aKidFrame, 1.2676 + nsHTMLReflowMetrics& aKidDesiredSize, 1.2677 + const nsRect& aOriginalKidRect, 1.2678 + const nsRect& aOriginalKidVisualOverflow) 1.2679 +{ 1.2680 + bool isFirstReflow = 1.2681 + (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; 1.2682 + 1.2683 + // Place and size the child 1.2684 + FinishReflowChild(aKidFrame, PresContext(), aKidDesiredSize, nullptr, 1.2685 + aReflowState.x, aReflowState.y, 0); 1.2686 + 1.2687 + InvalidateTableFrame(aKidFrame, aOriginalKidRect, aOriginalKidVisualOverflow, 1.2688 + isFirstReflow); 1.2689 + 1.2690 + // Adjust the running y-offset 1.2691 + aReflowState.y += aKidDesiredSize.Height(); 1.2692 + 1.2693 + // If our height is constrained, then update the available height 1.2694 + if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { 1.2695 + aReflowState.availSize.height -= aKidDesiredSize.Height(); 1.2696 + } 1.2697 +} 1.2698 + 1.2699 +void 1.2700 +nsTableFrame::OrderRowGroups(RowGroupArray& aChildren, 1.2701 + nsTableRowGroupFrame** aHead, 1.2702 + nsTableRowGroupFrame** aFoot) const 1.2703 +{ 1.2704 + aChildren.Clear(); 1.2705 + nsTableRowGroupFrame* head = nullptr; 1.2706 + nsTableRowGroupFrame* foot = nullptr; 1.2707 + 1.2708 + nsIFrame* kidFrame = mFrames.FirstChild(); 1.2709 + while (kidFrame) { 1.2710 + const nsStyleDisplay* kidDisplay = kidFrame->StyleDisplay(); 1.2711 + nsTableRowGroupFrame* rowGroup = 1.2712 + static_cast<nsTableRowGroupFrame*>(kidFrame); 1.2713 + 1.2714 + switch (kidDisplay->mDisplay) { 1.2715 + case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP: 1.2716 + if (head) { // treat additional thead like tbody 1.2717 + aChildren.AppendElement(rowGroup); 1.2718 + } 1.2719 + else { 1.2720 + head = rowGroup; 1.2721 + } 1.2722 + break; 1.2723 + case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP: 1.2724 + if (foot) { // treat additional tfoot like tbody 1.2725 + aChildren.AppendElement(rowGroup); 1.2726 + } 1.2727 + else { 1.2728 + foot = rowGroup; 1.2729 + } 1.2730 + break; 1.2731 + case NS_STYLE_DISPLAY_TABLE_ROW_GROUP: 1.2732 + aChildren.AppendElement(rowGroup); 1.2733 + break; 1.2734 + default: 1.2735 + NS_NOTREACHED("How did this produce an nsTableRowGroupFrame?"); 1.2736 + // Just ignore it 1.2737 + break; 1.2738 + } 1.2739 + // Get the next sibling but skip it if it's also the next-in-flow, since 1.2740 + // a next-in-flow will not be part of the current table. 1.2741 + while (kidFrame) { 1.2742 + nsIFrame* nif = kidFrame->GetNextInFlow(); 1.2743 + kidFrame = kidFrame->GetNextSibling(); 1.2744 + if (kidFrame != nif) 1.2745 + break; 1.2746 + } 1.2747 + } 1.2748 + 1.2749 + // put the thead first 1.2750 + if (head) { 1.2751 + aChildren.InsertElementAt(0, head); 1.2752 + } 1.2753 + if (aHead) 1.2754 + *aHead = head; 1.2755 + // put the tfoot after the last tbody 1.2756 + if (foot) { 1.2757 + aChildren.AppendElement(foot); 1.2758 + } 1.2759 + if (aFoot) 1.2760 + *aFoot = foot; 1.2761 +} 1.2762 + 1.2763 +nsTableRowGroupFrame* 1.2764 +nsTableFrame::GetTHead() const 1.2765 +{ 1.2766 + nsIFrame* kidFrame = mFrames.FirstChild(); 1.2767 + while (kidFrame) { 1.2768 + if (kidFrame->StyleDisplay()->mDisplay == 1.2769 + NS_STYLE_DISPLAY_TABLE_HEADER_GROUP) { 1.2770 + return static_cast<nsTableRowGroupFrame*>(kidFrame); 1.2771 + } 1.2772 + 1.2773 + // Get the next sibling but skip it if it's also the next-in-flow, since 1.2774 + // a next-in-flow will not be part of the current table. 1.2775 + while (kidFrame) { 1.2776 + nsIFrame* nif = kidFrame->GetNextInFlow(); 1.2777 + kidFrame = kidFrame->GetNextSibling(); 1.2778 + if (kidFrame != nif) 1.2779 + break; 1.2780 + } 1.2781 + } 1.2782 + 1.2783 + return nullptr; 1.2784 +} 1.2785 + 1.2786 +nsTableRowGroupFrame* 1.2787 +nsTableFrame::GetTFoot() const 1.2788 +{ 1.2789 + nsIFrame* kidFrame = mFrames.FirstChild(); 1.2790 + while (kidFrame) { 1.2791 + if (kidFrame->StyleDisplay()->mDisplay == 1.2792 + NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP) { 1.2793 + return static_cast<nsTableRowGroupFrame*>(kidFrame); 1.2794 + } 1.2795 + 1.2796 + // Get the next sibling but skip it if it's also the next-in-flow, since 1.2797 + // a next-in-flow will not be part of the current table. 1.2798 + while (kidFrame) { 1.2799 + nsIFrame* nif = kidFrame->GetNextInFlow(); 1.2800 + kidFrame = kidFrame->GetNextSibling(); 1.2801 + if (kidFrame != nif) 1.2802 + break; 1.2803 + } 1.2804 + } 1.2805 + 1.2806 + return nullptr; 1.2807 +} 1.2808 + 1.2809 +static bool 1.2810 +IsRepeatable(nscoord aFrameHeight, nscoord aPageHeight) 1.2811 +{ 1.2812 + return aFrameHeight < (aPageHeight / 4); 1.2813 +} 1.2814 + 1.2815 +nsresult 1.2816 +nsTableFrame::SetupHeaderFooterChild(const nsTableReflowState& aReflowState, 1.2817 + nsTableRowGroupFrame* aFrame, 1.2818 + nscoord* aDesiredHeight) 1.2819 +{ 1.2820 + nsPresContext* presContext = PresContext(); 1.2821 + nscoord pageHeight = presContext->GetPageSize().height; 1.2822 + 1.2823 + // Reflow the child with unconstrainted height 1.2824 + nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState, 1.2825 + aFrame, 1.2826 + nsSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE), 1.2827 + -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); 1.2828 + InitChildReflowState(kidReflowState); 1.2829 + kidReflowState.mFlags.mIsTopOfPage = true; 1.2830 + nsHTMLReflowMetrics desiredSize(aReflowState.reflowState); 1.2831 + desiredSize.Width() = desiredSize.Height() = 0; 1.2832 + nsReflowStatus status; 1.2833 + nsresult rv = ReflowChild(aFrame, presContext, desiredSize, kidReflowState, 1.2834 + aReflowState.x, aReflowState.y, 0, status); 1.2835 + NS_ENSURE_SUCCESS(rv, rv); 1.2836 + // The child will be reflowed again "for real" so no need to place it now 1.2837 + 1.2838 + aFrame->SetRepeatable(IsRepeatable(desiredSize.Height(), pageHeight)); 1.2839 + *aDesiredHeight = desiredSize.Height(); 1.2840 + return NS_OK; 1.2841 +} 1.2842 + 1.2843 +void 1.2844 +nsTableFrame::PlaceRepeatedFooter(nsTableReflowState& aReflowState, 1.2845 + nsTableRowGroupFrame *aTfoot, 1.2846 + nscoord aFooterHeight) 1.2847 +{ 1.2848 + nsPresContext* presContext = PresContext(); 1.2849 + nsSize kidAvailSize(aReflowState.availSize); 1.2850 + kidAvailSize.height = aFooterHeight; 1.2851 + nsHTMLReflowState footerReflowState(presContext, 1.2852 + aReflowState.reflowState, 1.2853 + aTfoot, kidAvailSize, 1.2854 + -1, -1, 1.2855 + nsHTMLReflowState::CALLER_WILL_INIT); 1.2856 + InitChildReflowState(footerReflowState); 1.2857 + aReflowState.y += GetCellSpacingY(); 1.2858 + 1.2859 + nsRect origTfootRect = aTfoot->GetRect(); 1.2860 + nsRect origTfootVisualOverflow = aTfoot->GetVisualOverflowRect(); 1.2861 + 1.2862 + nsReflowStatus footerStatus; 1.2863 + nsHTMLReflowMetrics desiredSize(aReflowState.reflowState); 1.2864 + desiredSize.Width() = desiredSize.Height() = 0; 1.2865 + ReflowChild(aTfoot, presContext, desiredSize, footerReflowState, 1.2866 + aReflowState.x, aReflowState.y, 0, footerStatus); 1.2867 + PlaceChild(aReflowState, aTfoot, desiredSize, origTfootRect, 1.2868 + origTfootVisualOverflow); 1.2869 +} 1.2870 + 1.2871 +// Reflow the children based on the avail size and reason in aReflowState 1.2872 +// update aReflowMetrics a aStatus 1.2873 +nsresult 1.2874 +nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, 1.2875 + nsReflowStatus& aStatus, 1.2876 + nsIFrame*& aLastChildReflowed, 1.2877 + nsOverflowAreas& aOverflowAreas) 1.2878 +{ 1.2879 + aStatus = NS_FRAME_COMPLETE; 1.2880 + aLastChildReflowed = nullptr; 1.2881 + 1.2882 + nsIFrame* prevKidFrame = nullptr; 1.2883 + nsresult rv = NS_OK; 1.2884 + nscoord cellSpacingY = GetCellSpacingY(); 1.2885 + 1.2886 + nsPresContext* presContext = PresContext(); 1.2887 + // XXXldb Should we be checking constrained height instead? 1.2888 + // tables are not able to pull back children from its next inflow, so even 1.2889 + // under paginated contexts tables are should not paginate if they are inside 1.2890 + // column set 1.2891 + bool isPaginated = presContext->IsPaginated() && 1.2892 + NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height && 1.2893 + aReflowState.reflowState.mFlags.mTableIsSplittable; 1.2894 + 1.2895 + aOverflowAreas.Clear(); 1.2896 + 1.2897 + bool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() || 1.2898 + mBits.mResizedColumns || 1.2899 + IsGeometryDirty(); 1.2900 + 1.2901 + RowGroupArray rowGroups; 1.2902 + nsTableRowGroupFrame *thead, *tfoot; 1.2903 + OrderRowGroups(rowGroups, &thead, &tfoot); 1.2904 + bool pageBreak = false; 1.2905 + nscoord footerHeight = 0; 1.2906 + 1.2907 + // Determine the repeatablility of headers and footers, and also the desired 1.2908 + // height of any repeatable footer. 1.2909 + // The repeatability of headers on continued tables is handled 1.2910 + // when they are created in nsCSSFrameConstructor::CreateContinuingTableFrame. 1.2911 + // We handle the repeatability of footers again here because we need to 1.2912 + // determine the footer's height anyway. We could perhaps optimize by 1.2913 + // using the footer's prev-in-flow's height instead of reflowing it again, 1.2914 + // but there's no real need. 1.2915 + if (isPaginated) { 1.2916 + if (thead && !GetPrevInFlow()) { 1.2917 + nscoord desiredHeight; 1.2918 + rv = SetupHeaderFooterChild(aReflowState, thead, &desiredHeight); 1.2919 + if (NS_FAILED(rv)) 1.2920 + return rv; 1.2921 + } 1.2922 + if (tfoot) { 1.2923 + rv = SetupHeaderFooterChild(aReflowState, tfoot, &footerHeight); 1.2924 + if (NS_FAILED(rv)) 1.2925 + return rv; 1.2926 + } 1.2927 + } 1.2928 + // if the child is a tbody in paginated mode reduce the height by a repeated footer 1.2929 + bool allowRepeatedFooter = false; 1.2930 + for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) { 1.2931 + nsIFrame* kidFrame = rowGroups[childX]; 1.2932 + // Get the frame state bits 1.2933 + // See if we should only reflow the dirty child frames 1.2934 + if (reflowAllKids || 1.2935 + NS_SUBTREE_DIRTY(kidFrame) || 1.2936 + (aReflowState.reflowState.mFlags.mSpecialHeightReflow && 1.2937 + (isPaginated || (kidFrame->GetStateBits() & 1.2938 + NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) { 1.2939 + if (pageBreak) { 1.2940 + if (allowRepeatedFooter) { 1.2941 + PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); 1.2942 + } 1.2943 + else if (tfoot && tfoot->IsRepeatable()) { 1.2944 + tfoot->SetRepeatable(false); 1.2945 + } 1.2946 + PushChildren(rowGroups, childX); 1.2947 + aStatus = NS_FRAME_NOT_COMPLETE; 1.2948 + break; 1.2949 + } 1.2950 + 1.2951 + nsSize kidAvailSize(aReflowState.availSize); 1.2952 + allowRepeatedFooter = false; 1.2953 + if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.height)) { 1.2954 + nsTableRowGroupFrame* kidRG = 1.2955 + static_cast<nsTableRowGroupFrame*>(kidFrame); 1.2956 + if (kidRG != thead && kidRG != tfoot && tfoot && tfoot->IsRepeatable()) { 1.2957 + // the child is a tbody and there is a repeatable footer 1.2958 + NS_ASSERTION(tfoot == rowGroups[rowGroups.Length() - 1], "Missing footer!"); 1.2959 + if (footerHeight + cellSpacingY < kidAvailSize.height) { 1.2960 + allowRepeatedFooter = true; 1.2961 + kidAvailSize.height -= footerHeight + cellSpacingY; 1.2962 + } 1.2963 + } 1.2964 + } 1.2965 + 1.2966 + nsRect oldKidRect = kidFrame->GetRect(); 1.2967 + nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect(); 1.2968 + 1.2969 + nsHTMLReflowMetrics desiredSize(aReflowState.reflowState); 1.2970 + desiredSize.Width() = desiredSize.Height() = 0; 1.2971 + 1.2972 + // Reflow the child into the available space 1.2973 + nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState, 1.2974 + kidFrame, kidAvailSize, 1.2975 + -1, -1, 1.2976 + nsHTMLReflowState::CALLER_WILL_INIT); 1.2977 + InitChildReflowState(kidReflowState); 1.2978 + 1.2979 + // If this isn't the first row group, and the previous row group has a 1.2980 + // nonzero YMost, then we can't be at the top of the page. 1.2981 + // We ignore a repeated head row group in this check to avoid causing 1.2982 + // infinite loops in some circumstances - see bug 344883. 1.2983 + if (childX > ((thead && IsRepeatedFrame(thead)) ? 1u : 0u) && 1.2984 + (rowGroups[childX - 1]->GetRect().YMost() > 0)) { 1.2985 + kidReflowState.mFlags.mIsTopOfPage = false; 1.2986 + } 1.2987 + aReflowState.y += cellSpacingY; 1.2988 + if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { 1.2989 + aReflowState.availSize.height -= cellSpacingY; 1.2990 + } 1.2991 + // record the presence of a next in flow, it might get destroyed so we 1.2992 + // need to reorder the row group array 1.2993 + bool reorder = false; 1.2994 + if (kidFrame->GetNextInFlow()) 1.2995 + reorder = true; 1.2996 + 1.2997 + rv = ReflowChild(kidFrame, presContext, desiredSize, kidReflowState, 1.2998 + aReflowState.x, aReflowState.y, 0, aStatus); 1.2999 + 1.3000 + if (reorder) { 1.3001 + // reorder row groups the reflow may have changed the nextinflows 1.3002 + OrderRowGroups(rowGroups, &thead, &tfoot); 1.3003 + childX = rowGroups.IndexOf(kidFrame); 1.3004 + if (childX == RowGroupArray::NoIndex) { 1.3005 + // XXXbz can this happen? 1.3006 + childX = rowGroups.Length(); 1.3007 + } 1.3008 + } 1.3009 + if (isPaginated && !NS_FRAME_IS_FULLY_COMPLETE(aStatus) && 1.3010 + ShouldAvoidBreakInside(aReflowState.reflowState)) { 1.3011 + aStatus = NS_INLINE_LINE_BREAK_BEFORE(); 1.3012 + break; 1.3013 + } 1.3014 + // see if the rowgroup did not fit on this page might be pushed on 1.3015 + // the next page 1.3016 + if (isPaginated && 1.3017 + (NS_INLINE_IS_BREAK_BEFORE(aStatus) || 1.3018 + (NS_FRAME_IS_COMPLETE(aStatus) && 1.3019 + (NS_UNCONSTRAINEDSIZE != kidReflowState.AvailableHeight()) && 1.3020 + kidReflowState.AvailableHeight() < desiredSize.Height()))) { 1.3021 + if (ShouldAvoidBreakInside(aReflowState.reflowState)) { 1.3022 + aStatus = NS_INLINE_LINE_BREAK_BEFORE(); 1.3023 + break; 1.3024 + } 1.3025 + // if we are on top of the page place with dataloss 1.3026 + if (kidReflowState.mFlags.mIsTopOfPage) { 1.3027 + if (childX+1 < rowGroups.Length()) { 1.3028 + nsIFrame* nextRowGroupFrame = rowGroups[childX + 1]; 1.3029 + if (nextRowGroupFrame) { 1.3030 + PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect, 1.3031 + oldKidVisualOverflow); 1.3032 + if (allowRepeatedFooter) { 1.3033 + PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); 1.3034 + } 1.3035 + else if (tfoot && tfoot->IsRepeatable()) { 1.3036 + tfoot->SetRepeatable(false); 1.3037 + } 1.3038 + aStatus = NS_FRAME_NOT_COMPLETE; 1.3039 + PushChildren(rowGroups, childX + 1); 1.3040 + aLastChildReflowed = kidFrame; 1.3041 + break; 1.3042 + } 1.3043 + } 1.3044 + } 1.3045 + else { // we are not on top, push this rowgroup onto the next page 1.3046 + if (prevKidFrame) { // we had a rowgroup before so push this 1.3047 + if (allowRepeatedFooter) { 1.3048 + PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); 1.3049 + } 1.3050 + else if (tfoot && tfoot->IsRepeatable()) { 1.3051 + tfoot->SetRepeatable(false); 1.3052 + } 1.3053 + aStatus = NS_FRAME_NOT_COMPLETE; 1.3054 + PushChildren(rowGroups, childX); 1.3055 + aLastChildReflowed = prevKidFrame; 1.3056 + break; 1.3057 + } 1.3058 + else { // we can't push so lets make clear how much space we need 1.3059 + PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect, 1.3060 + oldKidVisualOverflow); 1.3061 + aLastChildReflowed = kidFrame; 1.3062 + if (allowRepeatedFooter) { 1.3063 + PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); 1.3064 + aLastChildReflowed = tfoot; 1.3065 + } 1.3066 + break; 1.3067 + } 1.3068 + } 1.3069 + } 1.3070 + 1.3071 + aLastChildReflowed = kidFrame; 1.3072 + 1.3073 + pageBreak = false; 1.3074 + // see if there is a page break after this row group or before the next one 1.3075 + if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated && 1.3076 + (NS_UNCONSTRAINEDSIZE != kidReflowState.AvailableHeight())) { 1.3077 + nsIFrame* nextKid = 1.3078 + (childX + 1 < rowGroups.Length()) ? rowGroups[childX + 1] : nullptr; 1.3079 + pageBreak = PageBreakAfter(kidFrame, nextKid); 1.3080 + } 1.3081 + 1.3082 + // Place the child 1.3083 + PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect, 1.3084 + oldKidVisualOverflow); 1.3085 + 1.3086 + // Remember where we just were in case we end up pushing children 1.3087 + prevKidFrame = kidFrame; 1.3088 + 1.3089 + // Special handling for incomplete children 1.3090 + if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { 1.3091 + nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow(); 1.3092 + if (!kidNextInFlow) { 1.3093 + // The child doesn't have a next-in-flow so create a continuing 1.3094 + // frame. This hooks the child into the flow 1.3095 + kidNextInFlow = presContext->PresShell()->FrameConstructor()-> 1.3096 + CreateContinuingFrame(presContext, kidFrame, this); 1.3097 + 1.3098 + // Insert the kid's new next-in-flow into our sibling list... 1.3099 + mFrames.InsertFrame(nullptr, kidFrame, kidNextInFlow); 1.3100 + // and in rowGroups after childX so that it will get pushed below. 1.3101 + rowGroups.InsertElementAt(childX + 1, 1.3102 + static_cast<nsTableRowGroupFrame*>(kidNextInFlow)); 1.3103 + } else if (kidNextInFlow == kidFrame->GetNextSibling()) { 1.3104 + // OrderRowGroups excludes NIFs in the child list from 'rowGroups' 1.3105 + // so we deal with that here to make sure they get pushed. 1.3106 + MOZ_ASSERT(!rowGroups.Contains(kidNextInFlow), 1.3107 + "OrderRowGroups must not put our NIF in 'rowGroups'"); 1.3108 + rowGroups.InsertElementAt(childX + 1, 1.3109 + static_cast<nsTableRowGroupFrame*>(kidNextInFlow)); 1.3110 + } 1.3111 + 1.3112 + // We've used up all of our available space so push the remaining 1.3113 + // children. 1.3114 + if (allowRepeatedFooter) { 1.3115 + PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); 1.3116 + } 1.3117 + else if (tfoot && tfoot->IsRepeatable()) { 1.3118 + tfoot->SetRepeatable(false); 1.3119 + } 1.3120 + 1.3121 + nsIFrame* nextSibling = kidFrame->GetNextSibling(); 1.3122 + if (nextSibling) { 1.3123 + PushChildren(rowGroups, childX + 1); 1.3124 + } 1.3125 + break; 1.3126 + } 1.3127 + } 1.3128 + else { // it isn't being reflowed 1.3129 + aReflowState.y += cellSpacingY; 1.3130 + nsRect kidRect = kidFrame->GetRect(); 1.3131 + if (kidRect.y != aReflowState.y) { 1.3132 + // invalidate the old position 1.3133 + kidFrame->InvalidateFrameSubtree(); 1.3134 + kidRect.y = aReflowState.y; 1.3135 + kidFrame->SetRect(kidRect); // move to the new position 1.3136 + RePositionViews(kidFrame); 1.3137 + // invalidate the new position 1.3138 + kidFrame->InvalidateFrameSubtree(); 1.3139 + } 1.3140 + aReflowState.y += kidRect.height; 1.3141 + 1.3142 + // If our height is constrained then update the available height. 1.3143 + if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { 1.3144 + aReflowState.availSize.height -= cellSpacingY + kidRect.height; 1.3145 + } 1.3146 + } 1.3147 + ConsiderChildOverflow(aOverflowAreas, kidFrame); 1.3148 + } 1.3149 + 1.3150 + // We've now propagated the column resizes and geometry changes to all 1.3151 + // the children. 1.3152 + mBits.mResizedColumns = false; 1.3153 + ClearGeometryDirty(); 1.3154 + 1.3155 + return rv; 1.3156 +} 1.3157 + 1.3158 +void 1.3159 +nsTableFrame::ReflowColGroups(nsRenderingContext *aRenderingContext) 1.3160 +{ 1.3161 + if (!GetPrevInFlow() && !HaveReflowedColGroups()) { 1.3162 + nsHTMLReflowMetrics kidMet(GetWritingMode()); 1.3163 + nsPresContext *presContext = PresContext(); 1.3164 + for (nsIFrame* kidFrame = mColGroups.FirstChild(); kidFrame; 1.3165 + kidFrame = kidFrame->GetNextSibling()) { 1.3166 + if (NS_SUBTREE_DIRTY(kidFrame)) { 1.3167 + // The column groups don't care about dimensions or reflow states. 1.3168 + nsHTMLReflowState kidReflowState(presContext, kidFrame, 1.3169 + aRenderingContext, nsSize(0,0)); 1.3170 + nsReflowStatus cgStatus; 1.3171 + ReflowChild(kidFrame, presContext, kidMet, kidReflowState, 0, 0, 0, 1.3172 + cgStatus); 1.3173 + FinishReflowChild(kidFrame, presContext, kidMet, nullptr, 0, 0, 0); 1.3174 + } 1.3175 + } 1.3176 + SetHaveReflowedColGroups(true); 1.3177 + } 1.3178 +} 1.3179 + 1.3180 +void 1.3181 +nsTableFrame::CalcDesiredHeight(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize) 1.3182 +{ 1.3183 + nsTableCellMap* cellMap = GetCellMap(); 1.3184 + if (!cellMap) { 1.3185 + NS_ASSERTION(false, "never ever call me until the cell map is built!"); 1.3186 + aDesiredSize.Height() = 0; 1.3187 + return; 1.3188 + } 1.3189 + nscoord cellSpacingY = GetCellSpacingY(); 1.3190 + nsMargin borderPadding = GetChildAreaOffset(&aReflowState); 1.3191 + 1.3192 + // get the natural height based on the last child's (row group) rect 1.3193 + RowGroupArray rowGroups; 1.3194 + OrderRowGroups(rowGroups); 1.3195 + if (rowGroups.IsEmpty()) { 1.3196 + // tables can be used as rectangular items without content 1.3197 + nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); 1.3198 + if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedHeight) && 1.3199 + (tableSpecifiedHeight > 0) && 1.3200 + eCompatibility_NavQuirks != PresContext()->CompatibilityMode()) { 1.3201 + // empty tables should not have a size in quirks mode 1.3202 + aDesiredSize.Height() = tableSpecifiedHeight; 1.3203 + } 1.3204 + else 1.3205 + aDesiredSize.Height() = 0; 1.3206 + return; 1.3207 + } 1.3208 + int32_t rowCount = cellMap->GetRowCount(); 1.3209 + int32_t colCount = cellMap->GetColCount(); 1.3210 + nscoord desiredHeight = borderPadding.top + borderPadding.bottom; 1.3211 + if (rowCount > 0 && colCount > 0) { 1.3212 + desiredHeight += cellSpacingY; 1.3213 + for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { 1.3214 + desiredHeight += rowGroups[rgX]->GetSize().height + cellSpacingY; 1.3215 + } 1.3216 + } 1.3217 + 1.3218 + // see if a specified table height requires dividing additional space to rows 1.3219 + if (!GetPrevInFlow()) { 1.3220 + nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); 1.3221 + if ((tableSpecifiedHeight > 0) && 1.3222 + (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) && 1.3223 + (tableSpecifiedHeight > desiredHeight)) { 1.3224 + // proportionately distribute the excess height to unconstrained rows in each 1.3225 + // unconstrained row group. 1.3226 + DistributeHeightToRows(aReflowState, tableSpecifiedHeight - desiredHeight); 1.3227 + // this might have changed the overflow area incorporate the childframe overflow area. 1.3228 + for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) { 1.3229 + ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame); 1.3230 + } 1.3231 + desiredHeight = tableSpecifiedHeight; 1.3232 + } 1.3233 + } 1.3234 + aDesiredSize.Height() = desiredHeight; 1.3235 +} 1.3236 + 1.3237 +static 1.3238 +void ResizeCells(nsTableFrame& aTableFrame) 1.3239 +{ 1.3240 + nsTableFrame::RowGroupArray rowGroups; 1.3241 + aTableFrame.OrderRowGroups(rowGroups); 1.3242 + nsHTMLReflowMetrics tableDesiredSize(aTableFrame.GetWritingMode()); // ??? 1.3243 + nsRect tableRect = aTableFrame.GetRect(); 1.3244 + tableDesiredSize.Width() = tableRect.width; 1.3245 + tableDesiredSize.Height() = tableRect.height; 1.3246 + tableDesiredSize.SetOverflowAreasToDesiredBounds(); 1.3247 + 1.3248 + for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { 1.3249 + nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; 1.3250 + 1.3251 + nsRect rowGroupRect = rgFrame->GetRect(); 1.3252 + nsHTMLReflowMetrics groupDesiredSize(tableDesiredSize.GetWritingMode()); 1.3253 + groupDesiredSize.Width() = rowGroupRect.width; 1.3254 + groupDesiredSize.Height() = rowGroupRect.height; 1.3255 + groupDesiredSize.SetOverflowAreasToDesiredBounds(); 1.3256 + 1.3257 + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); 1.3258 + while (rowFrame) { 1.3259 + rowFrame->DidResize(); 1.3260 + rgFrame->ConsiderChildOverflow(groupDesiredSize.mOverflowAreas, rowFrame); 1.3261 + rowFrame = rowFrame->GetNextRow(); 1.3262 + } 1.3263 + rgFrame->FinishAndStoreOverflow(&groupDesiredSize); 1.3264 + tableDesiredSize.mOverflowAreas.UnionWith(groupDesiredSize.mOverflowAreas + 1.3265 + rgFrame->GetPosition()); 1.3266 + } 1.3267 + aTableFrame.FinishAndStoreOverflow(&tableDesiredSize); 1.3268 +} 1.3269 + 1.3270 +void 1.3271 +nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, 1.3272 + nscoord aAmount) 1.3273 +{ 1.3274 + nscoord cellSpacingY = GetCellSpacingY(); 1.3275 + 1.3276 + nsMargin borderPadding = GetChildAreaOffset(&aReflowState); 1.3277 + 1.3278 + RowGroupArray rowGroups; 1.3279 + OrderRowGroups(rowGroups); 1.3280 + 1.3281 + nscoord amountUsed = 0; 1.3282 + // distribute space to each pct height row whose row group doesn't have a computed 1.3283 + // height, and base the pct on the table height. If the row group had a computed 1.3284 + // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights 1.3285 + nscoord pctBasis = aReflowState.ComputedHeight() - (GetCellSpacingY() * (GetRowCount() + 1)); 1.3286 + nscoord yOriginRG = borderPadding.top + GetCellSpacingY(); 1.3287 + nscoord yEndRG = yOriginRG; 1.3288 + uint32_t rgX; 1.3289 + for (rgX = 0; rgX < rowGroups.Length(); rgX++) { 1.3290 + nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; 1.3291 + nscoord amountUsedByRG = 0; 1.3292 + nscoord yOriginRow = 0; 1.3293 + nsRect rgRect = rgFrame->GetRect(); 1.3294 + if (!rgFrame->HasStyleHeight()) { 1.3295 + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); 1.3296 + while (rowFrame) { 1.3297 + nsRect rowRect = rowFrame->GetRect(); 1.3298 + if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) { 1.3299 + nscoord pctHeight = rowFrame->GetHeight(pctBasis); 1.3300 + nscoord amountForRow = std::min(aAmount - amountUsed, pctHeight - rowRect.height); 1.3301 + if (amountForRow > 0) { 1.3302 + nsRect oldRowRect = rowRect; 1.3303 + rowRect.height += amountForRow; 1.3304 + // XXXbz we don't need to change rowRect.y to be yOriginRow? 1.3305 + rowFrame->SetRect(rowRect); 1.3306 + yOriginRow += rowRect.height + cellSpacingY; 1.3307 + yEndRG += rowRect.height + cellSpacingY; 1.3308 + amountUsed += amountForRow; 1.3309 + amountUsedByRG += amountForRow; 1.3310 + //rowFrame->DidResize(); 1.3311 + nsTableFrame::RePositionViews(rowFrame); 1.3312 + 1.3313 + rgFrame->InvalidateFrameWithRect(oldRowRect); 1.3314 + rgFrame->InvalidateFrame(); 1.3315 + } 1.3316 + } 1.3317 + else { 1.3318 + if (amountUsed > 0 && yOriginRow != rowRect.y && 1.3319 + !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { 1.3320 + rowFrame->InvalidateFrameSubtree(); 1.3321 + rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow)); 1.3322 + nsTableFrame::RePositionViews(rowFrame); 1.3323 + rowFrame->InvalidateFrameSubtree(); 1.3324 + } 1.3325 + yOriginRow += rowRect.height + cellSpacingY; 1.3326 + yEndRG += rowRect.height + cellSpacingY; 1.3327 + } 1.3328 + rowFrame = rowFrame->GetNextRow(); 1.3329 + } 1.3330 + if (amountUsed > 0) { 1.3331 + if (rgRect.y != yOriginRG) { 1.3332 + rgFrame->InvalidateFrameSubtree(); 1.3333 + } 1.3334 + 1.3335 + nsRect origRgRect = rgRect; 1.3336 + nsRect origRgVisualOverflow = rgFrame->GetVisualOverflowRect(); 1.3337 + 1.3338 + rgRect.y = yOriginRG; 1.3339 + rgRect.height += amountUsedByRG; 1.3340 + 1.3341 + rgFrame->SetRect(rgRect); 1.3342 + 1.3343 + nsTableFrame::InvalidateTableFrame(rgFrame, origRgRect, 1.3344 + origRgVisualOverflow, false); 1.3345 + } 1.3346 + } 1.3347 + else if (amountUsed > 0 && yOriginRG != rgRect.y) { 1.3348 + rgFrame->InvalidateFrameSubtree(); 1.3349 + rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG)); 1.3350 + // Make sure child views are properly positioned 1.3351 + nsTableFrame::RePositionViews(rgFrame); 1.3352 + rgFrame->InvalidateFrameSubtree(); 1.3353 + } 1.3354 + yOriginRG = yEndRG; 1.3355 + } 1.3356 + 1.3357 + if (amountUsed >= aAmount) { 1.3358 + ResizeCells(*this); 1.3359 + return; 1.3360 + } 1.3361 + 1.3362 + // get the first row without a style height where its row group has an 1.3363 + // unconstrained height 1.3364 + nsTableRowGroupFrame* firstUnStyledRG = nullptr; 1.3365 + nsTableRowFrame* firstUnStyledRow = nullptr; 1.3366 + for (rgX = 0; rgX < rowGroups.Length() && !firstUnStyledRG; rgX++) { 1.3367 + nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; 1.3368 + if (!rgFrame->HasStyleHeight()) { 1.3369 + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); 1.3370 + while (rowFrame) { 1.3371 + if (!rowFrame->HasStyleHeight()) { 1.3372 + firstUnStyledRG = rgFrame; 1.3373 + firstUnStyledRow = rowFrame; 1.3374 + break; 1.3375 + } 1.3376 + rowFrame = rowFrame->GetNextRow(); 1.3377 + } 1.3378 + } 1.3379 + } 1.3380 + 1.3381 + nsTableRowFrame* lastEligibleRow = nullptr; 1.3382 + // Accumulate the correct divisor. This will be the total total height of all 1.3383 + // unstyled rows inside unstyled row groups, unless there are none, in which 1.3384 + // case, it will be number of all rows. If the unstyled rows don't have a 1.3385 + // height, divide the space equally among them. 1.3386 + nscoord divisor = 0; 1.3387 + int32_t eligibleRows = 0; 1.3388 + bool expandEmptyRows = false; 1.3389 + 1.3390 + if (!firstUnStyledRow) { 1.3391 + // there is no unstyled row 1.3392 + divisor = GetRowCount(); 1.3393 + } 1.3394 + else { 1.3395 + for (rgX = 0; rgX < rowGroups.Length(); rgX++) { 1.3396 + nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; 1.3397 + if (!firstUnStyledRG || !rgFrame->HasStyleHeight()) { 1.3398 + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); 1.3399 + while (rowFrame) { 1.3400 + if (!firstUnStyledRG || !rowFrame->HasStyleHeight()) { 1.3401 + NS_ASSERTION(rowFrame->GetSize().height >= 0, 1.3402 + "negative row frame height"); 1.3403 + divisor += rowFrame->GetSize().height; 1.3404 + eligibleRows++; 1.3405 + lastEligibleRow = rowFrame; 1.3406 + } 1.3407 + rowFrame = rowFrame->GetNextRow(); 1.3408 + } 1.3409 + } 1.3410 + } 1.3411 + if (divisor <= 0) { 1.3412 + if (eligibleRows > 0) { 1.3413 + expandEmptyRows = true; 1.3414 + } 1.3415 + else { 1.3416 + NS_ERROR("invalid divisor"); 1.3417 + return; 1.3418 + } 1.3419 + } 1.3420 + } 1.3421 + // allocate the extra height to the unstyled row groups and rows 1.3422 + nscoord heightToDistribute = aAmount - amountUsed; 1.3423 + yOriginRG = borderPadding.top + cellSpacingY; 1.3424 + yEndRG = yOriginRG; 1.3425 + for (rgX = 0; rgX < rowGroups.Length(); rgX++) { 1.3426 + nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; 1.3427 + nscoord amountUsedByRG = 0; 1.3428 + nscoord yOriginRow = 0; 1.3429 + nsRect rgRect = rgFrame->GetRect(); 1.3430 + nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect(); 1.3431 + // see if there is an eligible row group or we distribute to all rows 1.3432 + if (!firstUnStyledRG || !rgFrame->HasStyleHeight() || !eligibleRows) { 1.3433 + nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); 1.3434 + while (rowFrame) { 1.3435 + nsRect rowRect = rowFrame->GetRect(); 1.3436 + nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect(); 1.3437 + // see if there is an eligible row or we distribute to all rows 1.3438 + if (!firstUnStyledRow || !rowFrame->HasStyleHeight() || !eligibleRows) { 1.3439 + float ratio; 1.3440 + if (eligibleRows) { 1.3441 + if (!expandEmptyRows) { 1.3442 + // The amount of additional space each row gets is proportional to 1.3443 + // its height 1.3444 + ratio = float(rowRect.height) / float(divisor); 1.3445 + } else { 1.3446 + // empty rows get all the same additional space 1.3447 + ratio = 1.0f / float(eligibleRows); 1.3448 + } 1.3449 + } 1.3450 + else { 1.3451 + // all rows get the same additional space 1.3452 + ratio = 1.0f / float(divisor); 1.3453 + } 1.3454 + // give rows their additional space, except for the last row which 1.3455 + // gets the remainder 1.3456 + nscoord amountForRow = (rowFrame == lastEligibleRow) 1.3457 + ? aAmount - amountUsed : NSToCoordRound(((float)(heightToDistribute)) * ratio); 1.3458 + amountForRow = std::min(amountForRow, aAmount - amountUsed); 1.3459 + 1.3460 + if (yOriginRow != rowRect.y) { 1.3461 + rowFrame->InvalidateFrameSubtree(); 1.3462 + } 1.3463 + 1.3464 + // update the row height 1.3465 + nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width, 1.3466 + rowRect.height + amountForRow); 1.3467 + rowFrame->SetRect(newRowRect); 1.3468 + 1.3469 + yOriginRow += newRowRect.height + cellSpacingY; 1.3470 + yEndRG += newRowRect.height + cellSpacingY; 1.3471 + 1.3472 + amountUsed += amountForRow; 1.3473 + amountUsedByRG += amountForRow; 1.3474 + NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation"); 1.3475 + //rowFrame->DidResize(); 1.3476 + nsTableFrame::RePositionViews(rowFrame); 1.3477 + 1.3478 + nsTableFrame::InvalidateTableFrame(rowFrame, rowRect, rowVisualOverflow, 1.3479 + false); 1.3480 + } 1.3481 + else { 1.3482 + if (amountUsed > 0 && yOriginRow != rowRect.y) { 1.3483 + rowFrame->InvalidateFrameSubtree(); 1.3484 + rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow)); 1.3485 + nsTableFrame::RePositionViews(rowFrame); 1.3486 + rowFrame->InvalidateFrameSubtree(); 1.3487 + } 1.3488 + yOriginRow += rowRect.height + cellSpacingY; 1.3489 + yEndRG += rowRect.height + cellSpacingY; 1.3490 + } 1.3491 + rowFrame = rowFrame->GetNextRow(); 1.3492 + } 1.3493 + if (amountUsed > 0) { 1.3494 + if (rgRect.y != yOriginRG) { 1.3495 + rgFrame->InvalidateFrameSubtree(); 1.3496 + } 1.3497 + 1.3498 + rgFrame->SetRect(nsRect(rgRect.x, yOriginRG, rgRect.width, 1.3499 + rgRect.height + amountUsedByRG)); 1.3500 + 1.3501 + nsTableFrame::InvalidateTableFrame(rgFrame, rgRect, rgVisualOverflow, 1.3502 + false); 1.3503 + } 1.3504 + // Make sure child views are properly positioned 1.3505 + } 1.3506 + else if (amountUsed > 0 && yOriginRG != rgRect.y) { 1.3507 + rgFrame->InvalidateFrameSubtree(); 1.3508 + rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG)); 1.3509 + // Make sure child views are properly positioned 1.3510 + nsTableFrame::RePositionViews(rgFrame); 1.3511 + rgFrame->InvalidateFrameSubtree(); 1.3512 + } 1.3513 + yOriginRG = yEndRG; 1.3514 + } 1.3515 + 1.3516 + ResizeCells(*this); 1.3517 +} 1.3518 + 1.3519 +int32_t nsTableFrame::GetColumnWidth(int32_t aColIndex) 1.3520 +{ 1.3521 + nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow()); 1.3522 + if (this == firstInFlow) { 1.3523 + nsTableColFrame* colFrame = GetColFrame(aColIndex); 1.3524 + return colFrame ? colFrame->GetFinalWidth() : 0; 1.3525 + } 1.3526 + return firstInFlow->GetColumnWidth(aColIndex); 1.3527 +} 1.3528 + 1.3529 +// XXX: could cache this. But be sure to check style changes if you do! 1.3530 +nscoord nsTableFrame::GetCellSpacingX() 1.3531 +{ 1.3532 + if (IsBorderCollapse()) 1.3533 + return 0; 1.3534 + 1.3535 + return StyleTableBorder()->mBorderSpacingX; 1.3536 +} 1.3537 + 1.3538 +// XXX: could cache this. But be sure to check style changes if you do! 1.3539 +nscoord nsTableFrame::GetCellSpacingY() 1.3540 +{ 1.3541 + if (IsBorderCollapse()) 1.3542 + return 0; 1.3543 + 1.3544 + return StyleTableBorder()->mBorderSpacingY; 1.3545 +} 1.3546 + 1.3547 + 1.3548 +/* virtual */ nscoord 1.3549 +nsTableFrame::GetBaseline() const 1.3550 +{ 1.3551 + nscoord ascent = 0; 1.3552 + RowGroupArray orderedRowGroups; 1.3553 + OrderRowGroups(orderedRowGroups); 1.3554 + nsTableRowFrame* firstRow = nullptr; 1.3555 + for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { 1.3556 + nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex]; 1.3557 + if (rgFrame->GetRowCount()) { 1.3558 + firstRow = rgFrame->GetFirstRow(); 1.3559 + ascent = rgFrame->GetRect().y + firstRow->GetRect().y + firstRow->GetRowBaseline(); 1.3560 + break; 1.3561 + } 1.3562 + } 1.3563 + if (!firstRow) 1.3564 + ascent = GetRect().height; 1.3565 + return ascent; 1.3566 +} 1.3567 +/* ----- global methods ----- */ 1.3568 + 1.3569 +nsIFrame* 1.3570 +NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.3571 +{ 1.3572 + return new (aPresShell) nsTableFrame(aContext); 1.3573 +} 1.3574 + 1.3575 +NS_IMPL_FRAMEARENA_HELPERS(nsTableFrame) 1.3576 + 1.3577 +nsTableFrame* 1.3578 +nsTableFrame::GetTableFrame(nsIFrame* aFrame) 1.3579 +{ 1.3580 + for (nsIFrame* ancestor = aFrame->GetParent(); ancestor; 1.3581 + ancestor = ancestor->GetParent()) { 1.3582 + if (nsGkAtoms::tableFrame == ancestor->GetType()) { 1.3583 + return static_cast<nsTableFrame*>(ancestor); 1.3584 + } 1.3585 + } 1.3586 + NS_RUNTIMEABORT("unable to find table parent"); 1.3587 + return nullptr; 1.3588 +} 1.3589 + 1.3590 +nsTableFrame* 1.3591 +nsTableFrame::GetTableFramePassingThrough(nsIFrame* aMustPassThrough, 1.3592 + nsIFrame* aFrame) 1.3593 +{ 1.3594 + MOZ_ASSERT(aMustPassThrough == aFrame || 1.3595 + nsLayoutUtils::IsProperAncestorFrame(aMustPassThrough, aFrame), 1.3596 + "aMustPassThrough should be an ancestor"); 1.3597 + 1.3598 + // Retrieve the table frame, and ensure that we hit aMustPassThrough on the 1.3599 + // way. If we don't, just return null. 1.3600 + bool hitPassThroughFrame = false; 1.3601 + nsTableFrame* tableFrame = nullptr; 1.3602 + for (nsIFrame* ancestor = aFrame; ancestor; ancestor = ancestor->GetParent()) { 1.3603 + if (ancestor == aMustPassThrough) { 1.3604 + hitPassThroughFrame = true; 1.3605 + } 1.3606 + if (nsGkAtoms::tableFrame == ancestor->GetType()) { 1.3607 + tableFrame = static_cast<nsTableFrame*>(ancestor); 1.3608 + break; 1.3609 + } 1.3610 + } 1.3611 + 1.3612 + MOZ_ASSERT(tableFrame, "Should have a table frame here"); 1.3613 + return hitPassThroughFrame ? tableFrame : nullptr; 1.3614 +} 1.3615 + 1.3616 +bool 1.3617 +nsTableFrame::IsAutoHeight() 1.3618 +{ 1.3619 + const nsStyleCoord &height = StylePosition()->mHeight; 1.3620 + // Don't consider calc() here like this quirk for percent. 1.3621 + return height.GetUnit() == eStyleUnit_Auto || 1.3622 + (height.GetUnit() == eStyleUnit_Percent && 1.3623 + height.GetPercentValue() <= 0.0f); 1.3624 +} 1.3625 + 1.3626 +nscoord 1.3627 +nsTableFrame::CalcBorderBoxHeight(const nsHTMLReflowState& aState) 1.3628 +{ 1.3629 + nscoord height = aState.ComputedHeight(); 1.3630 + if (NS_AUTOHEIGHT != height) { 1.3631 + nsMargin borderPadding = GetChildAreaOffset(&aState); 1.3632 + height += borderPadding.top + borderPadding.bottom; 1.3633 + } 1.3634 + height = std::max(0, height); 1.3635 + 1.3636 + return height; 1.3637 +} 1.3638 + 1.3639 +bool 1.3640 +nsTableFrame::IsAutoLayout() 1.3641 +{ 1.3642 + if (StyleTable()->mLayoutStrategy == NS_STYLE_TABLE_LAYOUT_AUTO) 1.3643 + return true; 1.3644 + // a fixed-layout inline-table must have a width 1.3645 + // and tables with 'width: -moz-max-content' must be auto-layout 1.3646 + // (at least as long as FixedTableLayoutStrategy::GetPrefWidth returns 1.3647 + // nscoord_MAX) 1.3648 + const nsStyleCoord &width = StylePosition()->mWidth; 1.3649 + return (width.GetUnit() == eStyleUnit_Auto) || 1.3650 + (width.GetUnit() == eStyleUnit_Enumerated && 1.3651 + width.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT); 1.3652 +} 1.3653 + 1.3654 +#ifdef DEBUG_FRAME_DUMP 1.3655 +nsresult 1.3656 +nsTableFrame::GetFrameName(nsAString& aResult) const 1.3657 +{ 1.3658 + return MakeFrameName(NS_LITERAL_STRING("Table"), aResult); 1.3659 +} 1.3660 +#endif 1.3661 + 1.3662 +// Find the closet sibling before aPriorChildFrame (including aPriorChildFrame) that 1.3663 +// is of type aChildType 1.3664 +nsIFrame* 1.3665 +nsTableFrame::GetFrameAtOrBefore(nsIFrame* aParentFrame, 1.3666 + nsIFrame* aPriorChildFrame, 1.3667 + nsIAtom* aChildType) 1.3668 +{ 1.3669 + nsIFrame* result = nullptr; 1.3670 + if (!aPriorChildFrame) { 1.3671 + return result; 1.3672 + } 1.3673 + if (aChildType == aPriorChildFrame->GetType()) { 1.3674 + return aPriorChildFrame; 1.3675 + } 1.3676 + 1.3677 + // aPriorChildFrame is not of type aChildType, so we need start from 1.3678 + // the beginnng and find the closest one 1.3679 + nsIFrame* lastMatchingFrame = nullptr; 1.3680 + nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild(); 1.3681 + while (childFrame && (childFrame != aPriorChildFrame)) { 1.3682 + if (aChildType == childFrame->GetType()) { 1.3683 + lastMatchingFrame = childFrame; 1.3684 + } 1.3685 + childFrame = childFrame->GetNextSibling(); 1.3686 + } 1.3687 + return lastMatchingFrame; 1.3688 +} 1.3689 + 1.3690 +#ifdef DEBUG 1.3691 +void 1.3692 +nsTableFrame::DumpRowGroup(nsIFrame* aKidFrame) 1.3693 +{ 1.3694 + if (!aKidFrame) 1.3695 + return; 1.3696 + 1.3697 + nsIFrame* cFrame = aKidFrame->GetFirstPrincipalChild(); 1.3698 + while (cFrame) { 1.3699 + nsTableRowFrame *rowFrame = do_QueryFrame(cFrame); 1.3700 + if (rowFrame) { 1.3701 + printf("row(%d)=%p ", rowFrame->GetRowIndex(), 1.3702 + static_cast<void*>(rowFrame)); 1.3703 + nsIFrame* childFrame = cFrame->GetFirstPrincipalChild(); 1.3704 + while (childFrame) { 1.3705 + nsTableCellFrame *cellFrame = do_QueryFrame(childFrame); 1.3706 + if (cellFrame) { 1.3707 + int32_t colIndex; 1.3708 + cellFrame->GetColIndex(colIndex); 1.3709 + printf("cell(%d)=%p ", colIndex, static_cast<void*>(childFrame)); 1.3710 + } 1.3711 + childFrame = childFrame->GetNextSibling(); 1.3712 + } 1.3713 + printf("\n"); 1.3714 + } 1.3715 + else { 1.3716 + DumpRowGroup(rowFrame); 1.3717 + } 1.3718 + cFrame = cFrame->GetNextSibling(); 1.3719 + } 1.3720 +} 1.3721 + 1.3722 +void 1.3723 +nsTableFrame::Dump(bool aDumpRows, 1.3724 + bool aDumpCols, 1.3725 + bool aDumpCellMap) 1.3726 +{ 1.3727 + printf("***START TABLE DUMP*** \n"); 1.3728 + // dump the columns widths array 1.3729 + printf("mColWidths="); 1.3730 + int32_t numCols = GetColCount(); 1.3731 + int32_t colX; 1.3732 + for (colX = 0; colX < numCols; colX++) { 1.3733 + printf("%d ", GetColumnWidth(colX)); 1.3734 + } 1.3735 + printf("\n"); 1.3736 + 1.3737 + if (aDumpRows) { 1.3738 + nsIFrame* kidFrame = mFrames.FirstChild(); 1.3739 + while (kidFrame) { 1.3740 + DumpRowGroup(kidFrame); 1.3741 + kidFrame = kidFrame->GetNextSibling(); 1.3742 + } 1.3743 + } 1.3744 + 1.3745 + if (aDumpCols) { 1.3746 + // output col frame cache 1.3747 + printf("\n col frame cache ->"); 1.3748 + for (colX = 0; colX < numCols; colX++) { 1.3749 + nsTableColFrame* colFrame = mColFrames.ElementAt(colX); 1.3750 + if (0 == (colX % 8)) { 1.3751 + printf("\n"); 1.3752 + } 1.3753 + printf ("%d=%p ", colX, static_cast<void*>(colFrame)); 1.3754 + nsTableColType colType = colFrame->GetColType(); 1.3755 + switch (colType) { 1.3756 + case eColContent: 1.3757 + printf(" content "); 1.3758 + break; 1.3759 + case eColAnonymousCol: 1.3760 + printf(" anonymous-column "); 1.3761 + break; 1.3762 + case eColAnonymousColGroup: 1.3763 + printf(" anonymous-colgroup "); 1.3764 + break; 1.3765 + case eColAnonymousCell: 1.3766 + printf(" anonymous-cell "); 1.3767 + break; 1.3768 + } 1.3769 + } 1.3770 + printf("\n colgroups->"); 1.3771 + for (nsIFrame* childFrame = mColGroups.FirstChild(); childFrame; 1.3772 + childFrame = childFrame->GetNextSibling()) { 1.3773 + if (nsGkAtoms::tableColGroupFrame == childFrame->GetType()) { 1.3774 + nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame *)childFrame; 1.3775 + colGroupFrame->Dump(1); 1.3776 + } 1.3777 + } 1.3778 + for (colX = 0; colX < numCols; colX++) { 1.3779 + printf("\n"); 1.3780 + nsTableColFrame* colFrame = GetColFrame(colX); 1.3781 + colFrame->Dump(1); 1.3782 + } 1.3783 + } 1.3784 + if (aDumpCellMap) { 1.3785 + nsTableCellMap* cellMap = GetCellMap(); 1.3786 + cellMap->Dump(); 1.3787 + } 1.3788 + printf(" ***END TABLE DUMP*** \n"); 1.3789 +} 1.3790 +#endif 1.3791 + 1.3792 +// nsTableIterator 1.3793 +nsTableIterator::nsTableIterator(nsIFrame& aSource) 1.3794 +{ 1.3795 + nsIFrame* firstChild = aSource.GetFirstPrincipalChild(); 1.3796 + Init(firstChild); 1.3797 +} 1.3798 + 1.3799 +nsTableIterator::nsTableIterator(nsFrameList& aSource) 1.3800 +{ 1.3801 + nsIFrame* firstChild = aSource.FirstChild(); 1.3802 + Init(firstChild); 1.3803 +} 1.3804 + 1.3805 +void nsTableIterator::Init(nsIFrame* aFirstChild) 1.3806 +{ 1.3807 + mFirstListChild = aFirstChild; 1.3808 + mFirstChild = aFirstChild; 1.3809 + mCurrentChild = nullptr; 1.3810 + mLeftToRight = true; 1.3811 + mCount = -1; 1.3812 + 1.3813 + if (!mFirstChild) { 1.3814 + return; 1.3815 + } 1.3816 + 1.3817 + nsTableFrame* table = nsTableFrame::GetTableFrame(mFirstChild); 1.3818 + mLeftToRight = (NS_STYLE_DIRECTION_LTR == 1.3819 + table->StyleVisibility()->mDirection); 1.3820 + 1.3821 + if (!mLeftToRight) { 1.3822 + mCount = 0; 1.3823 + nsIFrame* nextChild = mFirstChild->GetNextSibling(); 1.3824 + while (nullptr != nextChild) { 1.3825 + mCount++; 1.3826 + mFirstChild = nextChild; 1.3827 + nextChild = nextChild->GetNextSibling(); 1.3828 + } 1.3829 + } 1.3830 +} 1.3831 + 1.3832 +nsIFrame* nsTableIterator::First() 1.3833 +{ 1.3834 + mCurrentChild = mFirstChild; 1.3835 + return mCurrentChild; 1.3836 +} 1.3837 + 1.3838 +nsIFrame* nsTableIterator::Next() 1.3839 +{ 1.3840 + if (!mCurrentChild) { 1.3841 + return nullptr; 1.3842 + } 1.3843 + 1.3844 + if (mLeftToRight) { 1.3845 + mCurrentChild = mCurrentChild->GetNextSibling(); 1.3846 + return mCurrentChild; 1.3847 + } 1.3848 + else { 1.3849 + nsIFrame* targetChild = mCurrentChild; 1.3850 + mCurrentChild = nullptr; 1.3851 + nsIFrame* child = mFirstListChild; 1.3852 + while (child && (child != targetChild)) { 1.3853 + mCurrentChild = child; 1.3854 + child = child->GetNextSibling(); 1.3855 + } 1.3856 + return mCurrentChild; 1.3857 + } 1.3858 +} 1.3859 + 1.3860 +bool nsTableIterator::IsLeftToRight() 1.3861 +{ 1.3862 + return mLeftToRight; 1.3863 +} 1.3864 + 1.3865 +int32_t nsTableIterator::Count() 1.3866 +{ 1.3867 + if (-1 == mCount) { 1.3868 + mCount = 0; 1.3869 + nsIFrame* child = mFirstListChild; 1.3870 + while (nullptr != child) { 1.3871 + mCount++; 1.3872 + child = child->GetNextSibling(); 1.3873 + } 1.3874 + } 1.3875 + return mCount; 1.3876 +} 1.3877 + 1.3878 +bool 1.3879 +nsTableFrame::ColumnHasCellSpacingBefore(int32_t aColIndex) const 1.3880 +{ 1.3881 + // Since fixed-layout tables should not have their column sizes change 1.3882 + // as they load, we assume that all columns are significant. 1.3883 + if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Fixed) 1.3884 + return true; 1.3885 + // the first column is always significant 1.3886 + if (aColIndex == 0) 1.3887 + return true; 1.3888 + nsTableCellMap* cellMap = GetCellMap(); 1.3889 + if (!cellMap) 1.3890 + return false; 1.3891 + return cellMap->GetNumCellsOriginatingInCol(aColIndex) > 0; 1.3892 +} 1.3893 + 1.3894 +/******************************************************************************** 1.3895 + * Collapsing Borders 1.3896 + * 1.3897 + * The CSS spec says to resolve border conflicts in this order: 1.3898 + * 1) any border with the style HIDDEN wins 1.3899 + * 2) the widest border with a style that is not NONE wins 1.3900 + * 3) the border styles are ranked in this order, highest to lowest precedence: 1.3901 + * double, solid, dashed, dotted, ridge, outset, groove, inset 1.3902 + * 4) borders that are of equal width and style (differ only in color) have this precedence: 1.3903 + * cell, row, rowgroup, col, colgroup, table 1.3904 + * 5) if all border styles are NONE, then that's the computed border style. 1.3905 + *******************************************************************************/ 1.3906 + 1.3907 +#ifdef DEBUG 1.3908 +#define VerifyNonNegativeDamageRect(r) \ 1.3909 + NS_ASSERTION((r).x >= 0, "negative col index"); \ 1.3910 + NS_ASSERTION((r).y >= 0, "negative row index"); \ 1.3911 + NS_ASSERTION((r).width >= 0, "negative horizontal damage"); \ 1.3912 + NS_ASSERTION((r).height >= 0, "negative vertical damage"); 1.3913 +#define VerifyDamageRect(r) \ 1.3914 + VerifyNonNegativeDamageRect(r); \ 1.3915 + NS_ASSERTION((r).XMost() <= GetColCount(), \ 1.3916 + "horizontal damage extends outside table"); \ 1.3917 + NS_ASSERTION((r).YMost() <= GetRowCount(), \ 1.3918 + "vertical damage extends outside table"); 1.3919 +#endif 1.3920 + 1.3921 +void 1.3922 +nsTableFrame::AddBCDamageArea(const nsIntRect& aValue) 1.3923 +{ 1.3924 + NS_ASSERTION(IsBorderCollapse(), "invalid AddBCDamageArea call"); 1.3925 +#ifdef DEBUG 1.3926 + VerifyDamageRect(aValue); 1.3927 +#endif 1.3928 + 1.3929 + SetNeedToCalcBCBorders(true); 1.3930 + // Get the property 1.3931 + BCPropertyData* value = GetBCProperty(true); 1.3932 + if (value) { 1.3933 +#ifdef DEBUG 1.3934 + VerifyNonNegativeDamageRect(value->mDamageArea); 1.3935 +#endif 1.3936 + // Clamp the old damage area to the current table area in case it shrunk. 1.3937 + int32_t cols = GetColCount(); 1.3938 + if (value->mDamageArea.XMost() > cols) { 1.3939 + if (value->mDamageArea.x > cols) { 1.3940 + value->mDamageArea.x = cols; 1.3941 + value->mDamageArea.width = 0; 1.3942 + } 1.3943 + else { 1.3944 + value->mDamageArea.width = cols - value->mDamageArea.x; 1.3945 + } 1.3946 + } 1.3947 + int32_t rows = GetRowCount(); 1.3948 + if (value->mDamageArea.YMost() > rows) { 1.3949 + if (value->mDamageArea.y > rows) { 1.3950 + value->mDamageArea.y = rows; 1.3951 + value->mDamageArea.height = 0; 1.3952 + } 1.3953 + else { 1.3954 + value->mDamageArea.height = rows - value->mDamageArea.y; 1.3955 + } 1.3956 + } 1.3957 + 1.3958 + // Construct a union of the new and old damage areas. 1.3959 + value->mDamageArea.UnionRect(value->mDamageArea, aValue); 1.3960 + } 1.3961 +} 1.3962 + 1.3963 + 1.3964 +void 1.3965 +nsTableFrame::SetFullBCDamageArea() 1.3966 +{ 1.3967 + NS_ASSERTION(IsBorderCollapse(), "invalid SetFullBCDamageArea call"); 1.3968 + 1.3969 + SetNeedToCalcBCBorders(true); 1.3970 + 1.3971 + BCPropertyData* value = GetBCProperty(true); 1.3972 + if (value) { 1.3973 + value->mDamageArea = nsIntRect(0, 0, GetColCount(), GetRowCount()); 1.3974 + } 1.3975 +} 1.3976 + 1.3977 + 1.3978 +/* BCCellBorder represents a border segment which can be either a horizontal 1.3979 + * or a vertical segment. For each segment we need to know the color, width, 1.3980 + * style, who owns it and how long it is in cellmap coordinates. 1.3981 + * Ownership of these segments is important to calculate which corners should 1.3982 + * be bevelled. This structure has dual use, its used first to compute the 1.3983 + * dominant border for horizontal and vertical segments and to store the 1.3984 + * preliminary computed border results in the BCCellBorders structure. 1.3985 + * This temporary storage is not symmetric with respect to horizontal and 1.3986 + * vertical border segments, its always column oriented. For each column in 1.3987 + * the cellmap there is a temporary stored vertical and horizontal segment. 1.3988 + * XXX_Bernd this asymmetry is the root of those rowspan bc border errors 1.3989 + */ 1.3990 +struct BCCellBorder 1.3991 +{ 1.3992 + BCCellBorder() { Reset(0, 1); } 1.3993 + void Reset(uint32_t aRowIndex, uint32_t aRowSpan); 1.3994 + nscolor color; // border segment color 1.3995 + BCPixelSize width; // border segment width in pixel coordinates !! 1.3996 + uint8_t style; // border segment style, possible values are defined 1.3997 + // in nsStyleConsts.h as NS_STYLE_BORDER_STYLE_* 1.3998 + BCBorderOwner owner; // border segment owner, possible values are defined 1.3999 + // in celldata.h. In the cellmap for each border 1.4000 + // segment we store the owner and later when 1.4001 + // painting we know the owner and can retrieve the 1.4002 + // style info from the corresponding frame 1.4003 + int32_t rowIndex; // rowIndex of temporary stored horizontal border 1.4004 + // segments relative to the table 1.4005 + int32_t rowSpan; // row span of temporary stored horizontal border 1.4006 + // segments 1.4007 +}; 1.4008 + 1.4009 +void 1.4010 +BCCellBorder::Reset(uint32_t aRowIndex, 1.4011 + uint32_t aRowSpan) 1.4012 +{ 1.4013 + style = NS_STYLE_BORDER_STYLE_NONE; 1.4014 + color = 0; 1.4015 + width = 0; 1.4016 + owner = eTableOwner; 1.4017 + rowIndex = aRowIndex; 1.4018 + rowSpan = aRowSpan; 1.4019 +} 1.4020 + 1.4021 +class BCMapCellIterator; 1.4022 + 1.4023 +/***************************************************************** 1.4024 + * BCMapCellInfo 1.4025 + * This structure stores information about the cellmap and all involved 1.4026 + * table related frames that are used during the computation of winning borders 1.4027 + * in CalcBCBorders so that they do need to be looked up again and again when 1.4028 + * iterating over the cells. 1.4029 + ****************************************************************/ 1.4030 +struct BCMapCellInfo 1.4031 +{ 1.4032 + BCMapCellInfo(nsTableFrame* aTableFrame); 1.4033 + void ResetCellInfo(); 1.4034 + void SetInfo(nsTableRowFrame* aNewRow, 1.4035 + int32_t aColIndex, 1.4036 + BCCellData* aCellData, 1.4037 + BCMapCellIterator* aIter, 1.4038 + nsCellMap* aCellMap = nullptr); 1.4039 + // The BCMapCellInfo has functions to set the continous 1.4040 + // border widths (see nsTablePainter.cpp for a description of the continous 1.4041 + // borders concept). The widths are computed inside these functions based on 1.4042 + // the current position inside the table and the cached frames that correspond 1.4043 + // to this position. The widths are stored in member variables of the internal 1.4044 + // table frames. 1.4045 + void SetTableTopLeftContBCBorder(); 1.4046 + void SetRowGroupLeftContBCBorder(); 1.4047 + void SetRowGroupRightContBCBorder(); 1.4048 + void SetRowGroupBottomContBCBorder(); 1.4049 + void SetRowLeftContBCBorder(); 1.4050 + void SetRowRightContBCBorder(); 1.4051 + void SetColumnTopRightContBCBorder(); 1.4052 + void SetColumnBottomContBCBorder(); 1.4053 + void SetColGroupBottomContBCBorder(); 1.4054 + void SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup, 1.4055 + nsTableRowFrame* aNextRow); 1.4056 + 1.4057 + // functions to set the border widths on the table related frames, where the 1.4058 + // knowledge about the current position in the table is used. 1.4059 + void SetTableTopBorderWidth(BCPixelSize aWidth); 1.4060 + void SetTableLeftBorderWidth(int32_t aRowY, BCPixelSize aWidth); 1.4061 + void SetTableRightBorderWidth(int32_t aRowY, BCPixelSize aWidth); 1.4062 + void SetTableBottomBorderWidth(BCPixelSize aWidth); 1.4063 + void SetLeftBorderWidths(BCPixelSize aWidth); 1.4064 + void SetRightBorderWidths(BCPixelSize aWidth); 1.4065 + void SetTopBorderWidths(BCPixelSize aWidth); 1.4066 + void SetBottomBorderWidths(BCPixelSize aWidth); 1.4067 + 1.4068 + // functions to compute the borders; they depend on the 1.4069 + // knowledge about the current position in the table. The edge functions 1.4070 + // should be called if a table edge is involved, otherwise the internal 1.4071 + // functions should be called. 1.4072 + BCCellBorder GetTopEdgeBorder(); 1.4073 + BCCellBorder GetBottomEdgeBorder(); 1.4074 + BCCellBorder GetLeftEdgeBorder(); 1.4075 + BCCellBorder GetRightEdgeBorder(); 1.4076 + BCCellBorder GetRightInternalBorder(); 1.4077 + BCCellBorder GetLeftInternalBorder(); 1.4078 + BCCellBorder GetTopInternalBorder(); 1.4079 + BCCellBorder GetBottomInternalBorder(); 1.4080 + 1.4081 + // functions to set the interal position information 1.4082 + void SetColumn(int32_t aColX); 1.4083 + // Increment the row as we loop over the rows of a rowspan 1.4084 + void IncrementRow(bool aResetToTopRowOfCell = false); 1.4085 + 1.4086 + // Helper functions to get extent of the cell 1.4087 + int32_t GetCellEndRowIndex() const; 1.4088 + int32_t GetCellEndColIndex() const; 1.4089 + 1.4090 + // storage of table information 1.4091 + nsTableFrame* mTableFrame; 1.4092 + int32_t mNumTableRows; 1.4093 + int32_t mNumTableCols; 1.4094 + BCPropertyData* mTableBCData; 1.4095 + 1.4096 + // storage of table ltr information, the border collapse code swaps the sides 1.4097 + // to account for rtl tables, this is done through mStartSide and mEndSide 1.4098 + bool mTableIsLTR; 1.4099 + mozilla::css::Side mStartSide; 1.4100 + mozilla::css::Side mEndSide; 1.4101 + 1.4102 + // a cell can only belong to one rowgroup 1.4103 + nsTableRowGroupFrame* mRowGroup; 1.4104 + 1.4105 + // a cell with a rowspan has a top and a bottom row, and rows in between 1.4106 + nsTableRowFrame* mTopRow; 1.4107 + nsTableRowFrame* mBottomRow; 1.4108 + nsTableRowFrame* mCurrentRowFrame; 1.4109 + 1.4110 + // a cell with a colspan has a left and right column and columns in between 1.4111 + // they can belong to different colgroups 1.4112 + nsTableColGroupFrame* mColGroup; 1.4113 + nsTableColGroupFrame* mCurrentColGroupFrame; 1.4114 + 1.4115 + nsTableColFrame* mLeftCol; 1.4116 + nsTableColFrame* mRightCol; 1.4117 + nsTableColFrame* mCurrentColFrame; 1.4118 + 1.4119 + // cell information 1.4120 + BCCellData* mCellData; 1.4121 + nsBCTableCellFrame* mCell; 1.4122 + 1.4123 + int32_t mRowIndex; 1.4124 + int32_t mRowSpan; 1.4125 + int32_t mColIndex; 1.4126 + int32_t mColSpan; 1.4127 + 1.4128 + // flags to describe the position of the cell with respect to the row- and 1.4129 + // colgroups, for instance mRgAtTop documents that the top cell border hits 1.4130 + // a rowgroup border 1.4131 + bool mRgAtTop; 1.4132 + bool mRgAtBottom; 1.4133 + bool mCgAtLeft; 1.4134 + bool mCgAtRight; 1.4135 + 1.4136 +}; 1.4137 + 1.4138 + 1.4139 +BCMapCellInfo::BCMapCellInfo(nsTableFrame* aTableFrame) 1.4140 +{ 1.4141 + mTableFrame = aTableFrame; 1.4142 + mTableIsLTR = 1.4143 + aTableFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR; 1.4144 + if (mTableIsLTR) { 1.4145 + mStartSide = NS_SIDE_LEFT; 1.4146 + mEndSide = NS_SIDE_RIGHT; 1.4147 + } 1.4148 + else { 1.4149 + mStartSide = NS_SIDE_RIGHT; 1.4150 + mEndSide = NS_SIDE_LEFT; 1.4151 + } 1.4152 + mNumTableRows = mTableFrame->GetRowCount(); 1.4153 + mNumTableCols = mTableFrame->GetColCount(); 1.4154 + mTableBCData = static_cast<BCPropertyData*> 1.4155 + (mTableFrame->Properties().Get(TableBCProperty())); 1.4156 + 1.4157 + ResetCellInfo(); 1.4158 +} 1.4159 + 1.4160 +void BCMapCellInfo::ResetCellInfo() 1.4161 +{ 1.4162 + mCellData = nullptr; 1.4163 + mRowGroup = nullptr; 1.4164 + mTopRow = nullptr; 1.4165 + mBottomRow = nullptr; 1.4166 + mColGroup = nullptr; 1.4167 + mLeftCol = nullptr; 1.4168 + mRightCol = nullptr; 1.4169 + mCell = nullptr; 1.4170 + mRowIndex = mRowSpan = mColIndex = mColSpan = 0; 1.4171 + mRgAtTop = mRgAtBottom = mCgAtLeft = mCgAtRight = false; 1.4172 +} 1.4173 + 1.4174 +inline int32_t BCMapCellInfo::GetCellEndRowIndex() const 1.4175 +{ 1.4176 + return mRowIndex + mRowSpan - 1; 1.4177 +} 1.4178 + 1.4179 +inline int32_t BCMapCellInfo::GetCellEndColIndex() const 1.4180 +{ 1.4181 + return mColIndex + mColSpan - 1; 1.4182 +} 1.4183 + 1.4184 + 1.4185 +class BCMapCellIterator 1.4186 +{ 1.4187 +public: 1.4188 + BCMapCellIterator(nsTableFrame* aTableFrame, 1.4189 + const nsIntRect& aDamageArea); 1.4190 + 1.4191 + void First(BCMapCellInfo& aMapCellInfo); 1.4192 + 1.4193 + void Next(BCMapCellInfo& aMapCellInfo); 1.4194 + 1.4195 + void PeekRight(BCMapCellInfo& aRefInfo, 1.4196 + uint32_t aRowIndex, 1.4197 + BCMapCellInfo& aAjaInfo); 1.4198 + 1.4199 + void PeekBottom(BCMapCellInfo& aRefInfo, 1.4200 + uint32_t aColIndex, 1.4201 + BCMapCellInfo& aAjaInfo); 1.4202 + 1.4203 + bool IsNewRow() { return mIsNewRow; } 1.4204 + 1.4205 + nsTableRowFrame* GetPrevRow() const { return mPrevRow; } 1.4206 + nsTableRowFrame* GetCurrentRow() const { return mRow; } 1.4207 + nsTableRowGroupFrame* GetCurrentRowGroup() const { return mRowGroup;} 1.4208 + 1.4209 + int32_t mRowGroupStart; 1.4210 + int32_t mRowGroupEnd; 1.4211 + bool mAtEnd; 1.4212 + nsCellMap* mCellMap; 1.4213 + 1.4214 +private: 1.4215 + bool SetNewRow(nsTableRowFrame* row = nullptr); 1.4216 + bool SetNewRowGroup(bool aFindFirstDamagedRow); 1.4217 + 1.4218 + nsTableFrame* mTableFrame; 1.4219 + nsTableCellMap* mTableCellMap; 1.4220 + nsTableFrame::RowGroupArray mRowGroups; 1.4221 + nsTableRowGroupFrame* mRowGroup; 1.4222 + int32_t mRowGroupIndex; 1.4223 + uint32_t mNumTableRows; 1.4224 + nsTableRowFrame* mRow; 1.4225 + nsTableRowFrame* mPrevRow; 1.4226 + bool mIsNewRow; 1.4227 + int32_t mRowIndex; 1.4228 + uint32_t mNumTableCols; 1.4229 + int32_t mColIndex; 1.4230 + nsPoint mAreaStart; 1.4231 + nsPoint mAreaEnd; 1.4232 +}; 1.4233 + 1.4234 +BCMapCellIterator::BCMapCellIterator(nsTableFrame* aTableFrame, 1.4235 + const nsIntRect& aDamageArea) 1.4236 +:mTableFrame(aTableFrame) 1.4237 +{ 1.4238 + mTableCellMap = aTableFrame->GetCellMap(); 1.4239 + 1.4240 + mAreaStart.x = aDamageArea.x; 1.4241 + mAreaStart.y = aDamageArea.y; 1.4242 + mAreaEnd.y = aDamageArea.y + aDamageArea.height - 1; 1.4243 + mAreaEnd.x = aDamageArea.x + aDamageArea.width - 1; 1.4244 + 1.4245 + mNumTableRows = mTableFrame->GetRowCount(); 1.4246 + mRow = nullptr; 1.4247 + mRowIndex = 0; 1.4248 + mNumTableCols = mTableFrame->GetColCount(); 1.4249 + mColIndex = 0; 1.4250 + mRowGroupIndex = -1; 1.4251 + 1.4252 + // Get the ordered row groups 1.4253 + aTableFrame->OrderRowGroups(mRowGroups); 1.4254 + 1.4255 + mAtEnd = true; // gets reset when First() is called 1.4256 +} 1.4257 + 1.4258 +// fill fields that we need for border collapse computation on a given cell 1.4259 +void 1.4260 +BCMapCellInfo::SetInfo(nsTableRowFrame* aNewRow, 1.4261 + int32_t aColIndex, 1.4262 + BCCellData* aCellData, 1.4263 + BCMapCellIterator* aIter, 1.4264 + nsCellMap* aCellMap) 1.4265 +{ 1.4266 + // fill the cell information 1.4267 + mCellData = aCellData; 1.4268 + mColIndex = aColIndex; 1.4269 + 1.4270 + // initialize the row information if it was not previously set for cells in 1.4271 + // this row 1.4272 + mRowIndex = 0; 1.4273 + if (aNewRow) { 1.4274 + mTopRow = aNewRow; 1.4275 + mRowIndex = aNewRow->GetRowIndex(); 1.4276 + } 1.4277 + 1.4278 + // fill cell frame info and row information 1.4279 + mCell = nullptr; 1.4280 + mRowSpan = 1; 1.4281 + mColSpan = 1; 1.4282 + if (aCellData) { 1.4283 + mCell = static_cast<nsBCTableCellFrame*>(aCellData->GetCellFrame()); 1.4284 + if (mCell) { 1.4285 + if (!mTopRow) { 1.4286 + mTopRow = static_cast<nsTableRowFrame*>(mCell->GetParent()); 1.4287 + if (!mTopRow) ABORT0(); 1.4288 + mRowIndex = mTopRow->GetRowIndex(); 1.4289 + } 1.4290 + mColSpan = mTableFrame->GetEffectiveColSpan(*mCell, aCellMap); 1.4291 + mRowSpan = mTableFrame->GetEffectiveRowSpan(*mCell, aCellMap); 1.4292 + } 1.4293 + } 1.4294 + 1.4295 + if (!mTopRow) { 1.4296 + mTopRow = aIter->GetCurrentRow(); 1.4297 + } 1.4298 + if (1 == mRowSpan) { 1.4299 + mBottomRow = mTopRow; 1.4300 + } 1.4301 + else { 1.4302 + mBottomRow = mTopRow->GetNextRow(); 1.4303 + if (mBottomRow) { 1.4304 + for (int32_t spanY = 2; mBottomRow && (spanY < mRowSpan); spanY++) { 1.4305 + mBottomRow = mBottomRow->GetNextRow(); 1.4306 + } 1.4307 + NS_ASSERTION(mBottomRow, "spanned row not found"); 1.4308 + } 1.4309 + else { 1.4310 + NS_ASSERTION(false, "error in cell map"); 1.4311 + mRowSpan = 1; 1.4312 + mBottomRow = mTopRow; 1.4313 + } 1.4314 + } 1.4315 + // row group frame info 1.4316 + // try to reuse the rgStart and rgEnd from the iterator as calls to 1.4317 + // GetRowCount() are computationally expensive and should be avoided if 1.4318 + // possible 1.4319 + uint32_t rgStart = aIter->mRowGroupStart; 1.4320 + uint32_t rgEnd = aIter->mRowGroupEnd; 1.4321 + mRowGroup = static_cast<nsTableRowGroupFrame*>(mTopRow->GetParent()); 1.4322 + if (mRowGroup != aIter->GetCurrentRowGroup()) { 1.4323 + rgStart = mRowGroup->GetStartRowIndex(); 1.4324 + rgEnd = rgStart + mRowGroup->GetRowCount() - 1; 1.4325 + } 1.4326 + uint32_t rowIndex = mTopRow->GetRowIndex(); 1.4327 + mRgAtTop = (rgStart == rowIndex); 1.4328 + mRgAtBottom = (rgEnd == rowIndex + mRowSpan - 1); 1.4329 + 1.4330 + // col frame info 1.4331 + mLeftCol = mTableFrame->GetColFrame(aColIndex); 1.4332 + if (!mLeftCol) ABORT0(); 1.4333 + 1.4334 + mRightCol = mLeftCol; 1.4335 + if (mColSpan > 1) { 1.4336 + nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + 1.4337 + mColSpan -1); 1.4338 + if (!colFrame) ABORT0(); 1.4339 + mRightCol = colFrame; 1.4340 + } 1.4341 + 1.4342 + // col group frame info 1.4343 + mColGroup = static_cast<nsTableColGroupFrame*>(mLeftCol->GetParent()); 1.4344 + int32_t cgStart = mColGroup->GetStartColumnIndex(); 1.4345 + int32_t cgEnd = std::max(0, cgStart + mColGroup->GetColCount() - 1); 1.4346 + mCgAtLeft = (cgStart == aColIndex); 1.4347 + mCgAtRight = (cgEnd == aColIndex + mColSpan - 1); 1.4348 +} 1.4349 + 1.4350 +bool 1.4351 +BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow) 1.4352 +{ 1.4353 + mAtEnd = true; 1.4354 + mPrevRow = mRow; 1.4355 + if (aRow) { 1.4356 + mRow = aRow; 1.4357 + } 1.4358 + else if (mRow) { 1.4359 + mRow = mRow->GetNextRow(); 1.4360 + } 1.4361 + if (mRow) { 1.4362 + mRowIndex = mRow->GetRowIndex(); 1.4363 + // get to the first entry with an originating cell 1.4364 + int32_t rgRowIndex = mRowIndex - mRowGroupStart; 1.4365 + if (uint32_t(rgRowIndex) >= mCellMap->mRows.Length()) 1.4366 + ABORT1(false); 1.4367 + const nsCellMap::CellDataArray& row = mCellMap->mRows[rgRowIndex]; 1.4368 + 1.4369 + for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) { 1.4370 + CellData* cellData = row.SafeElementAt(mColIndex); 1.4371 + if (!cellData) { // add a dead cell data 1.4372 + nsIntRect damageArea; 1.4373 + cellData = mCellMap->AppendCell(*mTableCellMap, nullptr, rgRowIndex, 1.4374 + false, 0, damageArea); 1.4375 + if (!cellData) ABORT1(false); 1.4376 + } 1.4377 + if (cellData && (cellData->IsOrig() || cellData->IsDead())) { 1.4378 + break; 1.4379 + } 1.4380 + } 1.4381 + mIsNewRow = true; 1.4382 + mAtEnd = false; 1.4383 + } 1.4384 + else ABORT1(false); 1.4385 + 1.4386 + return !mAtEnd; 1.4387 +} 1.4388 + 1.4389 +bool 1.4390 +BCMapCellIterator::SetNewRowGroup(bool aFindFirstDamagedRow) 1.4391 +{ 1.4392 + mAtEnd = true; 1.4393 + int32_t numRowGroups = mRowGroups.Length(); 1.4394 + mCellMap = nullptr; 1.4395 + for (mRowGroupIndex++; mRowGroupIndex < numRowGroups; mRowGroupIndex++) { 1.4396 + mRowGroup = mRowGroups[mRowGroupIndex]; 1.4397 + int32_t rowCount = mRowGroup->GetRowCount(); 1.4398 + mRowGroupStart = mRowGroup->GetStartRowIndex(); 1.4399 + mRowGroupEnd = mRowGroupStart + rowCount - 1; 1.4400 + if (rowCount > 0) { 1.4401 + mCellMap = mTableCellMap->GetMapFor(mRowGroup, mCellMap); 1.4402 + if (!mCellMap) ABORT1(false); 1.4403 + nsTableRowFrame* firstRow = mRowGroup->GetFirstRow(); 1.4404 + if (aFindFirstDamagedRow) { 1.4405 + if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) { 1.4406 + // the damage area starts in the row group 1.4407 + if (aFindFirstDamagedRow) { 1.4408 + // find the correct first damaged row 1.4409 + int32_t numRows = mAreaStart.y - mRowGroupStart; 1.4410 + for (int32_t i = 0; i < numRows; i++) { 1.4411 + firstRow = firstRow->GetNextRow(); 1.4412 + if (!firstRow) ABORT1(false); 1.4413 + } 1.4414 + } 1.4415 + } 1.4416 + else { 1.4417 + continue; 1.4418 + } 1.4419 + } 1.4420 + if (SetNewRow(firstRow)) { // sets mAtEnd 1.4421 + break; 1.4422 + } 1.4423 + } 1.4424 + } 1.4425 + 1.4426 + return !mAtEnd; 1.4427 +} 1.4428 + 1.4429 +void 1.4430 +BCMapCellIterator::First(BCMapCellInfo& aMapInfo) 1.4431 +{ 1.4432 + aMapInfo.ResetCellInfo(); 1.4433 + 1.4434 + SetNewRowGroup(true); // sets mAtEnd 1.4435 + while (!mAtEnd) { 1.4436 + if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) { 1.4437 + BCCellData* cellData = 1.4438 + static_cast<BCCellData*>(mCellMap->GetDataAt(mAreaStart.y - 1.4439 + mRowGroupStart, 1.4440 + mAreaStart.x)); 1.4441 + if (cellData && (cellData->IsOrig() || cellData->IsDead())) { 1.4442 + aMapInfo.SetInfo(mRow, mAreaStart.x, cellData, this); 1.4443 + return; 1.4444 + } 1.4445 + else { 1.4446 + NS_ASSERTION(((0 == mAreaStart.x) && (mRowGroupStart == mAreaStart.y)) , 1.4447 + "damage area expanded incorrectly"); 1.4448 + } 1.4449 + } 1.4450 + SetNewRowGroup(true); // sets mAtEnd 1.4451 + } 1.4452 +} 1.4453 + 1.4454 +void 1.4455 +BCMapCellIterator::Next(BCMapCellInfo& aMapInfo) 1.4456 +{ 1.4457 + if (mAtEnd) ABORT0(); 1.4458 + aMapInfo.ResetCellInfo(); 1.4459 + 1.4460 + mIsNewRow = false; 1.4461 + mColIndex++; 1.4462 + while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) { 1.4463 + for (; mColIndex <= mAreaEnd.x; mColIndex++) { 1.4464 + int32_t rgRowIndex = mRowIndex - mRowGroupStart; 1.4465 + BCCellData* cellData = 1.4466 + static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, mColIndex)); 1.4467 + if (!cellData) { // add a dead cell data 1.4468 + nsIntRect damageArea; 1.4469 + cellData = 1.4470 + static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr, 1.4471 + rgRowIndex, false, 0, 1.4472 + damageArea)); 1.4473 + if (!cellData) ABORT0(); 1.4474 + } 1.4475 + if (cellData && (cellData->IsOrig() || cellData->IsDead())) { 1.4476 + aMapInfo.SetInfo(mRow, mColIndex, cellData, this); 1.4477 + return; 1.4478 + } 1.4479 + } 1.4480 + if (mRowIndex >= mRowGroupEnd) { 1.4481 + SetNewRowGroup(false); // could set mAtEnd 1.4482 + } 1.4483 + else { 1.4484 + SetNewRow(); // could set mAtEnd 1.4485 + } 1.4486 + } 1.4487 + mAtEnd = true; 1.4488 +} 1.4489 + 1.4490 +void 1.4491 +BCMapCellIterator::PeekRight(BCMapCellInfo& aRefInfo, 1.4492 + uint32_t aRowIndex, 1.4493 + BCMapCellInfo& aAjaInfo) 1.4494 +{ 1.4495 + aAjaInfo.ResetCellInfo(); 1.4496 + int32_t colIndex = aRefInfo.mColIndex + aRefInfo.mColSpan; 1.4497 + uint32_t rgRowIndex = aRowIndex - mRowGroupStart; 1.4498 + 1.4499 + BCCellData* cellData = 1.4500 + static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex)); 1.4501 + if (!cellData) { // add a dead cell data 1.4502 + NS_ASSERTION(colIndex < mTableCellMap->GetColCount(), "program error"); 1.4503 + nsIntRect damageArea; 1.4504 + cellData = 1.4505 + static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr, 1.4506 + rgRowIndex, false, 0, 1.4507 + damageArea)); 1.4508 + if (!cellData) ABORT0(); 1.4509 + } 1.4510 + nsTableRowFrame* row = nullptr; 1.4511 + if (cellData->IsRowSpan()) { 1.4512 + rgRowIndex -= cellData->GetRowSpanOffset(); 1.4513 + cellData = 1.4514 + static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex)); 1.4515 + if (!cellData) 1.4516 + ABORT0(); 1.4517 + } 1.4518 + else { 1.4519 + row = mRow; 1.4520 + } 1.4521 + aAjaInfo.SetInfo(row, colIndex, cellData, this); 1.4522 +} 1.4523 + 1.4524 +void 1.4525 +BCMapCellIterator::PeekBottom(BCMapCellInfo& aRefInfo, 1.4526 + uint32_t aColIndex, 1.4527 + BCMapCellInfo& aAjaInfo) 1.4528 +{ 1.4529 + aAjaInfo.ResetCellInfo(); 1.4530 + int32_t rowIndex = aRefInfo.mRowIndex + aRefInfo.mRowSpan; 1.4531 + int32_t rgRowIndex = rowIndex - mRowGroupStart; 1.4532 + nsTableRowGroupFrame* rg = mRowGroup; 1.4533 + nsCellMap* cellMap = mCellMap; 1.4534 + nsTableRowFrame* nextRow = nullptr; 1.4535 + if (rowIndex > mRowGroupEnd) { 1.4536 + int32_t nextRgIndex = mRowGroupIndex; 1.4537 + do { 1.4538 + nextRgIndex++; 1.4539 + rg = mRowGroups.SafeElementAt(nextRgIndex); 1.4540 + if (rg) { 1.4541 + cellMap = mTableCellMap->GetMapFor(rg, cellMap); if (!cellMap) ABORT0(); 1.4542 + rgRowIndex = 0; 1.4543 + nextRow = rg->GetFirstRow(); 1.4544 + } 1.4545 + } 1.4546 + while (rg && !nextRow); 1.4547 + if(!rg) return; 1.4548 + } 1.4549 + else { 1.4550 + // get the row within the same row group 1.4551 + nextRow = mRow; 1.4552 + for (int32_t i = 0; i < aRefInfo.mRowSpan; i++) { 1.4553 + nextRow = nextRow->GetNextRow(); if (!nextRow) ABORT0(); 1.4554 + } 1.4555 + } 1.4556 + 1.4557 + BCCellData* cellData = 1.4558 + static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex)); 1.4559 + if (!cellData) { // add a dead cell data 1.4560 + NS_ASSERTION(rgRowIndex < cellMap->GetRowCount(), "program error"); 1.4561 + nsIntRect damageArea; 1.4562 + cellData = 1.4563 + static_cast<BCCellData*>(cellMap->AppendCell(*mTableCellMap, nullptr, 1.4564 + rgRowIndex, false, 0, 1.4565 + damageArea)); 1.4566 + if (!cellData) ABORT0(); 1.4567 + } 1.4568 + if (cellData->IsColSpan()) { 1.4569 + aColIndex -= cellData->GetColSpanOffset(); 1.4570 + cellData = 1.4571 + static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex)); 1.4572 + } 1.4573 + aAjaInfo.SetInfo(nextRow, aColIndex, cellData, this, cellMap); 1.4574 +} 1.4575 + 1.4576 +// Assign priorities to border styles. For example, styleToPriority(NS_STYLE_BORDER_STYLE_SOLID) 1.4577 +// will return the priority of NS_STYLE_BORDER_STYLE_SOLID. 1.4578 +static uint8_t styleToPriority[13] = { 0, // NS_STYLE_BORDER_STYLE_NONE 1.4579 + 2, // NS_STYLE_BORDER_STYLE_GROOVE 1.4580 + 4, // NS_STYLE_BORDER_STYLE_RIDGE 1.4581 + 5, // NS_STYLE_BORDER_STYLE_DOTTED 1.4582 + 6, // NS_STYLE_BORDER_STYLE_DASHED 1.4583 + 7, // NS_STYLE_BORDER_STYLE_SOLID 1.4584 + 8, // NS_STYLE_BORDER_STYLE_DOUBLE 1.4585 + 1, // NS_STYLE_BORDER_STYLE_INSET 1.4586 + 3, // NS_STYLE_BORDER_STYLE_OUTSET 1.4587 + 9 };// NS_STYLE_BORDER_STYLE_HIDDEN 1.4588 +// priority rules follow CSS 2.1 spec 1.4589 +// 'hidden', 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', 1.4590 +// and the lowest: 'inset'. none is even weaker 1.4591 +#define CELL_CORNER true 1.4592 + 1.4593 +/** return the border style, border color for a given frame and side 1.4594 + * @param aFrame - query the info for this frame 1.4595 + * @param aSide - the side of the frame 1.4596 + * @param aStyle - the border style 1.4597 + * @param aColor - the border color 1.4598 + * @param aTableIsLTR - table direction is LTR 1.4599 + */ 1.4600 +static void 1.4601 +GetColorAndStyle(const nsIFrame* aFrame, 1.4602 + mozilla::css::Side aSide, 1.4603 + uint8_t& aStyle, 1.4604 + nscolor& aColor, 1.4605 + bool aTableIsLTR) 1.4606 +{ 1.4607 + NS_PRECONDITION(aFrame, "null frame"); 1.4608 + // initialize out arg 1.4609 + aColor = 0; 1.4610 + const nsStyleBorder* styleData = aFrame->StyleBorder(); 1.4611 + if(!aTableIsLTR) { // revert the directions 1.4612 + if (NS_SIDE_RIGHT == aSide) { 1.4613 + aSide = NS_SIDE_LEFT; 1.4614 + } 1.4615 + else if (NS_SIDE_LEFT == aSide) { 1.4616 + aSide = NS_SIDE_RIGHT; 1.4617 + } 1.4618 + } 1.4619 + aStyle = styleData->GetBorderStyle(aSide); 1.4620 + 1.4621 + if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) || 1.4622 + (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) { 1.4623 + return; 1.4624 + } 1.4625 + aColor = aFrame->StyleContext()->GetVisitedDependentColor( 1.4626 + nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[aSide]); 1.4627 +} 1.4628 + 1.4629 +/** coerce the paint style as required by CSS2.1 1.4630 + * @param aFrame - query the info for this frame 1.4631 + * @param aSide - the side of the frame 1.4632 + * @param aStyle - the border style 1.4633 + * @param aColor - the border color 1.4634 + * @param aTableIsLTR - table direction is LTR 1.4635 + */ 1.4636 +static void 1.4637 +GetPaintStyleInfo(const nsIFrame* aFrame, 1.4638 + mozilla::css::Side aSide, 1.4639 + uint8_t& aStyle, 1.4640 + nscolor& aColor, 1.4641 + bool aTableIsLTR) 1.4642 +{ 1.4643 + GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR); 1.4644 + if (NS_STYLE_BORDER_STYLE_INSET == aStyle) { 1.4645 + aStyle = NS_STYLE_BORDER_STYLE_RIDGE; 1.4646 + } 1.4647 + else if (NS_STYLE_BORDER_STYLE_OUTSET == aStyle) { 1.4648 + aStyle = NS_STYLE_BORDER_STYLE_GROOVE; 1.4649 + } 1.4650 +} 1.4651 + 1.4652 +/** return the border style, border color and the width in pixel for a given 1.4653 + * frame and side 1.4654 + * @param aFrame - query the info for this frame 1.4655 + * @param aSide - the side of the frame 1.4656 + * @param aStyle - the border style 1.4657 + * @param aColor - the border color 1.4658 + * @param aTableIsLTR - table direction is LTR 1.4659 + * @param aWidth - the border width in px. 1.4660 + * @param aTwipsToPixels - conversion factor from twips to pixel 1.4661 + */ 1.4662 +static void 1.4663 +GetColorAndStyle(const nsIFrame* aFrame, 1.4664 + mozilla::css::Side aSide, 1.4665 + uint8_t& aStyle, 1.4666 + nscolor& aColor, 1.4667 + bool aTableIsLTR, 1.4668 + BCPixelSize& aWidth) 1.4669 +{ 1.4670 + GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR); 1.4671 + if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) || 1.4672 + (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) { 1.4673 + aWidth = 0; 1.4674 + return; 1.4675 + } 1.4676 + const nsStyleBorder* styleData = aFrame->StyleBorder(); 1.4677 + nscoord width; 1.4678 + if(!aTableIsLTR) { // revert the directions 1.4679 + if (NS_SIDE_RIGHT == aSide) { 1.4680 + aSide = NS_SIDE_LEFT; 1.4681 + } 1.4682 + else if (NS_SIDE_LEFT == aSide) { 1.4683 + aSide = NS_SIDE_RIGHT; 1.4684 + } 1.4685 + } 1.4686 + width = styleData->GetComputedBorderWidth(aSide); 1.4687 + aWidth = nsPresContext::AppUnitsToIntCSSPixels(width); 1.4688 +} 1.4689 + 1.4690 +class nsDelayedCalcBCBorders : public nsRunnable { 1.4691 +public: 1.4692 + nsDelayedCalcBCBorders(nsIFrame* aFrame) : 1.4693 + mFrame(aFrame) {} 1.4694 + 1.4695 + NS_IMETHOD Run() MOZ_OVERRIDE { 1.4696 + if (mFrame) { 1.4697 + nsTableFrame* tableFrame = static_cast <nsTableFrame*>(mFrame.GetFrame()); 1.4698 + if (tableFrame->NeedToCalcBCBorders()) { 1.4699 + tableFrame->CalcBCBorders(); 1.4700 + } 1.4701 + } 1.4702 + return NS_OK; 1.4703 + } 1.4704 +private: 1.4705 + nsWeakFrame mFrame; 1.4706 +}; 1.4707 + 1.4708 +bool 1.4709 +nsTableFrame::BCRecalcNeeded(nsStyleContext* aOldStyleContext, 1.4710 + nsStyleContext* aNewStyleContext) 1.4711 +{ 1.4712 + // Attention: the old style context is the one we're forgetting, 1.4713 + // and hence possibly completely bogus for GetStyle* purposes. 1.4714 + // We use PeekStyleData instead. 1.4715 + 1.4716 + const nsStyleBorder* oldStyleData = aOldStyleContext->PeekStyleBorder(); 1.4717 + if (!oldStyleData) 1.4718 + return false; 1.4719 + 1.4720 + const nsStyleBorder* newStyleData = aNewStyleContext->StyleBorder(); 1.4721 + nsChangeHint change = newStyleData->CalcDifference(*oldStyleData); 1.4722 + if (!change) 1.4723 + return false; 1.4724 + if (change & nsChangeHint_NeedReflow) 1.4725 + return true; // the caller only needs to mark the bc damage area 1.4726 + if (change & nsChangeHint_RepaintFrame) { 1.4727 + // we need to recompute the borders and the caller needs to mark 1.4728 + // the bc damage area 1.4729 + // XXX In principle this should only be necessary for border style changes 1.4730 + // However the bc painting code tries to maximize the drawn border segments 1.4731 + // so it stores in the cellmap where a new border segment starts and this 1.4732 + // introduces a unwanted cellmap data dependence on color 1.4733 + nsCOMPtr<nsIRunnable> evt = new nsDelayedCalcBCBorders(this); 1.4734 + NS_DispatchToCurrentThread(evt); 1.4735 + return true; 1.4736 + } 1.4737 + return false; 1.4738 +} 1.4739 + 1.4740 + 1.4741 +// Compare two border segments, this comparison depends whether the two 1.4742 +// segments meet at a corner and whether the second segment is horizontal. 1.4743 +// The return value is whichever of aBorder1 or aBorder2 dominates. 1.4744 +static const BCCellBorder& 1.4745 +CompareBorders(bool aIsCorner, // Pass true for corner calculations 1.4746 + const BCCellBorder& aBorder1, 1.4747 + const BCCellBorder& aBorder2, 1.4748 + bool aSecondIsHorizontal, 1.4749 + bool* aFirstDominates = nullptr) 1.4750 +{ 1.4751 + bool firstDominates = true; 1.4752 + 1.4753 + if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder1.style) { 1.4754 + firstDominates = (aIsCorner) ? false : true; 1.4755 + } 1.4756 + else if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder2.style) { 1.4757 + firstDominates = (aIsCorner) ? true : false; 1.4758 + } 1.4759 + else if (aBorder1.width < aBorder2.width) { 1.4760 + firstDominates = false; 1.4761 + } 1.4762 + else if (aBorder1.width == aBorder2.width) { 1.4763 + if (styleToPriority[aBorder1.style] < styleToPriority[aBorder2.style]) { 1.4764 + firstDominates = false; 1.4765 + } 1.4766 + else if (styleToPriority[aBorder1.style] == styleToPriority[aBorder2.style]) { 1.4767 + if (aBorder1.owner == aBorder2.owner) { 1.4768 + firstDominates = !aSecondIsHorizontal; 1.4769 + } 1.4770 + else if (aBorder1.owner < aBorder2.owner) { 1.4771 + firstDominates = false; 1.4772 + } 1.4773 + } 1.4774 + } 1.4775 + 1.4776 + if (aFirstDominates) 1.4777 + *aFirstDominates = firstDominates; 1.4778 + 1.4779 + if (firstDominates) 1.4780 + return aBorder1; 1.4781 + return aBorder2; 1.4782 +} 1.4783 + 1.4784 +/** calc the dominant border by considering the table, row/col group, row/col, 1.4785 + * cell. 1.4786 + * Depending on whether the side is vertical or horizontal and whether 1.4787 + * adjacent frames are taken into account the ownership of a single border 1.4788 + * segment is defined. The return value is the dominating border 1.4789 + * The cellmap stores only top and left borders for each cellmap position. 1.4790 + * If the cell border is owned by the cell that is left of the border 1.4791 + * it will be an adjacent owner aka eAjaCellOwner. See celldata.h for the other 1.4792 + * scenarios with a adjacent owner. 1.4793 + * @param xxxFrame - the frame for style information, might be zero if 1.4794 + * it should not be considered 1.4795 + * @param aSide - side of the frames that should be considered 1.4796 + * @param aAja - the border comparison takes place from the point of 1.4797 + * a frame that is adjacent to the cellmap entry, for 1.4798 + * when a cell owns its lower border it will be the 1.4799 + * adjacent owner as in the cellmap only top and left 1.4800 + * borders are stored. 1.4801 + * @param aTwipsToPixels - conversion factor as borders need to be drawn pixel 1.4802 + * aligned. 1.4803 + */ 1.4804 +static BCCellBorder 1.4805 +CompareBorders(const nsIFrame* aTableFrame, 1.4806 + const nsIFrame* aColGroupFrame, 1.4807 + const nsIFrame* aColFrame, 1.4808 + const nsIFrame* aRowGroupFrame, 1.4809 + const nsIFrame* aRowFrame, 1.4810 + const nsIFrame* aCellFrame, 1.4811 + bool aTableIsLTR, 1.4812 + mozilla::css::Side aSide, 1.4813 + bool aAja) 1.4814 +{ 1.4815 + BCCellBorder border, tempBorder; 1.4816 + bool horizontal = (NS_SIDE_TOP == aSide) || (NS_SIDE_BOTTOM == aSide); 1.4817 + 1.4818 + // start with the table as dominant if present 1.4819 + if (aTableFrame) { 1.4820 + GetColorAndStyle(aTableFrame, aSide, border.style, border.color, aTableIsLTR, border.width); 1.4821 + border.owner = eTableOwner; 1.4822 + if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { 1.4823 + return border; 1.4824 + } 1.4825 + } 1.4826 + // see if the colgroup is dominant 1.4827 + if (aColGroupFrame) { 1.4828 + GetColorAndStyle(aColGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); 1.4829 + tempBorder.owner = (aAja && !horizontal) ? eAjaColGroupOwner : eColGroupOwner; 1.4830 + // pass here and below false for aSecondIsHorizontal as it is only used for corner calculations. 1.4831 + border = CompareBorders(!CELL_CORNER, border, tempBorder, false); 1.4832 + if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { 1.4833 + return border; 1.4834 + } 1.4835 + } 1.4836 + // see if the col is dominant 1.4837 + if (aColFrame) { 1.4838 + GetColorAndStyle(aColFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); 1.4839 + tempBorder.owner = (aAja && !horizontal) ? eAjaColOwner : eColOwner; 1.4840 + border = CompareBorders(!CELL_CORNER, border, tempBorder, false); 1.4841 + if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { 1.4842 + return border; 1.4843 + } 1.4844 + } 1.4845 + // see if the rowgroup is dominant 1.4846 + if (aRowGroupFrame) { 1.4847 + GetColorAndStyle(aRowGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); 1.4848 + tempBorder.owner = (aAja && horizontal) ? eAjaRowGroupOwner : eRowGroupOwner; 1.4849 + border = CompareBorders(!CELL_CORNER, border, tempBorder, false); 1.4850 + if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { 1.4851 + return border; 1.4852 + } 1.4853 + } 1.4854 + // see if the row is dominant 1.4855 + if (aRowFrame) { 1.4856 + GetColorAndStyle(aRowFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); 1.4857 + tempBorder.owner = (aAja && horizontal) ? eAjaRowOwner : eRowOwner; 1.4858 + border = CompareBorders(!CELL_CORNER, border, tempBorder, false); 1.4859 + if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { 1.4860 + return border; 1.4861 + } 1.4862 + } 1.4863 + // see if the cell is dominant 1.4864 + if (aCellFrame) { 1.4865 + GetColorAndStyle(aCellFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); 1.4866 + tempBorder.owner = (aAja) ? eAjaCellOwner : eCellOwner; 1.4867 + border = CompareBorders(!CELL_CORNER, border, tempBorder, false); 1.4868 + } 1.4869 + return border; 1.4870 +} 1.4871 + 1.4872 +static bool 1.4873 +Perpendicular(mozilla::css::Side aSide1, 1.4874 + mozilla::css::Side aSide2) 1.4875 +{ 1.4876 + switch (aSide1) { 1.4877 + case NS_SIDE_TOP: 1.4878 + return (NS_SIDE_BOTTOM != aSide2); 1.4879 + case NS_SIDE_RIGHT: 1.4880 + return (NS_SIDE_LEFT != aSide2); 1.4881 + case NS_SIDE_BOTTOM: 1.4882 + return (NS_SIDE_TOP != aSide2); 1.4883 + default: // NS_SIDE_LEFT 1.4884 + return (NS_SIDE_RIGHT != aSide2); 1.4885 + } 1.4886 +} 1.4887 + 1.4888 +// XXX allocate this as number-of-cols+1 instead of number-of-cols+1 * number-of-rows+1 1.4889 +struct BCCornerInfo 1.4890 +{ 1.4891 + BCCornerInfo() { ownerColor = 0; ownerWidth = subWidth = ownerElem = subSide = 1.4892 + subElem = hasDashDot = numSegs = bevel = 0; ownerSide = NS_SIDE_TOP; 1.4893 + ownerStyle = 0xFF; subStyle = NS_STYLE_BORDER_STYLE_SOLID; } 1.4894 + void Set(mozilla::css::Side aSide, 1.4895 + BCCellBorder border); 1.4896 + 1.4897 + void Update(mozilla::css::Side aSide, 1.4898 + BCCellBorder border); 1.4899 + 1.4900 + nscolor ownerColor; // color of borderOwner 1.4901 + uint16_t ownerWidth; // pixel width of borderOwner 1.4902 + uint16_t subWidth; // pixel width of the largest border intersecting the border perpendicular 1.4903 + // to ownerSide 1.4904 + uint32_t ownerSide:2; // mozilla::css::Side (e.g NS_SIDE_TOP, NS_SIDE_RIGHT, etc) of the border 1.4905 + // owning the corner relative to the corner 1.4906 + uint32_t ownerElem:3; // elem type (e.g. eTable, eGroup, etc) owning the corner 1.4907 + uint32_t ownerStyle:8; // border style of ownerElem 1.4908 + uint32_t subSide:2; // side of border with subWidth relative to the corner 1.4909 + uint32_t subElem:3; // elem type (e.g. eTable, eGroup, etc) of sub owner 1.4910 + uint32_t subStyle:8; // border style of subElem 1.4911 + uint32_t hasDashDot:1; // does a dashed, dotted segment enter the corner, they cannot be beveled 1.4912 + uint32_t numSegs:3; // number of segments entering corner 1.4913 + uint32_t bevel:1; // is the corner beveled (uses the above two fields together with subWidth) 1.4914 + uint32_t unused:1; 1.4915 +}; 1.4916 + 1.4917 +void 1.4918 +BCCornerInfo::Set(mozilla::css::Side aSide, 1.4919 + BCCellBorder aBorder) 1.4920 +{ 1.4921 + ownerElem = aBorder.owner; 1.4922 + ownerStyle = aBorder.style; 1.4923 + ownerWidth = aBorder.width; 1.4924 + ownerColor = aBorder.color; 1.4925 + ownerSide = aSide; 1.4926 + hasDashDot = 0; 1.4927 + numSegs = 0; 1.4928 + if (aBorder.width > 0) { 1.4929 + numSegs++; 1.4930 + hasDashDot = (NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) || 1.4931 + (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style); 1.4932 + } 1.4933 + bevel = 0; 1.4934 + subWidth = 0; 1.4935 + // the following will get set later 1.4936 + subSide = ((aSide == NS_SIDE_LEFT) || (aSide == NS_SIDE_RIGHT)) ? NS_SIDE_TOP : NS_SIDE_LEFT; 1.4937 + subElem = eTableOwner; 1.4938 + subStyle = NS_STYLE_BORDER_STYLE_SOLID; 1.4939 +} 1.4940 + 1.4941 +void 1.4942 +BCCornerInfo::Update(mozilla::css::Side aSide, 1.4943 + BCCellBorder aBorder) 1.4944 +{ 1.4945 + bool existingWins = false; 1.4946 + if (0xFF == ownerStyle) { // initial value indiating that it hasn't been set yet 1.4947 + Set(aSide, aBorder); 1.4948 + } 1.4949 + else { 1.4950 + bool horizontal = (NS_SIDE_LEFT == aSide) || (NS_SIDE_RIGHT == aSide); // relative to the corner 1.4951 + BCCellBorder oldBorder, tempBorder; 1.4952 + oldBorder.owner = (BCBorderOwner) ownerElem; 1.4953 + oldBorder.style = ownerStyle; 1.4954 + oldBorder.width = ownerWidth; 1.4955 + oldBorder.color = ownerColor; 1.4956 + 1.4957 + mozilla::css::Side oldSide = mozilla::css::Side(ownerSide); 1.4958 + 1.4959 + tempBorder = CompareBorders(CELL_CORNER, oldBorder, aBorder, horizontal, &existingWins); 1.4960 + 1.4961 + ownerElem = tempBorder.owner; 1.4962 + ownerStyle = tempBorder.style; 1.4963 + ownerWidth = tempBorder.width; 1.4964 + ownerColor = tempBorder.color; 1.4965 + if (existingWins) { // existing corner is dominant 1.4966 + if (::Perpendicular(mozilla::css::Side(ownerSide), aSide)) { 1.4967 + // see if the new sub info replaces the old 1.4968 + BCCellBorder subBorder; 1.4969 + subBorder.owner = (BCBorderOwner) subElem; 1.4970 + subBorder.style = subStyle; 1.4971 + subBorder.width = subWidth; 1.4972 + subBorder.color = 0; // we are not interested in subBorder color 1.4973 + bool firstWins; 1.4974 + 1.4975 + tempBorder = CompareBorders(CELL_CORNER, subBorder, aBorder, horizontal, &firstWins); 1.4976 + 1.4977 + subElem = tempBorder.owner; 1.4978 + subStyle = tempBorder.style; 1.4979 + subWidth = tempBorder.width; 1.4980 + if (!firstWins) { 1.4981 + subSide = aSide; 1.4982 + } 1.4983 + } 1.4984 + } 1.4985 + else { // input args are dominant 1.4986 + ownerSide = aSide; 1.4987 + if (::Perpendicular(oldSide, mozilla::css::Side(ownerSide))) { 1.4988 + subElem = oldBorder.owner; 1.4989 + subStyle = oldBorder.style; 1.4990 + subWidth = oldBorder.width; 1.4991 + subSide = oldSide; 1.4992 + } 1.4993 + } 1.4994 + if (aBorder.width > 0) { 1.4995 + numSegs++; 1.4996 + if (!hasDashDot && ((NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) || 1.4997 + (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style))) { 1.4998 + hasDashDot = 1; 1.4999 + } 1.5000 + } 1.5001 + 1.5002 + // bevel the corner if only two perpendicular non dashed/dotted segments enter the corner 1.5003 + bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot); 1.5004 + } 1.5005 +} 1.5006 + 1.5007 +struct BCCorners 1.5008 +{ 1.5009 + BCCorners(int32_t aNumCorners, 1.5010 + int32_t aStartIndex); 1.5011 + 1.5012 + ~BCCorners() { delete [] corners; } 1.5013 + 1.5014 + BCCornerInfo& operator [](int32_t i) const 1.5015 + { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error"); 1.5016 + return corners[clamped(i, startIndex, endIndex) - startIndex]; } 1.5017 + 1.5018 + int32_t startIndex; 1.5019 + int32_t endIndex; 1.5020 + BCCornerInfo* corners; 1.5021 +}; 1.5022 + 1.5023 +BCCorners::BCCorners(int32_t aNumCorners, 1.5024 + int32_t aStartIndex) 1.5025 +{ 1.5026 + NS_ASSERTION((aNumCorners > 0) && (aStartIndex >= 0), "program error"); 1.5027 + startIndex = aStartIndex; 1.5028 + endIndex = aStartIndex + aNumCorners - 1; 1.5029 + corners = new BCCornerInfo[aNumCorners]; 1.5030 +} 1.5031 + 1.5032 + 1.5033 +struct BCCellBorders 1.5034 +{ 1.5035 + BCCellBorders(int32_t aNumBorders, 1.5036 + int32_t aStartIndex); 1.5037 + 1.5038 + ~BCCellBorders() { delete [] borders; } 1.5039 + 1.5040 + BCCellBorder& operator [](int32_t i) const 1.5041 + { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error"); 1.5042 + return borders[clamped(i, startIndex, endIndex) - startIndex]; } 1.5043 + 1.5044 + int32_t startIndex; 1.5045 + int32_t endIndex; 1.5046 + BCCellBorder* borders; 1.5047 +}; 1.5048 + 1.5049 +BCCellBorders::BCCellBorders(int32_t aNumBorders, 1.5050 + int32_t aStartIndex) 1.5051 +{ 1.5052 + NS_ASSERTION((aNumBorders > 0) && (aStartIndex >= 0), "program error"); 1.5053 + startIndex = aStartIndex; 1.5054 + endIndex = aStartIndex + aNumBorders - 1; 1.5055 + borders = new BCCellBorder[aNumBorders]; 1.5056 +} 1.5057 + 1.5058 +// this function sets the new border properties and returns true if the border 1.5059 +// segment will start a new segment and not be accumulated into the previous 1.5060 +// segment. 1.5061 +static bool 1.5062 +SetBorder(const BCCellBorder& aNewBorder, 1.5063 + BCCellBorder& aBorder) 1.5064 +{ 1.5065 + bool changed = (aNewBorder.style != aBorder.style) || 1.5066 + (aNewBorder.width != aBorder.width) || 1.5067 + (aNewBorder.color != aBorder.color); 1.5068 + aBorder.color = aNewBorder.color; 1.5069 + aBorder.width = aNewBorder.width; 1.5070 + aBorder.style = aNewBorder.style; 1.5071 + aBorder.owner = aNewBorder.owner; 1.5072 + 1.5073 + return changed; 1.5074 +} 1.5075 + 1.5076 +// this function will set the horizontal border. It will return true if the 1.5077 +// existing segment will not be continued. Having a vertical owner of a corner 1.5078 +// should also start a new segment. 1.5079 +static bool 1.5080 +SetHorBorder(const BCCellBorder& aNewBorder, 1.5081 + const BCCornerInfo& aCorner, 1.5082 + BCCellBorder& aBorder) 1.5083 +{ 1.5084 + bool startSeg = ::SetBorder(aNewBorder, aBorder); 1.5085 + if (!startSeg) { 1.5086 + startSeg = ((NS_SIDE_LEFT != aCorner.ownerSide) && (NS_SIDE_RIGHT != aCorner.ownerSide)); 1.5087 + } 1.5088 + return startSeg; 1.5089 +} 1.5090 + 1.5091 +// Make the damage area larger on the top and bottom by at least one row and on the left and right 1.5092 +// at least one column. This is done so that adjacent elements are part of the border calculations. 1.5093 +// The extra segments and borders outside the actual damage area will not be updated in the cell map, 1.5094 +// because they in turn would need info from adjacent segments outside the damage area to be accurate. 1.5095 +void 1.5096 +nsTableFrame::ExpandBCDamageArea(nsIntRect& aRect) const 1.5097 +{ 1.5098 + int32_t numRows = GetRowCount(); 1.5099 + int32_t numCols = GetColCount(); 1.5100 + 1.5101 + int32_t dStartX = aRect.x; 1.5102 + int32_t dEndX = aRect.XMost() - 1; 1.5103 + int32_t dStartY = aRect.y; 1.5104 + int32_t dEndY = aRect.YMost() - 1; 1.5105 + 1.5106 + // expand the damage area in each direction 1.5107 + if (dStartX > 0) { 1.5108 + dStartX--; 1.5109 + } 1.5110 + if (dEndX < (numCols - 1)) { 1.5111 + dEndX++; 1.5112 + } 1.5113 + if (dStartY > 0) { 1.5114 + dStartY--; 1.5115 + } 1.5116 + if (dEndY < (numRows - 1)) { 1.5117 + dEndY++; 1.5118 + } 1.5119 + // Check the damage area so that there are no cells spanning in or out. If there are any then 1.5120 + // make the damage area as big as the table, similarly to the way the cell map decides whether 1.5121 + // to rebuild versus expand. This could be optimized to expand to the smallest area that contains 1.5122 + // no spanners, but it may not be worth the effort in general, and it would need to be done in the 1.5123 + // cell map as well. 1.5124 + bool haveSpanner = false; 1.5125 + if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) { 1.5126 + nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0(); 1.5127 + // Get the ordered row groups 1.5128 + RowGroupArray rowGroups; 1.5129 + OrderRowGroups(rowGroups); 1.5130 + 1.5131 + // Scope outside loop to be used as hint. 1.5132 + nsCellMap* cellMap = nullptr; 1.5133 + for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { 1.5134 + nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; 1.5135 + int32_t rgStartY = rgFrame->GetStartRowIndex(); 1.5136 + int32_t rgEndY = rgStartY + rgFrame->GetRowCount() - 1; 1.5137 + if (dEndY < rgStartY) 1.5138 + break; 1.5139 + cellMap = tableCellMap->GetMapFor(rgFrame, cellMap); 1.5140 + if (!cellMap) ABORT0(); 1.5141 + // check for spanners from above and below 1.5142 + if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) { 1.5143 + if (uint32_t(dStartY - rgStartY) >= cellMap->mRows.Length()) 1.5144 + ABORT0(); 1.5145 + const nsCellMap::CellDataArray& row = 1.5146 + cellMap->mRows[dStartY - rgStartY]; 1.5147 + for (int32_t x = dStartX; x <= dEndX; x++) { 1.5148 + CellData* cellData = row.SafeElementAt(x); 1.5149 + if (cellData && (cellData->IsRowSpan())) { 1.5150 + haveSpanner = true; 1.5151 + break; 1.5152 + } 1.5153 + } 1.5154 + if (dEndY < rgEndY) { 1.5155 + if (uint32_t(dEndY + 1 - rgStartY) >= cellMap->mRows.Length()) 1.5156 + ABORT0(); 1.5157 + const nsCellMap::CellDataArray& row2 = 1.5158 + cellMap->mRows[dEndY + 1 - rgStartY]; 1.5159 + for (int32_t x = dStartX; x <= dEndX; x++) { 1.5160 + CellData* cellData = row2.SafeElementAt(x); 1.5161 + if (cellData && (cellData->IsRowSpan())) { 1.5162 + haveSpanner = true; 1.5163 + break; 1.5164 + } 1.5165 + } 1.5166 + } 1.5167 + } 1.5168 + // check for spanners on the left and right 1.5169 + int32_t iterStartY = -1; 1.5170 + int32_t iterEndY = -1; 1.5171 + if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) { 1.5172 + // the damage area starts in the row group 1.5173 + iterStartY = dStartY; 1.5174 + iterEndY = std::min(dEndY, rgEndY); 1.5175 + } 1.5176 + else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) { 1.5177 + // the damage area ends in the row group 1.5178 + iterStartY = rgStartY; 1.5179 + iterEndY = dEndY; 1.5180 + } 1.5181 + else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) { 1.5182 + // the damage area contains the row group 1.5183 + iterStartY = rgStartY; 1.5184 + iterEndY = rgEndY; 1.5185 + } 1.5186 + if ((iterStartY >= 0) && (iterEndY >= 0)) { 1.5187 + for (int32_t y = iterStartY; y <= iterEndY; y++) { 1.5188 + if (uint32_t(y - rgStartY) >= cellMap->mRows.Length()) 1.5189 + ABORT0(); 1.5190 + const nsCellMap::CellDataArray& row = 1.5191 + cellMap->mRows[y - rgStartY]; 1.5192 + CellData* cellData = row.SafeElementAt(dStartX); 1.5193 + if (cellData && (cellData->IsColSpan())) { 1.5194 + haveSpanner = true; 1.5195 + break; 1.5196 + } 1.5197 + if (dEndX < (numCols - 1)) { 1.5198 + cellData = row.SafeElementAt(dEndX + 1); 1.5199 + if (cellData && (cellData->IsColSpan())) { 1.5200 + haveSpanner = true; 1.5201 + break; 1.5202 + } 1.5203 + } 1.5204 + } 1.5205 + } 1.5206 + } 1.5207 + } 1.5208 + if (haveSpanner) { 1.5209 + // make the damage area the whole table 1.5210 + aRect.x = 0; 1.5211 + aRect.y = 0; 1.5212 + aRect.width = numCols; 1.5213 + aRect.height = numRows; 1.5214 + } 1.5215 + else { 1.5216 + aRect.x = dStartX; 1.5217 + aRect.y = dStartY; 1.5218 + aRect.width = 1 + dEndX - dStartX; 1.5219 + aRect.height = 1 + dEndY - dStartY; 1.5220 + } 1.5221 +} 1.5222 + 1.5223 + 1.5224 +#define ADJACENT true 1.5225 +#define HORIZONTAL true 1.5226 + 1.5227 +void 1.5228 +BCMapCellInfo::SetTableTopLeftContBCBorder() 1.5229 +{ 1.5230 + BCCellBorder currentBorder; 1.5231 + //calculate continuous top first row & rowgroup border: special case 1.5232 + //because it must include the table in the collapse 1.5233 + if (mTopRow) { 1.5234 + currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup, 1.5235 + mTopRow, nullptr, mTableIsLTR, 1.5236 + NS_SIDE_TOP, !ADJACENT); 1.5237 + mTopRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width); 1.5238 + } 1.5239 + if (mCgAtRight && mColGroup) { 1.5240 + //calculate continuous top colgroup border once per colgroup 1.5241 + currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup, 1.5242 + mTopRow, nullptr, mTableIsLTR, 1.5243 + NS_SIDE_TOP, !ADJACENT); 1.5244 + mColGroup->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width); 1.5245 + } 1.5246 + if (0 == mColIndex) { 1.5247 + currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, nullptr, 1.5248 + nullptr, nullptr, mTableIsLTR, NS_SIDE_LEFT, 1.5249 + !ADJACENT); 1.5250 + mTableFrame->SetContinuousLeftBCBorderWidth(currentBorder.width); 1.5251 + } 1.5252 +} 1.5253 + 1.5254 +void 1.5255 +BCMapCellInfo::SetRowGroupLeftContBCBorder() 1.5256 +{ 1.5257 + BCCellBorder currentBorder; 1.5258 + //get row group continuous borders 1.5259 + if (mRgAtBottom && mRowGroup) { //once per row group, so check for bottom 1.5260 + currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup, 1.5261 + nullptr, nullptr, mTableIsLTR, NS_SIDE_LEFT, 1.5262 + !ADJACENT); 1.5263 + mRowGroup->SetContinuousBCBorderWidth(mStartSide, currentBorder.width); 1.5264 + } 1.5265 +} 1.5266 + 1.5267 +void 1.5268 +BCMapCellInfo::SetRowGroupRightContBCBorder() 1.5269 +{ 1.5270 + BCCellBorder currentBorder; 1.5271 + //get row group continuous borders 1.5272 + if (mRgAtBottom && mRowGroup) { //once per mRowGroup, so check for bottom 1.5273 + currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup, 1.5274 + nullptr, nullptr, mTableIsLTR, NS_SIDE_RIGHT, 1.5275 + ADJACENT); 1.5276 + mRowGroup->SetContinuousBCBorderWidth(mEndSide, currentBorder.width); 1.5277 + } 1.5278 +} 1.5279 + 1.5280 +void 1.5281 +BCMapCellInfo::SetColumnTopRightContBCBorder() 1.5282 +{ 1.5283 + BCCellBorder currentBorder; 1.5284 + //calculate column continuous borders 1.5285 + //we only need to do this once, so we'll do it only on the first row 1.5286 + currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame, 1.5287 + mCurrentColFrame, mRowGroup, mTopRow, nullptr, 1.5288 + mTableIsLTR, NS_SIDE_TOP, !ADJACENT); 1.5289 + ((nsTableColFrame*) mCurrentColFrame)->SetContinuousBCBorderWidth(NS_SIDE_TOP, 1.5290 + currentBorder.width); 1.5291 + if (mNumTableCols == GetCellEndColIndex() + 1) { 1.5292 + currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame, 1.5293 + mCurrentColFrame, nullptr, nullptr, nullptr, 1.5294 + mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT); 1.5295 + } 1.5296 + else { 1.5297 + currentBorder = CompareBorders(nullptr, mCurrentColGroupFrame, 1.5298 + mCurrentColFrame, nullptr,nullptr, nullptr, 1.5299 + mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT); 1.5300 + } 1.5301 + mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_RIGHT, 1.5302 + currentBorder.width); 1.5303 +} 1.5304 + 1.5305 +void 1.5306 +BCMapCellInfo::SetColumnBottomContBCBorder() 1.5307 +{ 1.5308 + BCCellBorder currentBorder; 1.5309 + //get col continuous border 1.5310 + currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame, 1.5311 + mCurrentColFrame, mRowGroup, mBottomRow, 1.5312 + nullptr, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT); 1.5313 + mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, 1.5314 + currentBorder.width); 1.5315 +} 1.5316 + 1.5317 +void 1.5318 +BCMapCellInfo::SetColGroupBottomContBCBorder() 1.5319 +{ 1.5320 + BCCellBorder currentBorder; 1.5321 + if (mColGroup) { 1.5322 + currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup, 1.5323 + mBottomRow, nullptr, mTableIsLTR, 1.5324 + NS_SIDE_BOTTOM, ADJACENT); 1.5325 + mColGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width); 1.5326 + } 1.5327 +} 1.5328 + 1.5329 +void 1.5330 +BCMapCellInfo::SetRowGroupBottomContBCBorder() 1.5331 +{ 1.5332 + BCCellBorder currentBorder; 1.5333 + if (mRowGroup) { 1.5334 + currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup, 1.5335 + mBottomRow, nullptr, mTableIsLTR, 1.5336 + NS_SIDE_BOTTOM, ADJACENT); 1.5337 + mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width); 1.5338 + } 1.5339 +} 1.5340 + 1.5341 +void 1.5342 +BCMapCellInfo::SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup, 1.5343 + nsTableRowFrame* aNextRow) 1.5344 +{ 1.5345 + BCCellBorder currentBorder, adjacentBorder; 1.5346 + 1.5347 + const nsIFrame* rowgroup = (mRgAtBottom) ? mRowGroup : nullptr; 1.5348 + currentBorder = CompareBorders(nullptr, nullptr, nullptr, rowgroup, mBottomRow, 1.5349 + nullptr, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT); 1.5350 + 1.5351 + adjacentBorder = CompareBorders(nullptr, nullptr, nullptr, aNextRowGroup, 1.5352 + aNextRow, nullptr, mTableIsLTR, NS_SIDE_TOP, 1.5353 + !ADJACENT); 1.5354 + currentBorder = CompareBorders(false, currentBorder, adjacentBorder, 1.5355 + HORIZONTAL); 1.5356 + if (aNextRow) { 1.5357 + aNextRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width); 1.5358 + } 1.5359 + if (mRgAtBottom && mRowGroup) { 1.5360 + mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width); 1.5361 + } 1.5362 +} 1.5363 + 1.5364 +void 1.5365 +BCMapCellInfo::SetRowLeftContBCBorder() 1.5366 +{ 1.5367 + //get row continuous borders 1.5368 + if (mCurrentRowFrame) { 1.5369 + BCCellBorder currentBorder; 1.5370 + currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup, 1.5371 + mCurrentRowFrame, nullptr, mTableIsLTR, 1.5372 + NS_SIDE_LEFT, !ADJACENT); 1.5373 + mCurrentRowFrame->SetContinuousBCBorderWidth(mStartSide, 1.5374 + currentBorder.width); 1.5375 + } 1.5376 +} 1.5377 + 1.5378 +void 1.5379 +BCMapCellInfo::SetRowRightContBCBorder() 1.5380 +{ 1.5381 + if (mCurrentRowFrame) { 1.5382 + BCCellBorder currentBorder; 1.5383 + currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup, 1.5384 + mCurrentRowFrame, nullptr, mTableIsLTR, 1.5385 + NS_SIDE_RIGHT, ADJACENT); 1.5386 + mCurrentRowFrame->SetContinuousBCBorderWidth(mEndSide, 1.5387 + currentBorder.width); 1.5388 + } 1.5389 +} 1.5390 +void 1.5391 +BCMapCellInfo::SetTableTopBorderWidth(BCPixelSize aWidth) 1.5392 +{ 1.5393 + mTableBCData->mTopBorderWidth = std::max(mTableBCData->mTopBorderWidth, aWidth); 1.5394 +} 1.5395 + 1.5396 +void 1.5397 +BCMapCellInfo::SetTableLeftBorderWidth(int32_t aRowY, BCPixelSize aWidth) 1.5398 +{ 1.5399 + // update the left/right first cell border 1.5400 + if (aRowY == 0) { 1.5401 + if (mTableIsLTR) { 1.5402 + mTableBCData->mLeftCellBorderWidth = aWidth; 1.5403 + } 1.5404 + else { 1.5405 + mTableBCData->mRightCellBorderWidth = aWidth; 1.5406 + } 1.5407 + } 1.5408 + mTableBCData->mLeftBorderWidth = std::max(mTableBCData->mLeftBorderWidth, 1.5409 + aWidth); 1.5410 +} 1.5411 + 1.5412 +void 1.5413 +BCMapCellInfo::SetTableRightBorderWidth(int32_t aRowY, BCPixelSize aWidth) 1.5414 +{ 1.5415 + // update the left/right first cell border 1.5416 + if (aRowY == 0) { 1.5417 + if (mTableIsLTR) { 1.5418 + mTableBCData->mRightCellBorderWidth = aWidth; 1.5419 + } 1.5420 + else { 1.5421 + mTableBCData->mLeftCellBorderWidth = aWidth; 1.5422 + } 1.5423 + } 1.5424 + mTableBCData->mRightBorderWidth = std::max(mTableBCData->mRightBorderWidth, 1.5425 + aWidth); 1.5426 +} 1.5427 + 1.5428 +void 1.5429 +BCMapCellInfo::SetRightBorderWidths(BCPixelSize aWidth) 1.5430 +{ 1.5431 + // update the borders of the cells and cols affected 1.5432 + if (mCell) { 1.5433 + mCell->SetBorderWidth(mEndSide, std::max(aWidth, 1.5434 + mCell->GetBorderWidth(mEndSide))); 1.5435 + } 1.5436 + if (mRightCol) { 1.5437 + BCPixelSize half = BC_BORDER_LEFT_HALF(aWidth); 1.5438 + mRightCol->SetRightBorderWidth(std::max(nscoord(half), 1.5439 + mRightCol->GetRightBorderWidth())); 1.5440 + } 1.5441 +} 1.5442 + 1.5443 +void 1.5444 +BCMapCellInfo::SetBottomBorderWidths(BCPixelSize aWidth) 1.5445 +{ 1.5446 + // update the borders of the affected cells and rows 1.5447 + if (mCell) { 1.5448 + mCell->SetBorderWidth(NS_SIDE_BOTTOM, std::max(aWidth, 1.5449 + mCell->GetBorderWidth(NS_SIDE_BOTTOM))); 1.5450 + } 1.5451 + if (mBottomRow) { 1.5452 + BCPixelSize half = BC_BORDER_TOP_HALF(aWidth); 1.5453 + mBottomRow->SetBottomBCBorderWidth(std::max(nscoord(half), 1.5454 + mBottomRow->GetBottomBCBorderWidth())); 1.5455 + } 1.5456 +} 1.5457 +void 1.5458 +BCMapCellInfo::SetTopBorderWidths(BCPixelSize aWidth) 1.5459 +{ 1.5460 + if (mCell) { 1.5461 + mCell->SetBorderWidth(NS_SIDE_TOP, std::max(aWidth, 1.5462 + mCell->GetBorderWidth(NS_SIDE_TOP))); 1.5463 + } 1.5464 + if (mTopRow) { 1.5465 + BCPixelSize half = BC_BORDER_BOTTOM_HALF(aWidth); 1.5466 + mTopRow->SetTopBCBorderWidth(std::max(nscoord(half), 1.5467 + mTopRow->GetTopBCBorderWidth())); 1.5468 + } 1.5469 +} 1.5470 +void 1.5471 +BCMapCellInfo::SetLeftBorderWidths(BCPixelSize aWidth) 1.5472 +{ 1.5473 + if (mCell) { 1.5474 + mCell->SetBorderWidth(mStartSide, std::max(aWidth, 1.5475 + mCell->GetBorderWidth(mStartSide))); 1.5476 + } 1.5477 + if (mLeftCol) { 1.5478 + BCPixelSize half = BC_BORDER_RIGHT_HALF(aWidth); 1.5479 + mLeftCol->SetLeftBorderWidth(std::max(nscoord(half), 1.5480 + mLeftCol->GetLeftBorderWidth())); 1.5481 + } 1.5482 +} 1.5483 + 1.5484 +void 1.5485 +BCMapCellInfo::SetTableBottomBorderWidth(BCPixelSize aWidth) 1.5486 +{ 1.5487 + mTableBCData->mBottomBorderWidth = std::max(mTableBCData->mBottomBorderWidth, 1.5488 + aWidth); 1.5489 +} 1.5490 + 1.5491 +void 1.5492 +BCMapCellInfo::SetColumn(int32_t aColX) 1.5493 +{ 1.5494 + mCurrentColFrame = mTableFrame->GetColFrame(aColX); 1.5495 + if (!mCurrentColFrame) { 1.5496 + NS_ERROR("null mCurrentColFrame"); 1.5497 + } 1.5498 + mCurrentColGroupFrame = static_cast<nsTableColGroupFrame*> 1.5499 + (mCurrentColFrame->GetParent()); 1.5500 + if (!mCurrentColGroupFrame) { 1.5501 + NS_ERROR("null mCurrentColGroupFrame"); 1.5502 + } 1.5503 +} 1.5504 + 1.5505 +void 1.5506 +BCMapCellInfo::IncrementRow(bool aResetToTopRowOfCell) 1.5507 +{ 1.5508 + mCurrentRowFrame = (aResetToTopRowOfCell) ? mTopRow : 1.5509 + mCurrentRowFrame->GetNextRow(); 1.5510 +} 1.5511 + 1.5512 +BCCellBorder 1.5513 +BCMapCellInfo::GetTopEdgeBorder() 1.5514 +{ 1.5515 + return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame, 1.5516 + mRowGroup, mTopRow, mCell, mTableIsLTR, NS_SIDE_TOP, 1.5517 + !ADJACENT); 1.5518 +} 1.5519 + 1.5520 +BCCellBorder 1.5521 +BCMapCellInfo::GetBottomEdgeBorder() 1.5522 +{ 1.5523 + return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame, 1.5524 + mRowGroup, mBottomRow, mCell, mTableIsLTR, 1.5525 + NS_SIDE_BOTTOM, ADJACENT); 1.5526 +} 1.5527 +BCCellBorder 1.5528 +BCMapCellInfo::GetLeftEdgeBorder() 1.5529 +{ 1.5530 + return CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup, 1.5531 + mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_LEFT, 1.5532 + !ADJACENT); 1.5533 +} 1.5534 +BCCellBorder 1.5535 +BCMapCellInfo::GetRightEdgeBorder() 1.5536 +{ 1.5537 + return CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup, 1.5538 + mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_RIGHT, 1.5539 + ADJACENT); 1.5540 +} 1.5541 +BCCellBorder 1.5542 +BCMapCellInfo::GetRightInternalBorder() 1.5543 +{ 1.5544 + const nsIFrame* cg = (mCgAtRight) ? mColGroup : nullptr; 1.5545 + return CompareBorders(nullptr, cg, mRightCol, nullptr, nullptr, mCell, 1.5546 + mTableIsLTR, NS_SIDE_RIGHT, ADJACENT); 1.5547 +} 1.5548 + 1.5549 +BCCellBorder 1.5550 +BCMapCellInfo::GetLeftInternalBorder() 1.5551 +{ 1.5552 + const nsIFrame* cg = (mCgAtLeft) ? mColGroup : nullptr; 1.5553 + return CompareBorders(nullptr, cg, mLeftCol, nullptr, nullptr, mCell, 1.5554 + mTableIsLTR, NS_SIDE_LEFT, !ADJACENT); 1.5555 +} 1.5556 + 1.5557 +BCCellBorder 1.5558 +BCMapCellInfo::GetBottomInternalBorder() 1.5559 +{ 1.5560 + const nsIFrame* rg = (mRgAtBottom) ? mRowGroup : nullptr; 1.5561 + return CompareBorders(nullptr, nullptr, nullptr, rg, mBottomRow, mCell, 1.5562 + mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT); 1.5563 +} 1.5564 + 1.5565 +BCCellBorder 1.5566 +BCMapCellInfo::GetTopInternalBorder() 1.5567 +{ 1.5568 + const nsIFrame* rg = (mRgAtTop) ? mRowGroup : nullptr; 1.5569 + return CompareBorders(nullptr, nullptr, nullptr, rg, mTopRow, mCell, 1.5570 + mTableIsLTR, NS_SIDE_TOP, !ADJACENT); 1.5571 +} 1.5572 + 1.5573 +/* Here is the order for storing border edges in the cell map as a cell is processed. There are 1.5574 + n=colspan top and bottom border edges per cell and n=rowspan left and right border edges per cell. 1.5575 + 1.5576 + 1) On the top edge of the table, store the top edge. Never store the top edge otherwise, since 1.5577 + a bottom edge from a cell above will take care of it. 1.5578 + 2) On the left edge of the table, store the left edge. Never store the left edge othewise, since 1.5579 + a right edge from a cell to the left will take care of it. 1.5580 + 3) Store the right edge (or edges if a row span) 1.5581 + 4) Store the bottom edge (or edges if a col span) 1.5582 + 1.5583 + Since corners are computed with only an array of BCCornerInfo indexed by the number-of-cols, corner 1.5584 + calculations are somewhat complicated. Using an array with number-of-rows * number-of-col entries 1.5585 + would simplify this, but at an extra in memory cost of nearly 12 bytes per cell map entry. Collapsing 1.5586 + borders already have about an extra 8 byte per cell map entry overhead (this could be 1.5587 + reduced to 4 bytes if we are willing to not store border widths in nsTableCellFrame), Here are the 1.5588 + rules in priority order for storing cornes in the cell map as a cell is processed. top-left means the 1.5589 + left endpoint of the border edge on the top of the cell. There are n=colspan top and bottom border 1.5590 + edges per cell and n=rowspan left and right border edges per cell. 1.5591 + 1.5592 + 1) On the top edge of the table, store the top-left corner, unless on the left edge of the table. 1.5593 + Never store the top-right corner, since it will get stored as a right-top corner. 1.5594 + 2) On the left edge of the table, store the left-top corner. Never store the left-bottom corner, 1.5595 + since it will get stored as a bottom-left corner. 1.5596 + 3) Store the right-top corner if (a) it is the top right corner of the table or (b) it is not on 1.5597 + the top edge of the table. Never store the right-bottom corner since it will get stored as a 1.5598 + bottom-right corner. 1.5599 + 4) Store the bottom-right corner, if it is the bottom right corner of the table. Never store it 1.5600 + otherwise, since it will get stored as either a right-top corner by a cell below or 1.5601 + a bottom-left corner from a cell to the right. 1.5602 + 5) Store the bottom-left corner, if (a) on the bottom edge of the table or (b) if the left edge hits 1.5603 + the top side of a colspan in its interior. Never store the corner otherwise, since it will 1.5604 + get stored as a right-top corner by a cell from below. 1.5605 + 1.5606 + XXX the BC-RTL hack - The correct fix would be a rewrite as described in bug 203686. 1.5607 + In order to draw borders in rtl conditions somehow correct, the existing structure which relies 1.5608 + heavily on the assumption that the next cell sibling will be on the right side, has been modified. 1.5609 + We flip the border during painting and during style lookup. Look for tableIsLTR for places where 1.5610 + the flipping is done. 1.5611 + */ 1.5612 + 1.5613 + 1.5614 + 1.5615 +// Calc the dominant border at every cell edge and corner within the current damage area 1.5616 +void 1.5617 +nsTableFrame::CalcBCBorders() 1.5618 +{ 1.5619 + NS_ASSERTION(IsBorderCollapse(), 1.5620 + "calling CalcBCBorders on separated-border table"); 1.5621 + nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0(); 1.5622 + int32_t numRows = GetRowCount(); 1.5623 + int32_t numCols = GetColCount(); 1.5624 + if (!numRows || !numCols) 1.5625 + return; // nothing to do 1.5626 + 1.5627 + // Get the property holding the table damage area and border widths 1.5628 + BCPropertyData* propData = GetBCProperty(); 1.5629 + if (!propData) ABORT0(); 1.5630 + 1.5631 + 1.5632 + 1.5633 + // calculate an expanded damage area 1.5634 + nsIntRect damageArea(propData->mDamageArea); 1.5635 + ExpandBCDamageArea(damageArea); 1.5636 + 1.5637 + // segments that are on the table border edges need 1.5638 + // to be initialized only once 1.5639 + bool tableBorderReset[4]; 1.5640 + for (uint32_t sideX = NS_SIDE_TOP; sideX <= NS_SIDE_LEFT; sideX++) { 1.5641 + tableBorderReset[sideX] = false; 1.5642 + } 1.5643 + 1.5644 + // vertical borders indexed in x-direction (cols) 1.5645 + BCCellBorders lastVerBorders(damageArea.width + 1, damageArea.x); 1.5646 + if (!lastVerBorders.borders) ABORT0(); 1.5647 + BCCellBorder lastTopBorder, lastBottomBorder; 1.5648 + // horizontal borders indexed in x-direction (cols) 1.5649 + BCCellBorders lastBottomBorders(damageArea.width + 1, damageArea.x); 1.5650 + if (!lastBottomBorders.borders) ABORT0(); 1.5651 + bool startSeg; 1.5652 + bool gotRowBorder = false; 1.5653 + 1.5654 + BCMapCellInfo info(this), ajaInfo(this); 1.5655 + 1.5656 + BCCellBorder currentBorder, adjacentBorder; 1.5657 + BCCorners topCorners(damageArea.width + 1, damageArea.x); 1.5658 + if (!topCorners.corners) ABORT0(); 1.5659 + BCCorners bottomCorners(damageArea.width + 1, damageArea.x); 1.5660 + if (!bottomCorners.corners) ABORT0(); 1.5661 + 1.5662 + BCMapCellIterator iter(this, damageArea); 1.5663 + for (iter.First(info); !iter.mAtEnd; iter.Next(info)) { 1.5664 + // see if lastTopBorder, lastBottomBorder need to be reset 1.5665 + if (iter.IsNewRow()) { 1.5666 + gotRowBorder = false; 1.5667 + lastTopBorder.Reset(info.mRowIndex, info.mRowSpan); 1.5668 + lastBottomBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan); 1.5669 + } 1.5670 + else if (info.mColIndex > damageArea.x) { 1.5671 + lastBottomBorder = lastBottomBorders[info.mColIndex - 1]; 1.5672 + if (info.mRowIndex > 1.5673 + (lastBottomBorder.rowIndex - lastBottomBorder.rowSpan)) { 1.5674 + // the top border's left edge butts against the middle of a rowspan 1.5675 + lastTopBorder.Reset(info.mRowIndex, info.mRowSpan); 1.5676 + } 1.5677 + if (lastBottomBorder.rowIndex > (info.GetCellEndRowIndex() + 1)) { 1.5678 + // the bottom border's left edge butts against the middle of a rowspan 1.5679 + lastBottomBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan); 1.5680 + } 1.5681 + } 1.5682 + 1.5683 + // find the dominant border considering the cell's top border and the table, 1.5684 + // row group, row if the border is at the top of the table, otherwise it was 1.5685 + // processed in a previous row 1.5686 + if (0 == info.mRowIndex) { 1.5687 + if (!tableBorderReset[NS_SIDE_TOP]) { 1.5688 + propData->mTopBorderWidth = 0; 1.5689 + tableBorderReset[NS_SIDE_TOP] = true; 1.5690 + } 1.5691 + for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex(); 1.5692 + colX++) { 1.5693 + info.SetColumn(colX); 1.5694 + currentBorder = info.GetTopEdgeBorder(); 1.5695 + // update/store the top left & top right corners of the seg 1.5696 + BCCornerInfo& tlCorner = topCorners[colX]; // top left 1.5697 + if (0 == colX) { 1.5698 + // we are on right hand side of the corner 1.5699 + tlCorner.Set(NS_SIDE_RIGHT, currentBorder); 1.5700 + } 1.5701 + else { 1.5702 + tlCorner.Update(NS_SIDE_RIGHT, currentBorder); 1.5703 + tableCellMap->SetBCBorderCorner(eTopLeft, *iter.mCellMap, 0, 0, colX, 1.5704 + mozilla::css::Side(tlCorner.ownerSide), 1.5705 + tlCorner.subWidth, 1.5706 + tlCorner.bevel); 1.5707 + } 1.5708 + topCorners[colX + 1].Set(NS_SIDE_LEFT, currentBorder); // top right 1.5709 + // update lastTopBorder and see if a new segment starts 1.5710 + startSeg = SetHorBorder(currentBorder, tlCorner, lastTopBorder); 1.5711 + // store the border segment in the cell map 1.5712 + tableCellMap->SetBCBorderEdge(NS_SIDE_TOP, *iter.mCellMap, 0, 0, colX, 1.5713 + 1, currentBorder.owner, 1.5714 + currentBorder.width, startSeg); 1.5715 + 1.5716 + info.SetTableTopBorderWidth(currentBorder.width); 1.5717 + info.SetTopBorderWidths(currentBorder.width); 1.5718 + info.SetColumnTopRightContBCBorder(); 1.5719 + } 1.5720 + info.SetTableTopLeftContBCBorder(); 1.5721 + } 1.5722 + else { 1.5723 + // see if the top border needs to be the start of a segment due to a 1.5724 + // vertical border owning the corner 1.5725 + if (info.mColIndex > 0) { 1.5726 + BCData& data = info.mCellData->mData; 1.5727 + if (!data.IsTopStart()) { 1.5728 + mozilla::css::Side cornerSide; 1.5729 + bool bevel; 1.5730 + data.GetCorner(cornerSide, bevel); 1.5731 + if ((NS_SIDE_TOP == cornerSide) || (NS_SIDE_BOTTOM == cornerSide)) { 1.5732 + data.SetTopStart(true); 1.5733 + } 1.5734 + } 1.5735 + } 1.5736 + } 1.5737 + 1.5738 + // find the dominant border considering the cell's left border and the 1.5739 + // table, col group, col if the border is at the left of the table, 1.5740 + // otherwise it was processed in a previous col 1.5741 + if (0 == info.mColIndex) { 1.5742 + if (!tableBorderReset[NS_SIDE_LEFT]) { 1.5743 + propData->mLeftBorderWidth = 0; 1.5744 + tableBorderReset[NS_SIDE_LEFT] = true; 1.5745 + } 1.5746 + info.mCurrentRowFrame = nullptr; 1.5747 + for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex(); 1.5748 + rowY++) { 1.5749 + info.IncrementRow(rowY == info.mRowIndex); 1.5750 + currentBorder = info.GetLeftEdgeBorder(); 1.5751 + BCCornerInfo& tlCorner = (0 == rowY) ? topCorners[0] : bottomCorners[0]; 1.5752 + tlCorner.Update(NS_SIDE_BOTTOM, currentBorder); 1.5753 + tableCellMap->SetBCBorderCorner(eTopLeft, *iter.mCellMap, 1.5754 + iter.mRowGroupStart, rowY, 0, 1.5755 + mozilla::css::Side(tlCorner.ownerSide), 1.5756 + tlCorner.subWidth, 1.5757 + tlCorner.bevel); 1.5758 + bottomCorners[0].Set(NS_SIDE_TOP, currentBorder); // bottom left 1.5759 + 1.5760 + // update lastVerBordersBorder and see if a new segment starts 1.5761 + startSeg = SetBorder(currentBorder, lastVerBorders[0]); 1.5762 + // store the border segment in the cell map 1.5763 + tableCellMap->SetBCBorderEdge(NS_SIDE_LEFT, *iter.mCellMap, 1.5764 + iter.mRowGroupStart, rowY, info.mColIndex, 1.5765 + 1, currentBorder.owner, 1.5766 + currentBorder.width, startSeg); 1.5767 + info.SetTableLeftBorderWidth(rowY , currentBorder.width); 1.5768 + info.SetLeftBorderWidths(currentBorder.width); 1.5769 + info.SetRowLeftContBCBorder(); 1.5770 + } 1.5771 + info.SetRowGroupLeftContBCBorder(); 1.5772 + } 1.5773 + 1.5774 + // find the dominant border considering the cell's right border, adjacent 1.5775 + // cells and the table, row group, row 1.5776 + if (info.mNumTableCols == info.GetCellEndColIndex() + 1) { 1.5777 + // touches right edge of table 1.5778 + if (!tableBorderReset[NS_SIDE_RIGHT]) { 1.5779 + propData->mRightBorderWidth = 0; 1.5780 + tableBorderReset[NS_SIDE_RIGHT] = true; 1.5781 + } 1.5782 + info.mCurrentRowFrame = nullptr; 1.5783 + for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex(); 1.5784 + rowY++) { 1.5785 + info.IncrementRow(rowY == info.mRowIndex); 1.5786 + currentBorder = info.GetRightEdgeBorder(); 1.5787 + // update/store the top right & bottom right corners 1.5788 + BCCornerInfo& trCorner = (0 == rowY) ? 1.5789 + topCorners[info.GetCellEndColIndex() + 1] : 1.5790 + bottomCorners[info.GetCellEndColIndex() + 1]; 1.5791 + trCorner.Update(NS_SIDE_BOTTOM, currentBorder); // top right 1.5792 + tableCellMap->SetBCBorderCorner(eTopRight, *iter.mCellMap, 1.5793 + iter.mRowGroupStart, rowY, 1.5794 + info.GetCellEndColIndex(), 1.5795 + mozilla::css::Side(trCorner.ownerSide), 1.5796 + trCorner.subWidth, 1.5797 + trCorner.bevel); 1.5798 + BCCornerInfo& brCorner = bottomCorners[info.GetCellEndColIndex() + 1]; 1.5799 + brCorner.Set(NS_SIDE_TOP, currentBorder); // bottom right 1.5800 + tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap, 1.5801 + iter.mRowGroupStart, rowY, 1.5802 + info.GetCellEndColIndex(), 1.5803 + mozilla::css::Side(brCorner.ownerSide), 1.5804 + brCorner.subWidth, 1.5805 + brCorner.bevel); 1.5806 + // update lastVerBorders and see if a new segment starts 1.5807 + startSeg = SetBorder(currentBorder, 1.5808 + lastVerBorders[info.GetCellEndColIndex() + 1]); 1.5809 + // store the border segment in the cell map and update cellBorders 1.5810 + tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *iter.mCellMap, 1.5811 + iter.mRowGroupStart, rowY, 1.5812 + info.GetCellEndColIndex(), 1, 1.5813 + currentBorder.owner, currentBorder.width, 1.5814 + startSeg); 1.5815 + info.SetTableRightBorderWidth(rowY, currentBorder.width); 1.5816 + info.SetRightBorderWidths(currentBorder.width); 1.5817 + info.SetRowRightContBCBorder(); 1.5818 + } 1.5819 + info.SetRowGroupRightContBCBorder(); 1.5820 + } 1.5821 + else { 1.5822 + int32_t segLength = 0; 1.5823 + BCMapCellInfo priorAjaInfo(this); 1.5824 + for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex(); 1.5825 + rowY += segLength) { 1.5826 + iter.PeekRight(info, rowY, ajaInfo); 1.5827 + currentBorder = info.GetRightInternalBorder(); 1.5828 + adjacentBorder = ajaInfo.GetLeftInternalBorder(); 1.5829 + currentBorder = CompareBorders(!CELL_CORNER, currentBorder, 1.5830 + adjacentBorder, !HORIZONTAL); 1.5831 + 1.5832 + segLength = std::max(1, ajaInfo.mRowIndex + ajaInfo.mRowSpan - rowY); 1.5833 + segLength = std::min(segLength, info.mRowIndex + info.mRowSpan - rowY); 1.5834 + 1.5835 + // update lastVerBorders and see if a new segment starts 1.5836 + startSeg = SetBorder(currentBorder, 1.5837 + lastVerBorders[info.GetCellEndColIndex() + 1]); 1.5838 + // store the border segment in the cell map and update cellBorders 1.5839 + if (info.GetCellEndColIndex() < damageArea.XMost() && 1.5840 + rowY >= damageArea.y && rowY < damageArea.YMost()) { 1.5841 + tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *iter.mCellMap, 1.5842 + iter.mRowGroupStart, rowY, 1.5843 + info.GetCellEndColIndex(), segLength, 1.5844 + currentBorder.owner, 1.5845 + currentBorder.width, startSeg); 1.5846 + info.SetRightBorderWidths(currentBorder.width); 1.5847 + ajaInfo.SetLeftBorderWidths(currentBorder.width); 1.5848 + } 1.5849 + // update the top right corner 1.5850 + bool hitsSpanOnRight = (rowY > ajaInfo.mRowIndex) && 1.5851 + (rowY < ajaInfo.mRowIndex + ajaInfo.mRowSpan); 1.5852 + BCCornerInfo* trCorner = ((0 == rowY) || hitsSpanOnRight) ? 1.5853 + &topCorners[info.GetCellEndColIndex() + 1] : 1.5854 + &bottomCorners[info.GetCellEndColIndex() + 1]; 1.5855 + trCorner->Update(NS_SIDE_BOTTOM, currentBorder); 1.5856 + // if this is not the first time through, 1.5857 + // consider the segment to the right 1.5858 + if (rowY != info.mRowIndex) { 1.5859 + currentBorder = priorAjaInfo.GetBottomInternalBorder(); 1.5860 + adjacentBorder = ajaInfo.GetTopInternalBorder(); 1.5861 + currentBorder = CompareBorders(!CELL_CORNER, currentBorder, 1.5862 + adjacentBorder, HORIZONTAL); 1.5863 + trCorner->Update(NS_SIDE_RIGHT, currentBorder); 1.5864 + } 1.5865 + // store the top right corner in the cell map 1.5866 + if (info.GetCellEndColIndex() < damageArea.XMost() && 1.5867 + rowY >= damageArea.y) { 1.5868 + if (0 != rowY) { 1.5869 + tableCellMap->SetBCBorderCorner(eTopRight, *iter.mCellMap, 1.5870 + iter.mRowGroupStart, rowY, 1.5871 + info.GetCellEndColIndex(), 1.5872 + mozilla::css::Side(trCorner->ownerSide), 1.5873 + trCorner->subWidth, 1.5874 + trCorner->bevel); 1.5875 + } 1.5876 + // store any corners this cell spans together with the aja cell 1.5877 + for (int32_t rX = rowY + 1; rX < rowY + segLength; rX++) { 1.5878 + tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap, 1.5879 + iter.mRowGroupStart, rX, 1.5880 + info.GetCellEndColIndex(), 1.5881 + mozilla::css::Side(trCorner->ownerSide), 1.5882 + trCorner->subWidth, false); 1.5883 + } 1.5884 + } 1.5885 + // update bottom right corner, topCorners, bottomCorners 1.5886 + hitsSpanOnRight = (rowY + segLength < 1.5887 + ajaInfo.mRowIndex + ajaInfo.mRowSpan); 1.5888 + BCCornerInfo& brCorner = (hitsSpanOnRight) ? 1.5889 + topCorners[info.GetCellEndColIndex() + 1] : 1.5890 + bottomCorners[info.GetCellEndColIndex() + 1]; 1.5891 + brCorner.Set(NS_SIDE_TOP, currentBorder); 1.5892 + priorAjaInfo = ajaInfo; 1.5893 + } 1.5894 + } 1.5895 + for (int32_t colX = info.mColIndex + 1; colX <= info.GetCellEndColIndex(); 1.5896 + colX++) { 1.5897 + lastVerBorders[colX].Reset(0,1); 1.5898 + } 1.5899 + 1.5900 + // find the dominant border considering the cell's bottom border, adjacent 1.5901 + // cells and the table, row group, row 1.5902 + if (info.mNumTableRows == info.GetCellEndRowIndex() + 1) { 1.5903 + // touches bottom edge of table 1.5904 + if (!tableBorderReset[NS_SIDE_BOTTOM]) { 1.5905 + propData->mBottomBorderWidth = 0; 1.5906 + tableBorderReset[NS_SIDE_BOTTOM] = true; 1.5907 + } 1.5908 + for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex(); 1.5909 + colX++) { 1.5910 + info.SetColumn(colX); 1.5911 + currentBorder = info.GetBottomEdgeBorder(); 1.5912 + // update/store the bottom left & bottom right corners 1.5913 + BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left 1.5914 + blCorner.Update(NS_SIDE_RIGHT, currentBorder); 1.5915 + tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap, 1.5916 + iter.mRowGroupStart, 1.5917 + info.GetCellEndRowIndex(), 1.5918 + colX, 1.5919 + mozilla::css::Side(blCorner.ownerSide), 1.5920 + blCorner.subWidth, blCorner.bevel); 1.5921 + BCCornerInfo& brCorner = bottomCorners[colX + 1]; // bottom right 1.5922 + brCorner.Update(NS_SIDE_LEFT, currentBorder); 1.5923 + if (info.mNumTableCols == colX + 1) { // lower right corner of the table 1.5924 + tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap, 1.5925 + iter.mRowGroupStart, 1.5926 + info.GetCellEndRowIndex(),colX, 1.5927 + mozilla::css::Side(brCorner.ownerSide), 1.5928 + brCorner.subWidth, 1.5929 + brCorner.bevel, true); 1.5930 + } 1.5931 + // update lastBottomBorder and see if a new segment starts 1.5932 + startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder); 1.5933 + if (!startSeg) { 1.5934 + // make sure that we did not compare apples to oranges i.e. the 1.5935 + // current border should be a continuation of the lastBottomBorder, 1.5936 + // as it is a bottom border 1.5937 + // add 1 to the info.GetCellEndRowIndex() 1.5938 + startSeg = (lastBottomBorder.rowIndex != 1.5939 + (info.GetCellEndRowIndex() + 1)); 1.5940 + } 1.5941 + // store the border segment in the cell map and update cellBorders 1.5942 + tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *iter.mCellMap, 1.5943 + iter.mRowGroupStart, 1.5944 + info.GetCellEndRowIndex(), 1.5945 + colX, 1, currentBorder.owner, 1.5946 + currentBorder.width, startSeg); 1.5947 + // update lastBottomBorders 1.5948 + lastBottomBorder.rowIndex = info.GetCellEndRowIndex() + 1; 1.5949 + lastBottomBorder.rowSpan = info.mRowSpan; 1.5950 + lastBottomBorders[colX] = lastBottomBorder; 1.5951 + 1.5952 + info.SetBottomBorderWidths(currentBorder.width); 1.5953 + info.SetTableBottomBorderWidth(currentBorder.width); 1.5954 + info.SetColumnBottomContBCBorder(); 1.5955 + } 1.5956 + info.SetRowGroupBottomContBCBorder(); 1.5957 + info.SetColGroupBottomContBCBorder(); 1.5958 + } 1.5959 + else { 1.5960 + int32_t segLength = 0; 1.5961 + for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex(); 1.5962 + colX += segLength) { 1.5963 + iter.PeekBottom(info, colX, ajaInfo); 1.5964 + currentBorder = info.GetBottomInternalBorder(); 1.5965 + adjacentBorder = ajaInfo.GetTopInternalBorder(); 1.5966 + currentBorder = CompareBorders(!CELL_CORNER, currentBorder, 1.5967 + adjacentBorder, HORIZONTAL); 1.5968 + segLength = std::max(1, ajaInfo.mColIndex + ajaInfo.mColSpan - colX); 1.5969 + segLength = std::min(segLength, info.mColIndex + info.mColSpan - colX); 1.5970 + 1.5971 + // update, store the bottom left corner 1.5972 + BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left 1.5973 + bool hitsSpanBelow = (colX > ajaInfo.mColIndex) && 1.5974 + (colX < ajaInfo.mColIndex + ajaInfo.mColSpan); 1.5975 + bool update = true; 1.5976 + if ((colX == info.mColIndex) && (colX > damageArea.x)) { 1.5977 + int32_t prevRowIndex = lastBottomBorders[colX - 1].rowIndex; 1.5978 + if (prevRowIndex > info.GetCellEndRowIndex() + 1) { 1.5979 + // hits a rowspan on the right 1.5980 + update = false; 1.5981 + // the corner was taken care of during the cell on the left 1.5982 + } 1.5983 + else if (prevRowIndex < info.GetCellEndRowIndex() + 1) { 1.5984 + // spans below the cell to the left 1.5985 + topCorners[colX] = blCorner; 1.5986 + blCorner.Set(NS_SIDE_RIGHT, currentBorder); 1.5987 + update = false; 1.5988 + } 1.5989 + } 1.5990 + if (update) { 1.5991 + blCorner.Update(NS_SIDE_RIGHT, currentBorder); 1.5992 + } 1.5993 + if (info.GetCellEndRowIndex() < damageArea.YMost() && 1.5994 + (colX >= damageArea.x)) { 1.5995 + if (hitsSpanBelow) { 1.5996 + tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap, 1.5997 + iter.mRowGroupStart, 1.5998 + info.GetCellEndRowIndex(), colX, 1.5999 + mozilla::css::Side(blCorner.ownerSide), 1.6000 + blCorner.subWidth, blCorner.bevel); 1.6001 + } 1.6002 + // store any corners this cell spans together with the aja cell 1.6003 + for (int32_t cX = colX + 1; cX < colX + segLength; cX++) { 1.6004 + BCCornerInfo& corner = bottomCorners[cX]; 1.6005 + corner.Set(NS_SIDE_RIGHT, currentBorder); 1.6006 + tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap, 1.6007 + iter.mRowGroupStart, 1.6008 + info.GetCellEndRowIndex(), cX, 1.6009 + mozilla::css::Side(corner.ownerSide), 1.6010 + corner.subWidth, 1.6011 + false); 1.6012 + } 1.6013 + } 1.6014 + // update lastBottomBorders and see if a new segment starts 1.6015 + startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder); 1.6016 + if (!startSeg) { 1.6017 + // make sure that we did not compare apples to oranges i.e. the 1.6018 + // current border should be a continuation of the lastBottomBorder, 1.6019 + // as it is a bottom border 1.6020 + // add 1 to the info.GetCellEndRowIndex() 1.6021 + startSeg = (lastBottomBorder.rowIndex != 1.6022 + info.GetCellEndRowIndex() + 1); 1.6023 + } 1.6024 + lastBottomBorder.rowIndex = info.GetCellEndRowIndex() + 1; 1.6025 + lastBottomBorder.rowSpan = info.mRowSpan; 1.6026 + for (int32_t cX = colX; cX < colX + segLength; cX++) { 1.6027 + lastBottomBorders[cX] = lastBottomBorder; 1.6028 + } 1.6029 + 1.6030 + // store the border segment the cell map and update cellBorders 1.6031 + if (info.GetCellEndRowIndex() < damageArea.YMost() && 1.6032 + (colX >= damageArea.x) && 1.6033 + (colX < damageArea.XMost())) { 1.6034 + tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *iter.mCellMap, 1.6035 + iter.mRowGroupStart, 1.6036 + info.GetCellEndRowIndex(), 1.6037 + colX, segLength, currentBorder.owner, 1.6038 + currentBorder.width, startSeg); 1.6039 + info.SetBottomBorderWidths(currentBorder.width); 1.6040 + ajaInfo.SetTopBorderWidths(currentBorder.width); 1.6041 + } 1.6042 + // update bottom right corner 1.6043 + BCCornerInfo& brCorner = bottomCorners[colX + segLength]; 1.6044 + brCorner.Update(NS_SIDE_LEFT, currentBorder); 1.6045 + } 1.6046 + if (!gotRowBorder && 1 == info.mRowSpan && 1.6047 + (ajaInfo.mTopRow || info.mRgAtBottom)) { 1.6048 + //get continuous row/row group border 1.6049 + //we need to check the row group's bottom border if this is 1.6050 + //the last row in the row group, but only a cell with rowspan=1 1.6051 + //will know whether *this* row is at the bottom 1.6052 + const nsIFrame* nextRowGroup = (ajaInfo.mRgAtTop) ? ajaInfo.mRowGroup : 1.6053 + nullptr; 1.6054 + info.SetInnerRowGroupBottomContBCBorder(nextRowGroup, ajaInfo.mTopRow); 1.6055 + gotRowBorder = true; 1.6056 + } 1.6057 + } 1.6058 + 1.6059 + // see if the cell to the right had a rowspan and its lower left border 1.6060 + // needs be joined with this one's bottom 1.6061 + // if there is a cell to the right and the cell to right was a rowspan 1.6062 + if ((info.mNumTableCols != info.GetCellEndColIndex() + 1) && 1.6063 + (lastBottomBorders[info.GetCellEndColIndex() + 1].rowSpan > 1)) { 1.6064 + BCCornerInfo& corner = bottomCorners[info.GetCellEndColIndex() + 1]; 1.6065 + if ((NS_SIDE_TOP != corner.ownerSide) && 1.6066 + (NS_SIDE_BOTTOM != corner.ownerSide)) { 1.6067 + // not a vertical owner 1.6068 + BCCellBorder& thisBorder = lastBottomBorder; 1.6069 + BCCellBorder& nextBorder = lastBottomBorders[info.mColIndex + 1]; 1.6070 + if ((thisBorder.color == nextBorder.color) && 1.6071 + (thisBorder.width == nextBorder.width) && 1.6072 + (thisBorder.style == nextBorder.style)) { 1.6073 + // set the flag on the next border indicating it is not the start of a 1.6074 + // new segment 1.6075 + if (iter.mCellMap) { 1.6076 + tableCellMap->ResetTopStart(NS_SIDE_BOTTOM, *iter.mCellMap, 1.6077 + info.GetCellEndRowIndex(), 1.6078 + info.GetCellEndColIndex() + 1); 1.6079 + } 1.6080 + } 1.6081 + } 1.6082 + } 1.6083 + } // for (iter.First(info); info.mCell; iter.Next(info)) { 1.6084 + // reset the bc flag and damage area 1.6085 + SetNeedToCalcBCBorders(false); 1.6086 + propData->mDamageArea = nsIntRect(0,0,0,0); 1.6087 +#ifdef DEBUG_TABLE_CELLMAP 1.6088 + mCellMap->Dump(); 1.6089 +#endif 1.6090 +} 1.6091 + 1.6092 +class BCPaintBorderIterator; 1.6093 + 1.6094 +struct BCVerticalSeg 1.6095 +{ 1.6096 + BCVerticalSeg(); 1.6097 + 1.6098 + void Start(BCPaintBorderIterator& aIter, 1.6099 + BCBorderOwner aBorderOwner, 1.6100 + BCPixelSize aVerSegWidth, 1.6101 + BCPixelSize aHorSegHeight); 1.6102 + 1.6103 + void Initialize(BCPaintBorderIterator& aIter); 1.6104 + void GetBottomCorner(BCPaintBorderIterator& aIter, 1.6105 + BCPixelSize aHorSegHeight); 1.6106 + 1.6107 + 1.6108 + void Paint(BCPaintBorderIterator& aIter, 1.6109 + nsRenderingContext& aRenderingContext, 1.6110 + BCPixelSize aHorSegHeight); 1.6111 + void AdvanceOffsetY(); 1.6112 + void IncludeCurrentBorder(BCPaintBorderIterator& aIter); 1.6113 + 1.6114 + 1.6115 + union { 1.6116 + nsTableColFrame* mCol; 1.6117 + int32_t mColWidth; 1.6118 + }; 1.6119 + nscoord mOffsetX; // x-offset with respect to the table edge 1.6120 + nscoord mOffsetY; // y-offset with respect to the table edge 1.6121 + nscoord mLength; // vertical length including corners 1.6122 + BCPixelSize mWidth; // width in pixels 1.6123 + 1.6124 + nsTableCellFrame* mAjaCell; // previous sibling to the first cell 1.6125 + // where the segment starts, it can be 1.6126 + // the owner of a segment 1.6127 + nsTableCellFrame* mFirstCell; // cell at the start of the segment 1.6128 + nsTableRowGroupFrame* mFirstRowGroup; // row group at the start of the segment 1.6129 + nsTableRowFrame* mFirstRow; // row at the start of the segment 1.6130 + nsTableCellFrame* mLastCell; // cell at the current end of the 1.6131 + // segment 1.6132 + 1.6133 + 1.6134 + uint8_t mOwner; // owner of the border, defines the 1.6135 + // style 1.6136 + mozilla::css::Side mTopBevelSide; // direction to bevel at the top 1.6137 + nscoord mTopBevelOffset; // how much to bevel at the top 1.6138 + BCPixelSize mBottomHorSegHeight; // height of the crossing 1.6139 + //horizontal border 1.6140 + nscoord mBottomOffset; // how much longer is the segment due 1.6141 + // to the horizontal border, by this 1.6142 + // amount the next segment needs to be 1.6143 + // shifted. 1.6144 + bool mIsBottomBevel; // should we bevel at the bottom 1.6145 +}; 1.6146 + 1.6147 +struct BCHorizontalSeg 1.6148 +{ 1.6149 + BCHorizontalSeg(); 1.6150 + 1.6151 + void Start(BCPaintBorderIterator& aIter, 1.6152 + BCBorderOwner aBorderOwner, 1.6153 + BCPixelSize aBottomVerSegWidth, 1.6154 + BCPixelSize aHorSegHeight); 1.6155 + void GetRightCorner(BCPaintBorderIterator& aIter, 1.6156 + BCPixelSize aLeftSegWidth); 1.6157 + void AdvanceOffsetX(int32_t aIncrement); 1.6158 + void IncludeCurrentBorder(BCPaintBorderIterator& aIter); 1.6159 + void Paint(BCPaintBorderIterator& aIter, 1.6160 + nsRenderingContext& aRenderingContext); 1.6161 + 1.6162 + nscoord mOffsetX; // x-offset with respect to the table edge 1.6163 + nscoord mOffsetY; // y-offset with respect to the table edge 1.6164 + nscoord mLength; // horizontal length including corners 1.6165 + BCPixelSize mWidth; // border width in pixels 1.6166 + nscoord mLeftBevelOffset; // how much to bevel at the left 1.6167 + mozilla::css::Side mLeftBevelSide; // direction to bevel at the left 1.6168 + bool mIsRightBevel; // should we bevel at the right end 1.6169 + nscoord mRightBevelOffset; // how much to bevel at the right 1.6170 + mozilla::css::Side mRightBevelSide; // direction to bevel at the right 1.6171 + nscoord mEndOffset; // how much longer is the segment due 1.6172 + // to the vertical border, by this 1.6173 + // amount the next segment needs to be 1.6174 + // shifted. 1.6175 + uint8_t mOwner; // owner of the border, defines the 1.6176 + // style 1.6177 + nsTableCellFrame* mFirstCell; // cell at the start of the segment 1.6178 + nsTableCellFrame* mAjaCell; // neighboring cell to the first cell 1.6179 + // where the segment starts, it can be 1.6180 + // the owner of a segment 1.6181 +}; 1.6182 + 1.6183 +// Iterates over borders (left border, corner, top border) in the cell map within a damage area 1.6184 +// from left to right, top to bottom. All members are in terms of the 1st in flow frames, except 1.6185 +// where suffixed by InFlow. 1.6186 +class BCPaintBorderIterator 1.6187 +{ 1.6188 +public: 1.6189 + 1.6190 + 1.6191 + BCPaintBorderIterator(nsTableFrame* aTable); 1.6192 + ~BCPaintBorderIterator() { if (mVerInfo) { 1.6193 + delete [] mVerInfo; 1.6194 + }} 1.6195 + void Reset(); 1.6196 + 1.6197 + /** 1.6198 + * Determine the damage area in terms of rows and columns and finalize 1.6199 + * mInitialOffsetX and mInitialOffsetY. 1.6200 + * @param aDirtyRect - dirty rect in table coordinates 1.6201 + * @return - true if we need to paint something given dirty rect 1.6202 + */ 1.6203 + bool SetDamageArea(const nsRect& aDamageRect); 1.6204 + void First(); 1.6205 + void Next(); 1.6206 + void AccumulateOrPaintHorizontalSegment(nsRenderingContext& aRenderingContext); 1.6207 + void AccumulateOrPaintVerticalSegment(nsRenderingContext& aRenderingContext); 1.6208 + void ResetVerInfo(); 1.6209 + void StoreColumnWidth(int32_t aIndex); 1.6210 + bool VerticalSegmentOwnsCorner(); 1.6211 + 1.6212 + nsTableFrame* mTable; 1.6213 + nsTableFrame* mTableFirstInFlow; 1.6214 + nsTableCellMap* mTableCellMap; 1.6215 + nsCellMap* mCellMap; 1.6216 + bool mTableIsLTR; 1.6217 + int32_t mColInc; // +1 for ltr -1 for rtl 1.6218 + const nsStyleBackground* mTableBgColor; 1.6219 + nsTableFrame::RowGroupArray mRowGroups; 1.6220 + 1.6221 + nsTableRowGroupFrame* mPrevRg; 1.6222 + nsTableRowGroupFrame* mRg; 1.6223 + bool mIsRepeatedHeader; 1.6224 + bool mIsRepeatedFooter; 1.6225 + nsTableRowGroupFrame* mStartRg; // first row group in the damagearea 1.6226 + int32_t mRgIndex; // current row group index in the 1.6227 + // mRowgroups array 1.6228 + int32_t mFifRgFirstRowIndex; // start row index of the first in 1.6229 + // flow of the row group 1.6230 + int32_t mRgFirstRowIndex; // row index of the first row in the 1.6231 + // row group 1.6232 + int32_t mRgLastRowIndex; // row index of the last row in the row 1.6233 + // group 1.6234 + int32_t mNumTableRows; // number of rows in the table and all 1.6235 + // continuations 1.6236 + int32_t mNumTableCols; // number of columns in the table 1.6237 + int32_t mColIndex; // with respect to the table 1.6238 + int32_t mRowIndex; // with respect to the table 1.6239 + int32_t mRepeatedHeaderRowIndex; // row index in a repeated 1.6240 + //header, it's equivalent to 1.6241 + // mRowIndex when we're in a repeated 1.6242 + // header, and set to the last row 1.6243 + // index of a repeated header when 1.6244 + // we're not 1.6245 + bool mIsNewRow; 1.6246 + bool mAtEnd; // the iterator cycled over all 1.6247 + // borders 1.6248 + nsTableRowFrame* mPrevRow; 1.6249 + nsTableRowFrame* mRow; 1.6250 + nsTableRowFrame* mStartRow; //first row in a inside the damagearea 1.6251 + 1.6252 + 1.6253 + // cell properties 1.6254 + nsTableCellFrame* mPrevCell; 1.6255 + nsTableCellFrame* mCell; 1.6256 + BCCellData* mPrevCellData; 1.6257 + BCCellData* mCellData; 1.6258 + BCData* mBCData; 1.6259 + 1.6260 + bool IsTableTopMost() {return (mRowIndex == 0) && !mTable->GetPrevInFlow();} 1.6261 + bool IsTableRightMost() {return (mColIndex >= mNumTableCols);} 1.6262 + bool IsTableBottomMost() {return (mRowIndex >= mNumTableRows) && !mTable->GetNextInFlow();} 1.6263 + bool IsTableLeftMost() {return (mColIndex == 0);} 1.6264 + bool IsDamageAreaTopMost() {return (mRowIndex == mDamageArea.y);} 1.6265 + bool IsDamageAreaRightMost() {return (mColIndex >= mDamageArea.XMost());} 1.6266 + bool IsDamageAreaBottomMost() {return (mRowIndex >= mDamageArea.YMost());} 1.6267 + bool IsDamageAreaLeftMost() {return (mColIndex == mDamageArea.x);} 1.6268 + int32_t GetRelativeColIndex() {return (mColIndex - mDamageArea.x);} 1.6269 + 1.6270 + nsIntRect mDamageArea; // damageArea in cellmap coordinates 1.6271 + bool IsAfterRepeatedHeader() { return !mIsRepeatedHeader && (mRowIndex == (mRepeatedHeaderRowIndex + 1));} 1.6272 + bool StartRepeatedFooter() {return mIsRepeatedFooter && (mRowIndex == mRgFirstRowIndex) && (mRowIndex != mDamageArea.y);} 1.6273 + nscoord mInitialOffsetX; // offsetX of the first border with 1.6274 + // respect to the table 1.6275 + nscoord mInitialOffsetY; // offsetY of the first border with 1.6276 + // respect to the table 1.6277 + nscoord mNextOffsetY; // offsetY of the next segment 1.6278 + BCVerticalSeg* mVerInfo; // this array is used differently when 1.6279 + // horizontal and vertical borders are drawn 1.6280 + // When horizontal border are drawn we cache 1.6281 + // the column widths and the width of the 1.6282 + // vertical borders that arrive from top 1.6283 + // When we draw vertical borders we store 1.6284 + // lengths and width for vertical borders 1.6285 + // before they are drawn while we move over 1.6286 + // the columns in the damage area 1.6287 + // It has one more elements than columns are 1.6288 + //in the table. 1.6289 + BCHorizontalSeg mHorSeg; // the horizontal segment while we 1.6290 + // move over the colums 1.6291 + BCPixelSize mPrevHorSegHeight; // the height of the previous 1.6292 + // horizontal border 1.6293 + 1.6294 +private: 1.6295 + 1.6296 + bool SetNewRow(nsTableRowFrame* aRow = nullptr); 1.6297 + bool SetNewRowGroup(); 1.6298 + void SetNewData(int32_t aRowIndex, int32_t aColIndex); 1.6299 + 1.6300 +}; 1.6301 + 1.6302 + 1.6303 + 1.6304 +BCPaintBorderIterator::BCPaintBorderIterator(nsTableFrame* aTable) 1.6305 +{ 1.6306 + mTable = aTable; 1.6307 + mVerInfo = nullptr; 1.6308 + nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr); 1.6309 + mTableFirstInFlow = static_cast<nsTableFrame*>(mTable->FirstInFlow()); 1.6310 + mTableCellMap = mTable->GetCellMap(); 1.6311 + // y position of first row in damage area 1.6312 + mInitialOffsetY = mTable->GetPrevInFlow() ? 0 : childAreaOffset.top; 1.6313 + mNumTableRows = mTable->GetRowCount(); 1.6314 + mNumTableCols = mTable->GetColCount(); 1.6315 + 1.6316 + // Get the ordered row groups 1.6317 + mTable->OrderRowGroups(mRowGroups); 1.6318 + // initialize to a non existing index 1.6319 + mRepeatedHeaderRowIndex = -99; 1.6320 + 1.6321 + mTableIsLTR = mTable->StyleVisibility()->mDirection == 1.6322 + NS_STYLE_DIRECTION_LTR; 1.6323 + mColInc = (mTableIsLTR) ? 1 : -1; 1.6324 + 1.6325 + nsIFrame* bgFrame = 1.6326 + nsCSSRendering::FindNonTransparentBackgroundFrame(aTable); 1.6327 + mTableBgColor = bgFrame->StyleBackground(); 1.6328 +} 1.6329 + 1.6330 +bool 1.6331 +BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect) 1.6332 +{ 1.6333 + 1.6334 + uint32_t startRowIndex, endRowIndex, startColIndex, endColIndex; 1.6335 + startRowIndex = endRowIndex = startColIndex = endColIndex = 0; 1.6336 + bool done = false; 1.6337 + bool haveIntersect = false; 1.6338 + // find startRowIndex, endRowIndex 1.6339 + nscoord rowY = mInitialOffsetY; 1.6340 + for (uint32_t rgX = 0; rgX < mRowGroups.Length() && !done; rgX++) { 1.6341 + nsTableRowGroupFrame* rgFrame = mRowGroups[rgX]; 1.6342 + for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame; 1.6343 + rowFrame = rowFrame->GetNextRow()) { 1.6344 + // conservatively estimate the half border widths outside the row 1.6345 + nscoord topBorderHalf = (mTable->GetPrevInFlow()) ? 0 : 1.6346 + nsPresContext::CSSPixelsToAppUnits(rowFrame->GetTopBCBorderWidth() + 1); 1.6347 + nscoord bottomBorderHalf = (mTable->GetNextInFlow()) ? 0 : 1.6348 + nsPresContext::CSSPixelsToAppUnits(rowFrame->GetBottomBCBorderWidth() + 1); 1.6349 + // get the row rect relative to the table rather than the row group 1.6350 + nsSize rowSize = rowFrame->GetSize(); 1.6351 + if (haveIntersect) { 1.6352 + if (aDirtyRect.YMost() >= (rowY - topBorderHalf)) { 1.6353 + nsTableRowFrame* fifRow = 1.6354 + static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow()); 1.6355 + endRowIndex = fifRow->GetRowIndex(); 1.6356 + } 1.6357 + else done = true; 1.6358 + } 1.6359 + else { 1.6360 + if ((rowY + rowSize.height + bottomBorderHalf) >= aDirtyRect.y) { 1.6361 + mStartRg = rgFrame; 1.6362 + mStartRow = rowFrame; 1.6363 + nsTableRowFrame* fifRow = 1.6364 + static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow()); 1.6365 + startRowIndex = endRowIndex = fifRow->GetRowIndex(); 1.6366 + haveIntersect = true; 1.6367 + } 1.6368 + else { 1.6369 + mInitialOffsetY += rowSize.height; 1.6370 + } 1.6371 + } 1.6372 + rowY += rowSize.height; 1.6373 + } 1.6374 + } 1.6375 + mNextOffsetY = mInitialOffsetY; 1.6376 + 1.6377 + // XXX comment refers to the obsolete NS_FRAME_OUTSIDE_CHILDREN flag 1.6378 + // XXX but I don't understand it, so not changing it for now 1.6379 + // outer table borders overflow the table, so the table might be 1.6380 + // target to other areas as the NS_FRAME_OUTSIDE_CHILDREN is set 1.6381 + // on the table 1.6382 + if (!haveIntersect) 1.6383 + return false; 1.6384 + // find startColIndex, endColIndex, startColX 1.6385 + haveIntersect = false; 1.6386 + if (0 == mNumTableCols) 1.6387 + return false; 1.6388 + int32_t leftCol, rightCol; // columns are in the range [leftCol, rightCol) 1.6389 + 1.6390 + nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr); 1.6391 + if (mTableIsLTR) { 1.6392 + mInitialOffsetX = childAreaOffset.left; // x position of first col in 1.6393 + // damage area 1.6394 + leftCol = 0; 1.6395 + rightCol = mNumTableCols; 1.6396 + } else { 1.6397 + // x position of first col in damage area 1.6398 + mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right; 1.6399 + leftCol = mNumTableCols-1; 1.6400 + rightCol = -1; 1.6401 + } 1.6402 + nscoord x = 0; 1.6403 + int32_t colX; 1.6404 + for (colX = leftCol; colX != rightCol; colX += mColInc) { 1.6405 + nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(colX); 1.6406 + if (!colFrame) ABORT1(false); 1.6407 + // get the col rect relative to the table rather than the col group 1.6408 + nsSize size = colFrame->GetSize(); 1.6409 + if (haveIntersect) { 1.6410 + // conservatively estimate the left half border width outside the col 1.6411 + nscoord leftBorderHalf = 1.6412 + nsPresContext::CSSPixelsToAppUnits(colFrame->GetLeftBorderWidth() + 1); 1.6413 + if (aDirtyRect.XMost() >= (x - leftBorderHalf)) { 1.6414 + endColIndex = colX; 1.6415 + } 1.6416 + else break; 1.6417 + } 1.6418 + else { 1.6419 + // conservatively estimate the right half border width outside the col 1.6420 + nscoord rightBorderHalf = 1.6421 + nsPresContext::CSSPixelsToAppUnits(colFrame->GetRightBorderWidth() + 1); 1.6422 + if ((x + size.width + rightBorderHalf) >= aDirtyRect.x) { 1.6423 + startColIndex = endColIndex = colX; 1.6424 + haveIntersect = true; 1.6425 + } 1.6426 + else { 1.6427 + mInitialOffsetX += mColInc * size.width; 1.6428 + } 1.6429 + } 1.6430 + x += size.width; 1.6431 + } 1.6432 + if (!mTableIsLTR) { 1.6433 + uint32_t temp; 1.6434 + mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right; 1.6435 + temp = startColIndex; startColIndex = endColIndex; endColIndex = temp; 1.6436 + for (uint32_t column = 0; column < startColIndex; column++) { 1.6437 + nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(column); 1.6438 + if (!colFrame) ABORT1(false); 1.6439 + nsSize size = colFrame->GetSize(); 1.6440 + mInitialOffsetX += mColInc * size.width; 1.6441 + } 1.6442 + } 1.6443 + if (!haveIntersect) 1.6444 + return false; 1.6445 + mDamageArea = nsIntRect(startColIndex, startRowIndex, 1.6446 + 1 + DeprecatedAbs<int32_t>(endColIndex - startColIndex), 1.6447 + 1 + endRowIndex - startRowIndex); 1.6448 + 1.6449 + Reset(); 1.6450 + mVerInfo = new BCVerticalSeg[mDamageArea.width + 1]; 1.6451 + if (!mVerInfo) 1.6452 + return false; 1.6453 + return true; 1.6454 +} 1.6455 + 1.6456 +void 1.6457 +BCPaintBorderIterator::Reset() 1.6458 +{ 1.6459 + mAtEnd = true; // gets reset when First() is called 1.6460 + mRg = mStartRg; 1.6461 + mPrevRow = nullptr; 1.6462 + mRow = mStartRow; 1.6463 + mRowIndex = 0; 1.6464 + mColIndex = 0; 1.6465 + mRgIndex = -1; 1.6466 + mPrevCell = nullptr; 1.6467 + mCell = nullptr; 1.6468 + mPrevCellData = nullptr; 1.6469 + mCellData = nullptr; 1.6470 + mBCData = nullptr; 1.6471 + ResetVerInfo(); 1.6472 +} 1.6473 + 1.6474 +/** 1.6475 + * Set the iterator data to a new cellmap coordinate 1.6476 + * @param aRowIndex - the row index 1.6477 + * @param aColIndex - the col index 1.6478 + */ 1.6479 +void 1.6480 +BCPaintBorderIterator::SetNewData(int32_t aY, 1.6481 + int32_t aX) 1.6482 +{ 1.6483 + if (!mTableCellMap || !mTableCellMap->mBCInfo) ABORT0(); 1.6484 + 1.6485 + mColIndex = aX; 1.6486 + mRowIndex = aY; 1.6487 + mPrevCellData = mCellData; 1.6488 + if (IsTableRightMost() && IsTableBottomMost()) { 1.6489 + mCell = nullptr; 1.6490 + mBCData = &mTableCellMap->mBCInfo->mLowerRightCorner; 1.6491 + } 1.6492 + else if (IsTableRightMost()) { 1.6493 + mCellData = nullptr; 1.6494 + mBCData = &mTableCellMap->mBCInfo->mRightBorders.ElementAt(aY); 1.6495 + } 1.6496 + else if (IsTableBottomMost()) { 1.6497 + mCellData = nullptr; 1.6498 + mBCData = &mTableCellMap->mBCInfo->mBottomBorders.ElementAt(aX); 1.6499 + } 1.6500 + else { 1.6501 + if (uint32_t(mRowIndex - mFifRgFirstRowIndex) < mCellMap->mRows.Length()) { 1.6502 + mBCData = nullptr; 1.6503 + mCellData = 1.6504 + (BCCellData*)mCellMap->mRows[mRowIndex - mFifRgFirstRowIndex].SafeElementAt(mColIndex); 1.6505 + if (mCellData) { 1.6506 + mBCData = &mCellData->mData; 1.6507 + if (!mCellData->IsOrig()) { 1.6508 + if (mCellData->IsRowSpan()) { 1.6509 + aY -= mCellData->GetRowSpanOffset(); 1.6510 + } 1.6511 + if (mCellData->IsColSpan()) { 1.6512 + aX -= mCellData->GetColSpanOffset(); 1.6513 + } 1.6514 + if ((aX >= 0) && (aY >= 0)) { 1.6515 + mCellData = (BCCellData*)mCellMap->mRows[aY - mFifRgFirstRowIndex][aX]; 1.6516 + } 1.6517 + } 1.6518 + if (mCellData->IsOrig()) { 1.6519 + mPrevCell = mCell; 1.6520 + mCell = mCellData->GetCellFrame(); 1.6521 + } 1.6522 + } 1.6523 + } 1.6524 + } 1.6525 +} 1.6526 + 1.6527 +/** 1.6528 + * Set the iterator to a new row 1.6529 + * @param aRow - the new row frame, if null the iterator will advance to the 1.6530 + * next row 1.6531 + */ 1.6532 +bool 1.6533 +BCPaintBorderIterator::SetNewRow(nsTableRowFrame* aRow) 1.6534 +{ 1.6535 + mPrevRow = mRow; 1.6536 + mRow = (aRow) ? aRow : mRow->GetNextRow(); 1.6537 + if (mRow) { 1.6538 + mIsNewRow = true; 1.6539 + mRowIndex = mRow->GetRowIndex(); 1.6540 + mColIndex = mDamageArea.x; 1.6541 + mPrevHorSegHeight = 0; 1.6542 + if (mIsRepeatedHeader) { 1.6543 + mRepeatedHeaderRowIndex = mRowIndex; 1.6544 + } 1.6545 + } 1.6546 + else { 1.6547 + mAtEnd = true; 1.6548 + } 1.6549 + return !mAtEnd; 1.6550 +} 1.6551 + 1.6552 +/** 1.6553 + * Advance the iterator to the next row group 1.6554 + */ 1.6555 +bool 1.6556 +BCPaintBorderIterator::SetNewRowGroup() 1.6557 +{ 1.6558 + 1.6559 + mRgIndex++; 1.6560 + 1.6561 + mIsRepeatedHeader = false; 1.6562 + mIsRepeatedFooter = false; 1.6563 + 1.6564 + NS_ASSERTION(mRgIndex >= 0, "mRgIndex out of bounds"); 1.6565 + if (uint32_t(mRgIndex) < mRowGroups.Length()) { 1.6566 + mPrevRg = mRg; 1.6567 + mRg = mRowGroups[mRgIndex]; 1.6568 + nsTableRowGroupFrame* fifRg = 1.6569 + static_cast<nsTableRowGroupFrame*>(mRg->FirstInFlow()); 1.6570 + mFifRgFirstRowIndex = fifRg->GetStartRowIndex(); 1.6571 + mRgFirstRowIndex = mRg->GetStartRowIndex(); 1.6572 + mRgLastRowIndex = mRgFirstRowIndex + mRg->GetRowCount() - 1; 1.6573 + 1.6574 + if (SetNewRow(mRg->GetFirstRow())) { 1.6575 + mCellMap = mTableCellMap->GetMapFor(fifRg, nullptr); 1.6576 + if (!mCellMap) ABORT1(false); 1.6577 + } 1.6578 + if (mRg && mTable->GetPrevInFlow() && !mRg->GetPrevInFlow()) { 1.6579 + // if mRowGroup doesn't have a prev in flow, then it may be a repeated 1.6580 + // header or footer 1.6581 + const nsStyleDisplay* display = mRg->StyleDisplay(); 1.6582 + if (mRowIndex == mDamageArea.y) { 1.6583 + mIsRepeatedHeader = (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay); 1.6584 + } 1.6585 + else { 1.6586 + mIsRepeatedFooter = (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == display->mDisplay); 1.6587 + } 1.6588 + } 1.6589 + } 1.6590 + else { 1.6591 + mAtEnd = true; 1.6592 + } 1.6593 + return !mAtEnd; 1.6594 +} 1.6595 + 1.6596 +/** 1.6597 + * Move the iterator to the first position in the damageArea 1.6598 + */ 1.6599 +void 1.6600 +BCPaintBorderIterator::First() 1.6601 +{ 1.6602 + if (!mTable || (mDamageArea.x >= mNumTableCols) || 1.6603 + (mDamageArea.y >= mNumTableRows)) ABORT0(); 1.6604 + 1.6605 + mAtEnd = false; 1.6606 + 1.6607 + uint32_t numRowGroups = mRowGroups.Length(); 1.6608 + for (uint32_t rgY = 0; rgY < numRowGroups; rgY++) { 1.6609 + nsTableRowGroupFrame* rowG = mRowGroups[rgY]; 1.6610 + int32_t start = rowG->GetStartRowIndex(); 1.6611 + int32_t end = start + rowG->GetRowCount() - 1; 1.6612 + if ((mDamageArea.y >= start) && (mDamageArea.y <= end)) { 1.6613 + mRgIndex = rgY - 1; // SetNewRowGroup increments rowGroupIndex 1.6614 + if (SetNewRowGroup()) { 1.6615 + while ((mRowIndex < mDamageArea.y) && !mAtEnd) { 1.6616 + SetNewRow(); 1.6617 + } 1.6618 + if (!mAtEnd) { 1.6619 + SetNewData(mDamageArea.y, mDamageArea.x); 1.6620 + } 1.6621 + } 1.6622 + return; 1.6623 + } 1.6624 + } 1.6625 + mAtEnd = true; 1.6626 +} 1.6627 + 1.6628 +/** 1.6629 + * Advance the iterator to the next position 1.6630 + */ 1.6631 +void 1.6632 +BCPaintBorderIterator::Next() 1.6633 +{ 1.6634 + if (mAtEnd) ABORT0(); 1.6635 + mIsNewRow = false; 1.6636 + 1.6637 + mColIndex++; 1.6638 + if (mColIndex > mDamageArea.XMost()) { 1.6639 + mRowIndex++; 1.6640 + if (mRowIndex == mDamageArea.YMost()) { 1.6641 + mColIndex = mDamageArea.x; 1.6642 + } 1.6643 + else if (mRowIndex < mDamageArea.YMost()) { 1.6644 + if (mRowIndex <= mRgLastRowIndex) { 1.6645 + SetNewRow(); 1.6646 + } 1.6647 + else { 1.6648 + SetNewRowGroup(); 1.6649 + } 1.6650 + } 1.6651 + else { 1.6652 + mAtEnd = true; 1.6653 + } 1.6654 + } 1.6655 + if (!mAtEnd) { 1.6656 + SetNewData(mRowIndex, mColIndex); 1.6657 + } 1.6658 +} 1.6659 + 1.6660 +// XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine 1.6661 +// them 1.6662 +/** Compute the vertical offset of a vertical border segment 1.6663 + * @param aCornerOwnerSide - which side owns the corner 1.6664 + * @param aCornerSubWidth - how wide is the nonwinning side of the corner 1.6665 + * @param aHorWidth - how wide is the horizontal edge of the corner 1.6666 + * @param aIsStartOfSeg - does this corner start a new segment 1.6667 + * @param aIsBevel - is this corner beveled 1.6668 + * @return - offset in twips 1.6669 + */ 1.6670 +static nscoord 1.6671 +CalcVerCornerOffset(mozilla::css::Side aCornerOwnerSide, 1.6672 + BCPixelSize aCornerSubWidth, 1.6673 + BCPixelSize aHorWidth, 1.6674 + bool aIsStartOfSeg, 1.6675 + bool aIsBevel) 1.6676 +{ 1.6677 + nscoord offset = 0; 1.6678 + // XXX These should be replaced with appropriate side-specific macros (which?) 1.6679 + BCPixelSize smallHalf, largeHalf; 1.6680 + if ((NS_SIDE_TOP == aCornerOwnerSide) || 1.6681 + (NS_SIDE_BOTTOM == aCornerOwnerSide)) { 1.6682 + DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf); 1.6683 + if (aIsBevel) { 1.6684 + offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; 1.6685 + } 1.6686 + else { 1.6687 + offset = (NS_SIDE_TOP == aCornerOwnerSide) ? smallHalf : -largeHalf; 1.6688 + } 1.6689 + } 1.6690 + else { 1.6691 + DivideBCBorderSize(aHorWidth, smallHalf, largeHalf); 1.6692 + if (aIsBevel) { 1.6693 + offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; 1.6694 + } 1.6695 + else { 1.6696 + offset = (aIsStartOfSeg) ? smallHalf : -largeHalf; 1.6697 + } 1.6698 + } 1.6699 + return nsPresContext::CSSPixelsToAppUnits(offset); 1.6700 +} 1.6701 + 1.6702 +/** Compute the horizontal offset of a horizontal border segment 1.6703 + * @param aCornerOwnerSide - which side owns the corner 1.6704 + * @param aCornerSubWidth - how wide is the nonwinning side of the corner 1.6705 + * @param aVerWidth - how wide is the vertical edge of the corner 1.6706 + * @param aIsStartOfSeg - does this corner start a new segment 1.6707 + * @param aIsBevel - is this corner beveled 1.6708 + * @param aTableIsLTR - direction, the computation depends on ltr or rtl 1.6709 + * @return - offset in twips 1.6710 + */ 1.6711 +static nscoord 1.6712 +CalcHorCornerOffset(mozilla::css::Side aCornerOwnerSide, 1.6713 + BCPixelSize aCornerSubWidth, 1.6714 + BCPixelSize aVerWidth, 1.6715 + bool aIsStartOfSeg, 1.6716 + bool aIsBevel, 1.6717 + bool aTableIsLTR) 1.6718 +{ 1.6719 + nscoord offset = 0; 1.6720 + // XXX These should be replaced with appropriate side-specific macros (which?) 1.6721 + BCPixelSize smallHalf, largeHalf; 1.6722 + if ((NS_SIDE_LEFT == aCornerOwnerSide) || 1.6723 + (NS_SIDE_RIGHT == aCornerOwnerSide)) { 1.6724 + if (aTableIsLTR) { 1.6725 + DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf); 1.6726 + } 1.6727 + else { 1.6728 + DivideBCBorderSize(aCornerSubWidth, largeHalf, smallHalf); 1.6729 + } 1.6730 + if (aIsBevel) { 1.6731 + offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; 1.6732 + } 1.6733 + else { 1.6734 + offset = (NS_SIDE_LEFT == aCornerOwnerSide) ? smallHalf : -largeHalf; 1.6735 + } 1.6736 + } 1.6737 + else { 1.6738 + if (aTableIsLTR) { 1.6739 + DivideBCBorderSize(aVerWidth, smallHalf, largeHalf); 1.6740 + } 1.6741 + else { 1.6742 + DivideBCBorderSize(aVerWidth, largeHalf, smallHalf); 1.6743 + } 1.6744 + if (aIsBevel) { 1.6745 + offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; 1.6746 + } 1.6747 + else { 1.6748 + offset = (aIsStartOfSeg) ? smallHalf : -largeHalf; 1.6749 + } 1.6750 + } 1.6751 + return nsPresContext::CSSPixelsToAppUnits(offset); 1.6752 +} 1.6753 + 1.6754 +BCVerticalSeg::BCVerticalSeg() 1.6755 +{ 1.6756 + mCol = nullptr; 1.6757 + mFirstCell = mLastCell = mAjaCell = nullptr; 1.6758 + mOffsetX = mOffsetY = mLength = mWidth = mTopBevelOffset = 0; 1.6759 + mTopBevelSide = NS_SIDE_TOP; 1.6760 + mOwner = eCellOwner; 1.6761 +} 1.6762 + 1.6763 +/** 1.6764 + * Start a new vertical segment 1.6765 + * @param aIter - iterator containing the structural information 1.6766 + * @param aBorderOwner - determines the border style 1.6767 + * @param aVerSegWidth - the width of segment in pixel 1.6768 + * @param aHorSegHeight - the width of the horizontal segment joining the corner 1.6769 + * at the start 1.6770 + */ 1.6771 +void 1.6772 +BCVerticalSeg::Start(BCPaintBorderIterator& aIter, 1.6773 + BCBorderOwner aBorderOwner, 1.6774 + BCPixelSize aVerSegWidth, 1.6775 + BCPixelSize aHorSegHeight) 1.6776 +{ 1.6777 + mozilla::css::Side ownerSide = NS_SIDE_TOP; 1.6778 + bool bevel = false; 1.6779 + 1.6780 + 1.6781 + nscoord cornerSubWidth = (aIter.mBCData) ? 1.6782 + aIter.mBCData->GetCorner(ownerSide, bevel) : 0; 1.6783 + 1.6784 + bool topBevel = (aVerSegWidth > 0) ? bevel : false; 1.6785 + BCPixelSize maxHorSegHeight = std::max(aIter.mPrevHorSegHeight, aHorSegHeight); 1.6786 + nscoord offset = CalcVerCornerOffset(ownerSide, cornerSubWidth, 1.6787 + maxHorSegHeight, true, 1.6788 + topBevel); 1.6789 + 1.6790 + mTopBevelOffset = topBevel ? 1.6791 + nsPresContext::CSSPixelsToAppUnits(maxHorSegHeight): 0; 1.6792 + // XXX this assumes that only corners where 2 segments join can be beveled 1.6793 + mTopBevelSide = (aHorSegHeight > 0) ? NS_SIDE_RIGHT : NS_SIDE_LEFT; 1.6794 + mOffsetY += offset; 1.6795 + mLength = -offset; 1.6796 + mWidth = aVerSegWidth; 1.6797 + mOwner = aBorderOwner; 1.6798 + mFirstCell = aIter.mCell; 1.6799 + mFirstRowGroup = aIter.mRg; 1.6800 + mFirstRow = aIter.mRow; 1.6801 + if (aIter.GetRelativeColIndex() > 0) { 1.6802 + mAjaCell = aIter.mVerInfo[aIter.GetRelativeColIndex() - 1].mLastCell; 1.6803 + } 1.6804 +} 1.6805 + 1.6806 +/** 1.6807 + * Initialize the vertical segments with information that will persist for any 1.6808 + * vertical segment in this column 1.6809 + * @param aIter - iterator containing the structural information 1.6810 + */ 1.6811 +void 1.6812 +BCVerticalSeg::Initialize(BCPaintBorderIterator& aIter) 1.6813 +{ 1.6814 + int32_t relColIndex = aIter.GetRelativeColIndex(); 1.6815 + mCol = aIter.IsTableRightMost() ? aIter.mVerInfo[relColIndex - 1].mCol : 1.6816 + aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex); 1.6817 + if (!mCol) ABORT0(); 1.6818 + if (0 == relColIndex) { 1.6819 + mOffsetX = aIter.mInitialOffsetX; 1.6820 + } 1.6821 + // set colX for the next column 1.6822 + if (!aIter.IsDamageAreaRightMost()) { 1.6823 + aIter.mVerInfo[relColIndex + 1].mOffsetX = mOffsetX + 1.6824 + aIter.mColInc * mCol->GetSize().width; 1.6825 + } 1.6826 + mOffsetY = aIter.mInitialOffsetY; 1.6827 + mLastCell = aIter.mCell; 1.6828 +} 1.6829 + 1.6830 +/** 1.6831 + * Compute the offsets for the bottom corner of a vertical segment 1.6832 + * @param aIter - iterator containing the structural information 1.6833 + * @param aHorSegHeight - the width of the horizontal segment joining the corner 1.6834 + * at the start 1.6835 + */ 1.6836 +void 1.6837 +BCVerticalSeg::GetBottomCorner(BCPaintBorderIterator& aIter, 1.6838 + BCPixelSize aHorSegHeight) 1.6839 +{ 1.6840 + mozilla::css::Side ownerSide = NS_SIDE_TOP; 1.6841 + nscoord cornerSubWidth = 0; 1.6842 + bool bevel = false; 1.6843 + if (aIter.mBCData) { 1.6844 + cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel); 1.6845 + } 1.6846 + mIsBottomBevel = (mWidth > 0) ? bevel : false; 1.6847 + mBottomHorSegHeight = std::max(aIter.mPrevHorSegHeight, aHorSegHeight); 1.6848 + mBottomOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth, 1.6849 + mBottomHorSegHeight, 1.6850 + false, mIsBottomBevel); 1.6851 + mLength += mBottomOffset; 1.6852 +} 1.6853 + 1.6854 +/** 1.6855 + * Paint the vertical segment 1.6856 + * @param aIter - iterator containing the structural information 1.6857 + * @param aRenderingContext - the rendering context 1.6858 + * @param aHorSegHeight - the width of the horizontal segment joining the corner 1.6859 + * at the start 1.6860 + */ 1.6861 +void 1.6862 +BCVerticalSeg::Paint(BCPaintBorderIterator& aIter, 1.6863 + nsRenderingContext& aRenderingContext, 1.6864 + BCPixelSize aHorSegHeight) 1.6865 +{ 1.6866 + // get the border style, color and paint the segment 1.6867 + mozilla::css::Side side = (aIter.IsDamageAreaRightMost()) ? NS_SIDE_RIGHT : 1.6868 + NS_SIDE_LEFT; 1.6869 + int32_t relColIndex = aIter.GetRelativeColIndex(); 1.6870 + nsTableColFrame* col = mCol; if (!col) ABORT0(); 1.6871 + nsTableCellFrame* cell = mFirstCell; // ??? 1.6872 + nsIFrame* owner = nullptr; 1.6873 + uint8_t style = NS_STYLE_BORDER_STYLE_SOLID; 1.6874 + nscolor color = 0xFFFFFFFF; 1.6875 + 1.6876 + switch (mOwner) { 1.6877 + case eTableOwner: 1.6878 + owner = aIter.mTable; 1.6879 + break; 1.6880 + case eAjaColGroupOwner: 1.6881 + side = NS_SIDE_RIGHT; 1.6882 + if (!aIter.IsTableRightMost() && (relColIndex > 0)) { 1.6883 + col = aIter.mVerInfo[relColIndex - 1].mCol; 1.6884 + } // and fall through 1.6885 + case eColGroupOwner: 1.6886 + if (col) { 1.6887 + owner = col->GetParent(); 1.6888 + } 1.6889 + break; 1.6890 + case eAjaColOwner: 1.6891 + side = NS_SIDE_RIGHT; 1.6892 + if (!aIter.IsTableRightMost() && (relColIndex > 0)) { 1.6893 + col = aIter.mVerInfo[relColIndex - 1].mCol; 1.6894 + } // and fall through 1.6895 + case eColOwner: 1.6896 + owner = col; 1.6897 + break; 1.6898 + case eAjaRowGroupOwner: 1.6899 + NS_ERROR("a neighboring rowgroup can never own a vertical border"); 1.6900 + // and fall through 1.6901 + case eRowGroupOwner: 1.6902 + NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(), 1.6903 + "row group can own border only at table edge"); 1.6904 + owner = mFirstRowGroup; 1.6905 + break; 1.6906 + case eAjaRowOwner: 1.6907 + NS_ASSERTION(false, "program error"); // and fall through 1.6908 + case eRowOwner: 1.6909 + NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(), 1.6910 + "row can own border only at table edge"); 1.6911 + owner = mFirstRow; 1.6912 + break; 1.6913 + case eAjaCellOwner: 1.6914 + side = NS_SIDE_RIGHT; 1.6915 + cell = mAjaCell; // and fall through 1.6916 + case eCellOwner: 1.6917 + owner = cell; 1.6918 + break; 1.6919 + } 1.6920 + if (owner) { 1.6921 + ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR); 1.6922 + } 1.6923 + BCPixelSize smallHalf, largeHalf; 1.6924 + DivideBCBorderSize(mWidth, smallHalf, largeHalf); 1.6925 + nsRect segRect(mOffsetX - nsPresContext::CSSPixelsToAppUnits(largeHalf), 1.6926 + mOffsetY, 1.6927 + nsPresContext::CSSPixelsToAppUnits(mWidth), mLength); 1.6928 + nscoord bottomBevelOffset = (mIsBottomBevel) ? 1.6929 + nsPresContext::CSSPixelsToAppUnits(mBottomHorSegHeight) : 0; 1.6930 + mozilla::css::Side bottomBevelSide = ((aHorSegHeight > 0) ^ !aIter.mTableIsLTR) ? 1.6931 + NS_SIDE_RIGHT : NS_SIDE_LEFT; 1.6932 + mozilla::css::Side topBevelSide = ((mTopBevelSide == NS_SIDE_RIGHT) ^ !aIter.mTableIsLTR)? 1.6933 + NS_SIDE_RIGHT : NS_SIDE_LEFT; 1.6934 + nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, 1.6935 + aIter.mTableBgColor, segRect, 1.6936 + nsPresContext::AppUnitsPerCSSPixel(), 1.6937 + topBevelSide, mTopBevelOffset, 1.6938 + bottomBevelSide, bottomBevelOffset); 1.6939 +} 1.6940 + 1.6941 +/** 1.6942 + * Advance the start point of a segment 1.6943 + */ 1.6944 +void 1.6945 +BCVerticalSeg::AdvanceOffsetY() 1.6946 +{ 1.6947 + mOffsetY += mLength - mBottomOffset; 1.6948 +} 1.6949 + 1.6950 +/** 1.6951 + * Accumulate the current segment 1.6952 + */ 1.6953 +void 1.6954 +BCVerticalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter) 1.6955 +{ 1.6956 + mLastCell = aIter.mCell; 1.6957 + mLength += aIter.mRow->GetRect().height; 1.6958 +} 1.6959 + 1.6960 +BCHorizontalSeg::BCHorizontalSeg() 1.6961 +{ 1.6962 + mOffsetX = mOffsetY = mLength = mWidth = mLeftBevelOffset = 0; 1.6963 + mLeftBevelSide = NS_SIDE_TOP; 1.6964 + mFirstCell = mAjaCell = nullptr; 1.6965 +} 1.6966 + 1.6967 +/** Initialize a horizontal border segment for painting 1.6968 + * @param aIter - iterator storing the current and adjacent frames 1.6969 + * @param aBorderOwner - which frame owns the border 1.6970 + * @param aBottomVerSegWidth - vertical segment width coming from up 1.6971 + * @param aHorSegHeight - the height of the segment 1.6972 + + */ 1.6973 +void 1.6974 +BCHorizontalSeg::Start(BCPaintBorderIterator& aIter, 1.6975 + BCBorderOwner aBorderOwner, 1.6976 + BCPixelSize aBottomVerSegWidth, 1.6977 + BCPixelSize aHorSegHeight) 1.6978 +{ 1.6979 + mozilla::css::Side cornerOwnerSide = NS_SIDE_TOP; 1.6980 + bool bevel = false; 1.6981 + 1.6982 + mOwner = aBorderOwner; 1.6983 + nscoord cornerSubWidth = (aIter.mBCData) ? 1.6984 + aIter.mBCData->GetCorner(cornerOwnerSide, 1.6985 + bevel) : 0; 1.6986 + 1.6987 + bool leftBevel = (aHorSegHeight > 0) ? bevel : false; 1.6988 + int32_t relColIndex = aIter.GetRelativeColIndex(); 1.6989 + nscoord maxVerSegWidth = std::max(aIter.mVerInfo[relColIndex].mWidth, 1.6990 + aBottomVerSegWidth); 1.6991 + nscoord offset = CalcHorCornerOffset(cornerOwnerSide, cornerSubWidth, 1.6992 + maxVerSegWidth, true, leftBevel, 1.6993 + aIter.mTableIsLTR); 1.6994 + mLeftBevelOffset = (leftBevel && (aHorSegHeight > 0)) ? maxVerSegWidth : 0; 1.6995 + // XXX this assumes that only corners where 2 segments join can be beveled 1.6996 + mLeftBevelSide = (aBottomVerSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; 1.6997 + if (aIter.mTableIsLTR) { 1.6998 + mOffsetX += offset; 1.6999 + } 1.7000 + else { 1.7001 + mOffsetX -= offset; 1.7002 + } 1.7003 + mLength = -offset; 1.7004 + mWidth = aHorSegHeight; 1.7005 + mFirstCell = aIter.mCell; 1.7006 + mAjaCell = (aIter.IsDamageAreaTopMost()) ? nullptr : 1.7007 + aIter.mVerInfo[relColIndex].mLastCell; 1.7008 +} 1.7009 + 1.7010 +/** 1.7011 + * Compute the offsets for the right corner of a horizontal segment 1.7012 + * @param aIter - iterator containing the structural information 1.7013 + * @param aLeftSegWidth - the width of the vertical segment joining the corner 1.7014 + * at the start 1.7015 + */ 1.7016 +void 1.7017 +BCHorizontalSeg::GetRightCorner(BCPaintBorderIterator& aIter, 1.7018 + BCPixelSize aLeftSegWidth) 1.7019 +{ 1.7020 + mozilla::css::Side ownerSide = NS_SIDE_TOP; 1.7021 + nscoord cornerSubWidth = 0; 1.7022 + bool bevel = false; 1.7023 + if (aIter.mBCData) { 1.7024 + cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel); 1.7025 + } 1.7026 + 1.7027 + mIsRightBevel = (mWidth > 0) ? bevel : 0; 1.7028 + int32_t relColIndex = aIter.GetRelativeColIndex(); 1.7029 + nscoord verWidth = std::max(aIter.mVerInfo[relColIndex].mWidth, aLeftSegWidth); 1.7030 + mEndOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth, 1.7031 + false, mIsRightBevel, aIter.mTableIsLTR); 1.7032 + mLength += mEndOffset; 1.7033 + mRightBevelOffset = (mIsRightBevel) ? 1.7034 + nsPresContext::CSSPixelsToAppUnits(verWidth) : 0; 1.7035 + mRightBevelSide = (aLeftSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; 1.7036 +} 1.7037 + 1.7038 +/** 1.7039 + * Paint the horizontal segment 1.7040 + * @param aIter - iterator containing the structural information 1.7041 + * @param aRenderingContext - the rendering context 1.7042 + */ 1.7043 +void 1.7044 +BCHorizontalSeg::Paint(BCPaintBorderIterator& aIter, 1.7045 + nsRenderingContext& aRenderingContext) 1.7046 +{ 1.7047 + // get the border style, color and paint the segment 1.7048 + mozilla::css::Side side = (aIter.IsDamageAreaBottomMost()) ? NS_SIDE_BOTTOM : 1.7049 + NS_SIDE_TOP; 1.7050 + nsIFrame* rg = aIter.mRg; if (!rg) ABORT0(); 1.7051 + nsIFrame* row = aIter.mRow; if (!row) ABORT0(); 1.7052 + nsIFrame* cell = mFirstCell; 1.7053 + nsIFrame* col; 1.7054 + nsIFrame* owner = nullptr; 1.7055 + 1.7056 + uint8_t style = NS_STYLE_BORDER_STYLE_SOLID; 1.7057 + nscolor color = 0xFFFFFFFF; 1.7058 + 1.7059 + 1.7060 + switch (mOwner) { 1.7061 + case eTableOwner: 1.7062 + owner = aIter.mTable; 1.7063 + break; 1.7064 + case eAjaColGroupOwner: 1.7065 + NS_ERROR("neighboring colgroups can never own a horizontal border"); 1.7066 + // and fall through 1.7067 + case eColGroupOwner: 1.7068 + NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(), 1.7069 + "col group can own border only at the table edge"); 1.7070 + col = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1); 1.7071 + if (!col) ABORT0(); 1.7072 + owner = col->GetParent(); 1.7073 + break; 1.7074 + case eAjaColOwner: 1.7075 + NS_ERROR("neighboring column can never own a horizontal border"); 1.7076 + // and fall through 1.7077 + case eColOwner: 1.7078 + NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(), 1.7079 + "col can own border only at the table edge"); 1.7080 + owner = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1); 1.7081 + break; 1.7082 + case eAjaRowGroupOwner: 1.7083 + side = NS_SIDE_BOTTOM; 1.7084 + rg = (aIter.IsTableBottomMost()) ? aIter.mRg : aIter.mPrevRg; 1.7085 + // and fall through 1.7086 + case eRowGroupOwner: 1.7087 + owner = rg; 1.7088 + break; 1.7089 + case eAjaRowOwner: 1.7090 + side = NS_SIDE_BOTTOM; 1.7091 + row = (aIter.IsTableBottomMost()) ? aIter.mRow : aIter.mPrevRow; 1.7092 + // and fall through 1.7093 + case eRowOwner: 1.7094 + owner = row; 1.7095 + break; 1.7096 + case eAjaCellOwner: 1.7097 + side = NS_SIDE_BOTTOM; 1.7098 + // if this is null due to the damage area origin-y > 0, then the border 1.7099 + // won't show up anyway 1.7100 + cell = mAjaCell; 1.7101 + // and fall through 1.7102 + case eCellOwner: 1.7103 + owner = cell; 1.7104 + break; 1.7105 + } 1.7106 + if (owner) { 1.7107 + ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR); 1.7108 + } 1.7109 + BCPixelSize smallHalf, largeHalf; 1.7110 + DivideBCBorderSize(mWidth, smallHalf, largeHalf); 1.7111 + nsRect segRect(mOffsetX, 1.7112 + mOffsetY - nsPresContext::CSSPixelsToAppUnits(largeHalf), 1.7113 + mLength, 1.7114 + nsPresContext::CSSPixelsToAppUnits(mWidth)); 1.7115 + if (aIter.mTableIsLTR) { 1.7116 + nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, 1.7117 + aIter.mTableBgColor, segRect, 1.7118 + nsPresContext::AppUnitsPerCSSPixel(), 1.7119 + mLeftBevelSide, 1.7120 + nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset), 1.7121 + mRightBevelSide, mRightBevelOffset); 1.7122 + } 1.7123 + else { 1.7124 + segRect.x -= segRect.width; 1.7125 + nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, 1.7126 + aIter.mTableBgColor, segRect, 1.7127 + nsPresContext::AppUnitsPerCSSPixel(), 1.7128 + mRightBevelSide, mRightBevelOffset, 1.7129 + mLeftBevelSide, 1.7130 + nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset)); 1.7131 + } 1.7132 +} 1.7133 + 1.7134 +/** 1.7135 + * Advance the start point of a segment 1.7136 + */ 1.7137 +void 1.7138 +BCHorizontalSeg::AdvanceOffsetX(int32_t aIncrement) 1.7139 +{ 1.7140 + mOffsetX += aIncrement * (mLength - mEndOffset); 1.7141 +} 1.7142 + 1.7143 +/** 1.7144 + * Accumulate the current segment 1.7145 + */ 1.7146 +void 1.7147 +BCHorizontalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter) 1.7148 +{ 1.7149 + mLength += aIter.mVerInfo[aIter.GetRelativeColIndex()].mColWidth; 1.7150 +} 1.7151 + 1.7152 +/** 1.7153 + * store the column width information while painting horizontal segment 1.7154 + */ 1.7155 +void 1.7156 +BCPaintBorderIterator::StoreColumnWidth(int32_t aIndex) 1.7157 +{ 1.7158 + if (IsTableRightMost()) { 1.7159 + mVerInfo[aIndex].mColWidth = mVerInfo[aIndex - 1].mColWidth; 1.7160 + } 1.7161 + else { 1.7162 + nsTableColFrame* col = mTableFirstInFlow->GetColFrame(mColIndex); 1.7163 + if (!col) ABORT0(); 1.7164 + mVerInfo[aIndex].mColWidth = col->GetSize().width; 1.7165 + } 1.7166 +} 1.7167 +/** 1.7168 + * Determine if a vertical segment owns the corder 1.7169 + */ 1.7170 +bool 1.7171 +BCPaintBorderIterator::VerticalSegmentOwnsCorner() 1.7172 +{ 1.7173 + mozilla::css::Side cornerOwnerSide = NS_SIDE_TOP; 1.7174 + bool bevel = false; 1.7175 + if (mBCData) { 1.7176 + mBCData->GetCorner(cornerOwnerSide, bevel); 1.7177 + } 1.7178 + // unitialized ownerside, bevel 1.7179 + return (NS_SIDE_TOP == cornerOwnerSide) || 1.7180 + (NS_SIDE_BOTTOM == cornerOwnerSide); 1.7181 +} 1.7182 + 1.7183 +/** 1.7184 + * Paint if necessary a horizontal segment, otherwise accumulate it 1.7185 + * @param aRenderingContext - the rendering context 1.7186 + */ 1.7187 +void 1.7188 +BCPaintBorderIterator::AccumulateOrPaintHorizontalSegment(nsRenderingContext& aRenderingContext) 1.7189 +{ 1.7190 + 1.7191 + int32_t relColIndex = GetRelativeColIndex(); 1.7192 + // store the current col width if it hasn't been already 1.7193 + if (mVerInfo[relColIndex].mColWidth < 0) { 1.7194 + StoreColumnWidth(relColIndex); 1.7195 + } 1.7196 + 1.7197 + BCBorderOwner borderOwner = eCellOwner; 1.7198 + BCBorderOwner ignoreBorderOwner; 1.7199 + bool isSegStart = true; 1.7200 + bool ignoreSegStart; 1.7201 + 1.7202 + nscoord leftSegWidth = 1.7203 + mBCData ? mBCData->GetLeftEdge(ignoreBorderOwner, ignoreSegStart) : 0; 1.7204 + nscoord topSegHeight = 1.7205 + mBCData ? mBCData->GetTopEdge(borderOwner, isSegStart) : 0; 1.7206 + 1.7207 + if (mIsNewRow || (IsDamageAreaLeftMost() && IsDamageAreaBottomMost())) { 1.7208 + // reset for every new row and on the bottom of the last row 1.7209 + mHorSeg.mOffsetY = mNextOffsetY; 1.7210 + mNextOffsetY = mNextOffsetY + mRow->GetSize().height; 1.7211 + mHorSeg.mOffsetX = mInitialOffsetX; 1.7212 + mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight); 1.7213 + } 1.7214 + 1.7215 + if (!IsDamageAreaLeftMost() && (isSegStart || IsDamageAreaRightMost() || 1.7216 + VerticalSegmentOwnsCorner())) { 1.7217 + // paint the previous seg or the current one if IsDamageAreaRightMost() 1.7218 + if (mHorSeg.mLength > 0) { 1.7219 + mHorSeg.GetRightCorner(*this, leftSegWidth); 1.7220 + if (mHorSeg.mWidth > 0) { 1.7221 + mHorSeg.Paint(*this, aRenderingContext); 1.7222 + } 1.7223 + mHorSeg.AdvanceOffsetX(mColInc); 1.7224 + } 1.7225 + mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight); 1.7226 + } 1.7227 + mHorSeg.IncludeCurrentBorder(*this); 1.7228 + mVerInfo[relColIndex].mWidth = leftSegWidth; 1.7229 + mVerInfo[relColIndex].mLastCell = mCell; 1.7230 +} 1.7231 +/** 1.7232 + * Paint if necessary a vertical segment, otherwise it 1.7233 + * @param aRenderingContext - the rendering context 1.7234 + */ 1.7235 +void 1.7236 +BCPaintBorderIterator::AccumulateOrPaintVerticalSegment(nsRenderingContext& aRenderingContext) 1.7237 +{ 1.7238 + BCBorderOwner borderOwner = eCellOwner; 1.7239 + BCBorderOwner ignoreBorderOwner; 1.7240 + bool isSegStart = true; 1.7241 + bool ignoreSegStart; 1.7242 + 1.7243 + nscoord verSegWidth = 1.7244 + mBCData ? mBCData->GetLeftEdge(borderOwner, isSegStart) : 0; 1.7245 + nscoord horSegHeight = 1.7246 + mBCData ? mBCData->GetTopEdge(ignoreBorderOwner, ignoreSegStart) : 0; 1.7247 + 1.7248 + int32_t relColIndex = GetRelativeColIndex(); 1.7249 + BCVerticalSeg& verSeg = mVerInfo[relColIndex]; 1.7250 + if (!verSeg.mCol) { // on the first damaged row and the first segment in the 1.7251 + // col 1.7252 + verSeg.Initialize(*this); 1.7253 + verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight); 1.7254 + } 1.7255 + 1.7256 + if (!IsDamageAreaTopMost() && (isSegStart || IsDamageAreaBottomMost() || 1.7257 + IsAfterRepeatedHeader() || 1.7258 + StartRepeatedFooter())) { 1.7259 + // paint the previous seg or the current one if IsDamageAreaBottomMost() 1.7260 + if (verSeg.mLength > 0) { 1.7261 + verSeg.GetBottomCorner(*this, horSegHeight); 1.7262 + if (verSeg.mWidth > 0) { 1.7263 + verSeg.Paint(*this, aRenderingContext, horSegHeight); 1.7264 + } 1.7265 + verSeg.AdvanceOffsetY(); 1.7266 + } 1.7267 + verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight); 1.7268 + } 1.7269 + verSeg.IncludeCurrentBorder(*this); 1.7270 + mPrevHorSegHeight = horSegHeight; 1.7271 +} 1.7272 + 1.7273 +/** 1.7274 + * Reset the vertical information cache 1.7275 + */ 1.7276 +void 1.7277 +BCPaintBorderIterator::ResetVerInfo() 1.7278 +{ 1.7279 + if (mVerInfo) { 1.7280 + memset(mVerInfo, 0, mDamageArea.width * sizeof(BCVerticalSeg)); 1.7281 + // XXX reinitialize properly 1.7282 + for (int32_t xIndex = 0; xIndex < mDamageArea.width; xIndex++) { 1.7283 + mVerInfo[xIndex].mColWidth = -1; 1.7284 + } 1.7285 + } 1.7286 +} 1.7287 + 1.7288 +/** 1.7289 + * Method to paint BCBorders, this does not use currently display lists although 1.7290 + * it will do this in future 1.7291 + * @param aRenderingContext - the rendering context 1.7292 + * @param aDirtyRect - inside this rectangle the BC Borders will redrawn 1.7293 + */ 1.7294 +void 1.7295 +nsTableFrame::PaintBCBorders(nsRenderingContext& aRenderingContext, 1.7296 + const nsRect& aDirtyRect) 1.7297 +{ 1.7298 + // We first transfer the aDirtyRect into cellmap coordinates to compute which 1.7299 + // cell borders need to be painted 1.7300 + BCPaintBorderIterator iter(this); 1.7301 + if (!iter.SetDamageArea(aDirtyRect)) 1.7302 + return; 1.7303 + 1.7304 + // First, paint all of the vertical borders from top to bottom and left to 1.7305 + // right as they become complete. They are painted first, since they are less 1.7306 + // efficient to paint than horizontal segments. They were stored with as few 1.7307 + // segments as possible (since horizontal borders are painted last and 1.7308 + // possibly over them). For every cell in a row that fails in the damage are 1.7309 + // we look up if the current border would start a new segment, if so we paint 1.7310 + // the previously stored vertical segment and start a new segment. After 1.7311 + // this we the now active segment with the current border. These 1.7312 + // segments are stored in mVerInfo to be used on the next row 1.7313 + for (iter.First(); !iter.mAtEnd; iter.Next()) { 1.7314 + iter.AccumulateOrPaintVerticalSegment(aRenderingContext); 1.7315 + } 1.7316 + 1.7317 + // Next, paint all of the horizontal border segments from top to bottom reuse 1.7318 + // the mVerInfo array to keep track of col widths and vertical segments for 1.7319 + // corner calculations 1.7320 + iter.Reset(); 1.7321 + for (iter.First(); !iter.mAtEnd; iter.Next()) { 1.7322 + iter.AccumulateOrPaintHorizontalSegment(aRenderingContext); 1.7323 + } 1.7324 +} 1.7325 + 1.7326 +bool nsTableFrame::RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols) 1.7327 +{ 1.7328 + bool result = false; 1.7329 + nsTableCellMap* cellMap = GetCellMap(); 1.7330 + NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated."); 1.7331 + if (cellMap) { 1.7332 + result = cellMap->RowHasSpanningCells(aRowIndex, aNumEffCols); 1.7333 + } 1.7334 + return result; 1.7335 +} 1.7336 + 1.7337 +bool nsTableFrame::RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols) 1.7338 +{ 1.7339 + bool result = false; 1.7340 + nsTableCellMap* cellMap = GetCellMap(); 1.7341 + NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated."); 1.7342 + if (cellMap) { 1.7343 + result = cellMap->RowIsSpannedInto(aRowIndex, aNumEffCols); 1.7344 + } 1.7345 + return result; 1.7346 +} 1.7347 + 1.7348 +/* static */ 1.7349 +void 1.7350 +nsTableFrame::InvalidateTableFrame(nsIFrame* aFrame, 1.7351 + const nsRect& aOrigRect, 1.7352 + const nsRect& aOrigVisualOverflow, 1.7353 + bool aIsFirstReflow) 1.7354 +{ 1.7355 + nsIFrame* parent = aFrame->GetParent(); 1.7356 + NS_ASSERTION(parent, "What happened here?"); 1.7357 + 1.7358 + if (parent->GetStateBits() & NS_FRAME_FIRST_REFLOW) { 1.7359 + // Don't bother; we'll invalidate the parent's overflow rect when 1.7360 + // we finish reflowing it. 1.7361 + return; 1.7362 + } 1.7363 + 1.7364 + // The part that looks at both the rect and the overflow rect is a 1.7365 + // bit of a hack. See nsBlockFrame::ReflowLine for an eloquent 1.7366 + // description of its hackishness. 1.7367 + // 1.7368 + // This doesn't really make sense now that we have DLBI. 1.7369 + // This code can probably be simplified a fair bit. 1.7370 + nsRect visualOverflow = aFrame->GetVisualOverflowRect(); 1.7371 + if (aIsFirstReflow || 1.7372 + aOrigRect.TopLeft() != aFrame->GetPosition() || 1.7373 + aOrigVisualOverflow.TopLeft() != visualOverflow.TopLeft()) { 1.7374 + // Invalidate the old and new overflow rects. Note that if the 1.7375 + // frame moved, we can't just use aOrigVisualOverflow, since it's in 1.7376 + // coordinates relative to the old position. So invalidate via 1.7377 + // aFrame's parent, and reposition that overflow rect to the right 1.7378 + // place. 1.7379 + // XXXbz this doesn't handle outlines, does it? 1.7380 + aFrame->InvalidateFrame(); 1.7381 + parent->InvalidateFrameWithRect(aOrigVisualOverflow + aOrigRect.TopLeft()); 1.7382 + } else if (aOrigRect.Size() != aFrame->GetSize() || 1.7383 + aOrigVisualOverflow.Size() != visualOverflow.Size()){ 1.7384 + aFrame->InvalidateFrameWithRect(aOrigVisualOverflow); 1.7385 + aFrame->InvalidateFrame(); 1.7386 + parent->InvalidateFrameWithRect(aOrigRect);; 1.7387 + parent->InvalidateFrame(); 1.7388 + } 1.7389 +}