Bug 1107783 - part 1, [css-grid] Implement abs.pos. grid item placement and reflow. r=dholbert

This commit is contained in:
Mats Palmgren 2015-03-26 18:57:39 +00:00
parent 2c7b1ae84e
commit d7f28003bb
3 changed files with 282 additions and 4 deletions

View File

@ -16,6 +16,7 @@
#include "nsHTMLReflowState.h" #include "nsHTMLReflowState.h"
#include "nsPresContext.h" #include "nsPresContext.h"
#include "nsCSSFrameConstructor.h" #include "nsCSSFrameConstructor.h"
#include "nsGridContainerFrame.h"
#ifdef DEBUG #ifdef DEBUG
#include "nsBlockFrame.h" #include "nsBlockFrame.h"
@ -119,6 +120,14 @@ nsAbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame,
bool reflowAll = aReflowState.ShouldReflowAllKids(); bool reflowAll = aReflowState.ShouldReflowAllKids();
// The 'width' check below is an optimization to avoid the virtual GetType()
// call in most cases. 'aContainingBlock' isn't used for grid items,
// each item has its own CB on a frame property instead.
// @see nsGridContainerFrame::ReflowChildren
const bool isGrid =
aContainingBlock.width == nsGridContainerFrame::VERY_LIKELY_A_GRID_CONTAINER &&
aDelegatingFrame->GetType() == nsGkAtoms::gridContainerFrame;
nsIFrame* kidFrame; nsIFrame* kidFrame;
nsOverflowContinuationTracker tracker(aDelegatingFrame, true); nsOverflowContinuationTracker tracker(aDelegatingFrame, true);
for (kidFrame = mAbsoluteFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) { for (kidFrame = mAbsoluteFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) {
@ -127,8 +136,9 @@ nsAbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame,
if (kidNeedsReflow && !aPresContext->HasPendingInterrupt()) { if (kidNeedsReflow && !aPresContext->HasPendingInterrupt()) {
// Reflow the frame // Reflow the frame
nsReflowStatus kidStatus = NS_FRAME_COMPLETE; nsReflowStatus kidStatus = NS_FRAME_COMPLETE;
ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowState, const nsRect& cb = isGrid ? nsGridContainerFrame::GridItemCB(kidFrame)
aContainingBlock, : aContainingBlock;
ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowState, cb,
aConstrainHeight, kidFrame, kidStatus, aConstrainHeight, kidFrame, kidStatus,
aOverflowAreas); aOverflowAreas);
nsIFrame* nextFrame = kidFrame->GetNextInFlow(); nsIFrame* nextFrame = kidFrame->GetNextInFlow();

View File

@ -9,9 +9,12 @@
#include "nsGridContainerFrame.h" #include "nsGridContainerFrame.h"
#include "mozilla/Maybe.h" #include "mozilla/Maybe.h"
#include "nsAbsoluteContainingBlock.h"
#include "nsAlgorithm.h" // for clamped()
#include "nsCSSAnonBoxes.h" #include "nsCSSAnonBoxes.h"
#include "nsDataHashtable.h" #include "nsDataHashtable.h"
#include "nsHashKeys.h" #include "nsHashKeys.h"
#include "nsIFrameInlines.h"
#include "nsPresContext.h" #include "nsPresContext.h"
#include "nsReadableUtils.h" #include "nsReadableUtils.h"
#include "nsRuleNode.h" #include "nsRuleNode.h"
@ -146,6 +149,19 @@ IsNameWithStartSuffix(const nsString& aString, uint32_t* aIndex)
return IsNameWithSuffix(aString, NS_LITERAL_STRING("-start"), aIndex); return IsNameWithSuffix(aString, NS_LITERAL_STRING("-start"), aIndex);
} }
static nscoord
GridLinePosition(uint32_t aLine, const nsTArray<TrackSize>& aTrackSizes)
{
MOZ_ASSERT(aLine != 0, "expected a 1-based line number");
const uint32_t endIndex = aLine - 1;
MOZ_ASSERT(endIndex <= aTrackSizes.Length(), "aTrackSizes is too small");
nscoord pos = 0;
for (uint32_t i = 0; i < endIndex; ++i) {
pos += aTrackSizes[i].mBase;
}
return pos;
}
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Frame class boilerplate // Frame class boilerplate
@ -170,6 +186,18 @@ NS_NewGridContainerFrame(nsIPresShell* aPresShell,
// nsGridContainerFrame Method Implementations // nsGridContainerFrame Method Implementations
// =========================================== // ===========================================
/*static*/ const nsRect&
nsGridContainerFrame::GridItemCB(nsIFrame* aChild)
{
MOZ_ASSERT((aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
aChild->IsAbsolutelyPositioned());
nsRect* cb = static_cast<nsRect*>(aChild->Properties().Get(
GridItemContainingBlockRect()));
MOZ_ASSERT(cb, "this method must only be called on grid items, and the grid "
"container should've reflowed this item by now and set up cb");
return *cb;
}
void void
nsGridContainerFrame::AddImplicitNamedAreas( nsGridContainerFrame::AddImplicitNamedAreas(
const nsTArray<nsTArray<nsString>>& aLineNameLists) const nsTArray<nsTArray<nsString>>& aLineNameLists)
@ -420,6 +448,53 @@ nsGridContainerFrame::PlaceDefinite(nsIFrame* aChild,
mExplicitGridRowEnd, aStyle)); mExplicitGridRowEnd, aStyle));
} }
nsGridContainerFrame::LineRange
nsGridContainerFrame::ResolveAbsPosLineRange(
const nsStyleGridLine& aStart,
const nsStyleGridLine& aEnd,
const nsTArray<nsTArray<nsString>>& aLineNameList,
uint32_t GridNamedArea::* aAreaStart,
uint32_t GridNamedArea::* aAreaEnd,
uint32_t aExplicitGridEnd,
uint32_t aGridEnd,
const nsStylePosition* aStyle)
{
if (aStart.IsAuto()) {
if (aEnd.IsAuto()) {
return LineRange(0, 0);
}
uint32_t end = ResolveLine(aEnd, aEnd.mInteger, 0, aLineNameList, aAreaStart,
aAreaEnd, aExplicitGridEnd, eLineRangeSideEnd,
aStyle);
MOZ_ASSERT(end != 0, "resolving non-auto line shouldn't result in auto");
if (aEnd.mHasSpan) {
++end;
}
return LineRange(0, clamped(end, 1U, aGridEnd));
}
if (aEnd.IsAuto()) {
uint32_t start =
ResolveLine(aStart, aStart.mInteger, 0, aLineNameList, aAreaStart,
aAreaEnd, aExplicitGridEnd, eLineRangeSideStart, aStyle);
MOZ_ASSERT(start != 0, "resolving non-auto line shouldn't result in auto");
if (aStart.mHasSpan) {
start = std::max(int32_t(aGridEnd) - int32_t(start), 1);
}
return LineRange(clamped(start, 1U, aGridEnd), 0);
}
LineRange r = ResolveLineRange(aStart, aEnd, aLineNameList, aAreaStart,
aAreaEnd, aExplicitGridEnd, aStyle);
MOZ_ASSERT(!r.IsAuto(), "resolving definite lines shouldn't result in auto");
// Clamp definite lines to be within the implicit grid.
// Note that this implies mStart may be equal to mEnd.
r.mStart = clamped(r.mStart, 1U, aGridEnd);
r.mEnd = clamped(r.mEnd, 1U, aGridEnd);
MOZ_ASSERT(r.mStart <= r.mEnd);
return r;
}
uint32_t uint32_t
nsGridContainerFrame::FindAutoCol(uint32_t aStartCol, uint32_t aLockedRow, nsGridContainerFrame::FindAutoCol(uint32_t aStartCol, uint32_t aLockedRow,
const GridArea* aArea) const const GridArea* aArea) const
@ -461,6 +536,26 @@ nsGridContainerFrame::FindAutoCol(uint32_t aStartCol, uint32_t aLockedRow,
return candidate + 1; // return a 1-based column number return candidate + 1; // return a 1-based column number
} }
nsGridContainerFrame::GridArea
nsGridContainerFrame::PlaceAbsPos(nsIFrame* aChild,
const nsStylePosition* aStyle)
{
const nsStylePosition* itemStyle = aChild->StylePosition();
return GridArea(
ResolveAbsPosLineRange(itemStyle->mGridColumnStart,
itemStyle->mGridColumnEnd,
aStyle->mGridTemplateColumns.mLineNameLists,
&GridNamedArea::mColumnStart,
&GridNamedArea::mColumnEnd,
mExplicitGridColEnd, mGridColEnd, aStyle),
ResolveAbsPosLineRange(itemStyle->mGridRowStart,
itemStyle->mGridRowEnd,
aStyle->mGridTemplateRows.mLineNameLists,
&GridNamedArea::mRowStart,
&GridNamedArea::mRowEnd,
mExplicitGridRowEnd, mGridRowEnd, aStyle));
}
void void
nsGridContainerFrame::PlaceAutoCol(uint32_t aStartCol, GridArea* aArea) const nsGridContainerFrame::PlaceAutoCol(uint32_t aStartCol, GridArea* aArea) const
{ {
@ -687,6 +782,24 @@ nsGridContainerFrame::PlaceGridItems(const nsStylePosition* aStyle)
InflateGridFor(*area); InflateGridFor(*area);
} }
} }
if (IsAbsoluteContainer()) {
// 9.4 Absolutely-positioned Grid Items
// http://dev.w3.org/csswg/css-grid/#abspos-items
// We only resolve definite lines here; we'll align auto positions to the
// grid container later during reflow.
nsFrameList children(GetChildList(GetAbsoluteListID()));
for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
nsIFrame* child = e.get();
GridArea area(PlaceAbsPos(child, aStyle));
GridArea* prop = GetGridAreaForChild(child);
if (prop) {
*prop = area;
} else {
child->Properties().Set(GridAreaProperty(), new GridArea(area));
}
}
}
} }
static void static void
@ -772,7 +885,9 @@ void
nsGridContainerFrame::LineRange::ToPositionAndLength( nsGridContainerFrame::LineRange::ToPositionAndLength(
const nsTArray<TrackSize>& aTrackSizes, nscoord* aPos, nscoord* aLength) const const nsTArray<TrackSize>& aTrackSizes, nscoord* aPos, nscoord* aLength) const
{ {
MOZ_ASSERT(mStart != 0 && Extent() > 0, "expected a definite LineRange"); //MOZ_ASSERT(mStart != 0 && Extent() > 0, "expected a definite LineRange");
// XXX relaxed it a bit for abs.pos. items for now:
MOZ_ASSERT(mStart != 0 && mEnd != 0, "expected a definite LineRange");
nscoord pos = 0; nscoord pos = 0;
const uint32_t start = mStart - 1; const uint32_t start = mStart - 1;
uint32_t i = 0; uint32_t i = 0;
@ -790,6 +905,34 @@ nsGridContainerFrame::LineRange::ToPositionAndLength(
*aLength = length; *aLength = length;
} }
void
nsGridContainerFrame::LineRange::ToPositionAndLengthForAbsPos(
const nsTArray<TrackSize>& aTrackSizes, nscoord aGridOrigin,
nscoord* aPos, nscoord* aLength) const
{
// A "0" line represents "auto", which for abspos children, contributes
// the corresponding edge of the grid's padding-box.
if (mEnd == 0) {
if (mStart == 0) {
// done
} else {
const nscoord endPos = *aPos + *aLength;
nscoord startPos = ::GridLinePosition(mStart, aTrackSizes);
*aPos = aGridOrigin + startPos;
*aLength = std::max(endPos - *aPos, 0);
}
} else {
if (mStart == 0) {
nscoord endPos = ::GridLinePosition(mEnd, aTrackSizes);
*aLength = std::max(aGridOrigin + endPos, 0);
} else {
nscoord pos;
ToPositionAndLength(aTrackSizes, &pos, aLength);
*aPos = aGridOrigin + pos;
}
}
}
LogicalRect LogicalRect
nsGridContainerFrame::ContainingBlockFor( nsGridContainerFrame::ContainingBlockFor(
const WritingMode& aWM, const WritingMode& aWM,
@ -803,6 +946,26 @@ nsGridContainerFrame::ContainingBlockFor(
return LogicalRect(aWM, i, b, iSize, bSize); return LogicalRect(aWM, i, b, iSize, bSize);
} }
LogicalRect
nsGridContainerFrame::ContainingBlockForAbsPos(
const WritingMode& aWM,
const GridArea& aArea,
const nsTArray<TrackSize>& aColSizes,
const nsTArray<TrackSize>& aRowSizes,
const LogicalPoint& aGridOrigin,
const LogicalRect& aGridCB) const
{
nscoord i = aGridCB.IStart(aWM);
nscoord b = aGridCB.BStart(aWM);
nscoord iSize = aGridCB.ISize(aWM);
nscoord bSize = aGridCB.BSize(aWM);
aArea.mCols.ToPositionAndLengthForAbsPos(aColSizes, aGridOrigin.I(aWM),
&i, &iSize);
aArea.mRows.ToPositionAndLengthForAbsPos(aRowSizes, aGridOrigin.B(aWM),
&b, &bSize);
return LogicalRect(aWM, i, b, iSize, bSize);
}
void void
nsGridContainerFrame::ReflowChildren(const LogicalRect& aContentArea, nsGridContainerFrame::ReflowChildren(const LogicalRect& aContentArea,
const nsTArray<TrackSize>& aColSizes, const nsTArray<TrackSize>& aColSizes,
@ -843,6 +1006,44 @@ nsGridContainerFrame::ReflowChildren(const LogicalRect& aContentArea,
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child); ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child);
// XXX deal with 'childStatus' not being COMPLETE // XXX deal with 'childStatus' not being COMPLETE
} }
if (IsAbsoluteContainer()) {
nsFrameList children(GetChildList(GetAbsoluteListID()));
if (!children.IsEmpty()) {
LogicalMargin pad(aReflowState.ComputedLogicalPadding());
pad.ApplySkipSides(GetLogicalSkipSides(&aReflowState));
// 'gridOrigin' is the origin of the grid (the start of the first track),
// with respect to the grid container's padding-box (CB).
const LogicalPoint gridOrigin(wm, pad.IStart(wm), pad.BStart(wm));
const LogicalRect gridCB(wm, 0, 0,
aContentArea.ISize(wm) + pad.IStartEnd(wm),
aContentArea.BSize(wm) + pad.BStartEnd(wm));
for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
nsIFrame* child = e.get();
GridArea* area = GetGridAreaForChild(child);
MOZ_ASSERT(area);
LogicalRect itemCB(ContainingBlockForAbsPos(wm, *area,
aColSizes, aRowSizes,
gridOrigin, gridCB));
// nsAbsoluteContainingBlock::Reflow uses physical coordinates.
nsRect* cb = static_cast<nsRect*>(child->Properties().Get(
GridItemContainingBlockRect()));
if (!cb) {
cb = new nsRect;
child->Properties().Set(GridItemContainingBlockRect(), cb);
}
*cb = itemCB.GetPhysicalRect(wm, gridWidth);
}
// This rect isn't used at all for layout so we use it to optimize
// away the virtual GetType() call in the callee in most cases.
// @see nsAbsoluteContainingBlock::Reflow
nsRect dummyRect(0, 0, VERY_LIKELY_A_GRID_CONTAINER, 0);
GetAbsoluteContainingBlock()->Reflow(this, pc, aReflowState, aStatus,
dummyRect, true,
true, true, // XXX could be optimized
&aDesiredSize.mOverflowAreas);
}
}
} }
void void

View File

@ -38,12 +38,25 @@ public:
virtual nsresult GetFrameName(nsAString& aResult) const override; virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif #endif
/**
* Return the containing block for aChild which MUST be an abs.pos. child
* of a grid container. This is just a helper method for
* nsAbsoluteContainingBlock::Reflow - it's not meant to be used elsewhere.
*/
static const nsRect& GridItemCB(nsIFrame* aChild);
struct TrackSize { struct TrackSize {
nscoord mBase; nscoord mBase;
nscoord mLimit; nscoord mLimit;
}; };
// @see nsAbsoluteContainingBlock::Reflow about this magic number
static const nscoord VERY_LIKELY_A_GRID_CONTAINER = -123456789;
NS_DECLARE_FRAME_PROPERTY(GridItemContainingBlockRect, DeleteValue<nsRect>)
protected: protected:
typedef mozilla::LogicalPoint LogicalPoint;
typedef mozilla::LogicalRect LogicalRect; typedef mozilla::LogicalRect LogicalRect;
typedef mozilla::WritingMode WritingMode; typedef mozilla::WritingMode WritingMode;
typedef mozilla::css::GridNamedArea GridNamedArea; typedef mozilla::css::GridNamedArea GridNamedArea;
@ -57,7 +70,12 @@ protected:
* (both 1-based) where mStart < mEnd. Before it's definite it can also * (both 1-based) where mStart < mEnd. Before it's definite it can also
* represent an auto position with a span, where mStart == 0 and mEnd is * represent an auto position with a span, where mStart == 0 and mEnd is
* the (non-zero positive) span. * the (non-zero positive) span.
* In both states the invariant mEnd > mStart holds. * In both states the invariant mEnd > mStart holds (for normal flow items).
*
* For abs.pos. grid items, mStart and mEnd may both be zero, meaning
* "attach this side to the grid container containing block edge".
* Additionally, mEnd >= mStart holds when both are definite (non-zero),
* i.e. the invariant is slightly relaxed compared to normal flow items.
*/ */
struct LineRange { struct LineRange {
LineRange(uint32_t aStart, uint32_t aEnd) LineRange(uint32_t aStart, uint32_t aEnd)
@ -88,6 +106,15 @@ protected:
*/ */
void ToPositionAndLength(const nsTArray<TrackSize>& aTrackSizes, void ToPositionAndLength(const nsTArray<TrackSize>& aTrackSizes,
nscoord* aPos, nscoord* aLength) const; nscoord* aPos, nscoord* aLength) const;
/**
* Given an array of track sizes and a grid origin coordinate, adjust the
* abs.pos. containing block along an axis given by aPos and aLength.
* aPos and aLength should already be initialized to the grid container
* containing block for this axis before calling this method.
*/
void ToPositionAndLengthForAbsPos(const nsTArray<TrackSize>& aTrackSizes,
nscoord aGridOrigin,
nscoord* aPos, nscoord* aLength) const;
uint32_t mStart; // the start line, or zero for 'auto' uint32_t mStart; // the start line, or zero for 'auto'
uint32_t mEnd; // the end line, or the span length for 'auto' uint32_t mEnd; // the end line, or the span length for 'auto'
@ -181,6 +208,21 @@ protected:
uint32_t aExplicitGridEnd, uint32_t aExplicitGridEnd,
const nsStylePosition* aStyle); const nsStylePosition* aStyle);
/**
* As above but for an abs.pos. child. Any 'auto' lines will be represented
* by zero in the LineRange result.
* @param aGridEnd the last line in the (final) implicit grid
*/
LineRange
ResolveAbsPosLineRange(const nsStyleGridLine& aStart,
const nsStyleGridLine& aEnd,
const nsTArray<nsTArray<nsString>>& aLineNameList,
uint32_t GridNamedArea::* aAreaStart,
uint32_t GridNamedArea::* aAreaEnd,
uint32_t aExplicitGridEnd,
uint32_t aGridEnd,
const nsStylePosition* aStyle);
/** /**
* Return a GridArea with non-auto lines placed at a definite line number * Return a GridArea with non-auto lines placed at a definite line number
* and with placement errors resolved. One or both positions may still be * and with placement errors resolved. One or both positions may still be
@ -244,6 +286,15 @@ protected:
void PlaceAutoAutoInColOrder(uint32_t aStartCol, uint32_t aStartRow, void PlaceAutoAutoInColOrder(uint32_t aStartCol, uint32_t aStartRow,
GridArea* aArea) const; GridArea* aArea) const;
/**
* Place an abs.pos. child and return its grid area.
* @note the resulting area may still have 'auto' lines in one or both
* dimensions (represented as zero).
* @param aChild the abs.pos. grid item to place
* @param aStyle the StylePosition() for the grid container
*/
GridArea PlaceAbsPos(nsIFrame* aChild, const nsStylePosition* aStyle);
/** /**
* Place all child frames into the grid and expand the (implicit) grid as * Place all child frames into the grid and expand the (implicit) grid as
* needed. The allocated GridAreas are stored in the GridAreaProperty * needed. The allocated GridAreas are stored in the GridAreaProperty
@ -331,6 +382,22 @@ protected:
const nsTArray<TrackSize>& aColSizes, const nsTArray<TrackSize>& aColSizes,
const nsTArray<TrackSize>& aRowSizes) const; const nsTArray<TrackSize>& aRowSizes) const;
/**
* Return the containing block for an abs.pos. grid item occupying aArea.
* Any 'auto' lines in the grid area will be aligned with grid container
* containing block on that side.
* @param aColSizes column track sizes
* @param aRowSizes row track sizes
* @param aGridOrigin the origin of the grid
* @param aGridCB the grid container containing block (its padding area)
*/
LogicalRect ContainingBlockForAbsPos(const WritingMode& aWM,
const GridArea& aArea,
const nsTArray<TrackSize>& aColSizes,
const nsTArray<TrackSize>& aRowSizes,
const LogicalPoint& aGridOrigin,
const LogicalRect& aGridCB) const;
/** /**
* Reflow and place our children. * Reflow and place our children.
*/ */