layout/tables/nsTableRowFrame.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial