Bug 1281320 - [css-grid] Implement 'fit-content([ <length> | <percentage> ])' value for <track-size>. r=dholbert

This commit is contained in:
Mats Palmgren 2016-08-26 15:16:57 +02:00
Родитель 60eb164682
Коммит 15b4f3793a
6 изменённых файлов: 140 добавлений и 35 удалений

Просмотреть файл

@ -10,6 +10,7 @@
#include <algorithm> // for std::stable_sort
#include <limits>
#include "mozilla/Function.h"
#include "mozilla/Maybe.h"
#include "mozilla/PodOperations.h" // for PodZero
#include "nsAbsoluteContainingBlock.h"
@ -149,6 +150,7 @@ struct nsGridContainerFrame::TrackSize
eSkipGrowUnlimited2 = 0x400,
eSkipGrowUnlimited = eSkipGrowUnlimited1 | eSkipGrowUnlimited2,
eBreakBefore = 0x800,
eFitContent = 0x1000,
};
static bool IsMinContent(const nsStyleCoord& aCoord)
@ -181,13 +183,21 @@ nsGridContainerFrame::TrackSize::Initialize(nscoord aPercentageBasis,
MOZ_ASSERT(mBase == 0 && mLimit == 0 && mState == 0,
"track size data is expected to be initialized to zero");
auto minSizeUnit = aMinCoord.GetUnit();
auto maxSizeUnit = aMaxCoord.GetUnit();
if (minSizeUnit == eStyleUnit_None) {
// This track is sized using fit-content(size) (represented in style system
// with minCoord=None,maxCoord=size). In layout, fit-content(size) behaves
// as minmax(auto, max-content), with 'size' as an additional upper-bound.
mState = eFitContent;
minSizeUnit = eStyleUnit_Auto;
maxSizeUnit = eStyleUnit_Enumerated; // triggers max-content sizing below
}
if (::IsPercentOfIndefiniteSize(aMinCoord, aPercentageBasis)) {
// https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-percentage
// "If the inline or block size of the grid container is indefinite,
// <percentage> values relative to that size are treated as 'auto'."
minSizeUnit = eStyleUnit_Auto;
}
auto maxSizeUnit = aMaxCoord.GetUnit();
if (::IsPercentOfIndefiniteSize(aMaxCoord, aPercentageBasis)) {
maxSizeUnit = eStyleUnit_Auto;
}
@ -195,11 +205,11 @@ nsGridContainerFrame::TrackSize::Initialize(nscoord aPercentageBasis,
switch (minSizeUnit) {
case eStyleUnit_FlexFraction:
case eStyleUnit_Auto:
mState = eAutoMinSizing;
mState |= eAutoMinSizing;
break;
case eStyleUnit_Enumerated:
mState = IsMinContent(aMinCoord) ? eMinContentMinSizing
: eMaxContentMinSizing;
mState |= IsMinContent(aMinCoord) ? eMinContentMinSizing
: eMaxContentMinSizing;
break;
default:
mBase = ::ResolveToDefiniteSize(aMinCoord, aPercentageBasis);
@ -1232,6 +1242,8 @@ struct nsGridContainerFrame::Tracks
}
}
using FitContentClamper =
function<bool(uint32_t aTrack, nscoord aMinSize, nscoord* aSize)>;
/**
* Grow the planned size for tracks in aGrowableTracks up to their limit
* and then freeze them (all aGrowableTracks must be unfrozen on entry).
@ -1239,7 +1251,8 @@ struct nsGridContainerFrame::Tracks
*/
nscoord GrowTracksToLimit(nscoord aAvailableSpace,
nsTArray<TrackSize>& aPlan,
const nsTArray<uint32_t>& aGrowableTracks) const
const nsTArray<uint32_t>& aGrowableTracks,
FitContentClamper aFitContentClamper) const
{
MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0);
nscoord space = aAvailableSpace;
@ -1252,11 +1265,17 @@ struct nsGridContainerFrame::Tracks
continue;
}
nscoord newBase = sz.mBase + spacePerTrack;
if (newBase > sz.mLimit) {
nscoord consumed = sz.mLimit - sz.mBase;
nscoord limit = sz.mLimit;
if (MOZ_UNLIKELY((sz.mState & TrackSize::eFitContent) &&
aFitContentClamper)) {
// Clamp the limit to the fit-content() size, for §12.5.2 step 5/6.
aFitContentClamper(track, sz.mBase, &limit);
}
if (newBase > limit) {
nscoord consumed = limit - sz.mBase;
if (consumed > 0) {
space -= consumed;
sz.mBase = sz.mLimit;
sz.mBase = limit;
}
sz.mState |= TrackSize::eFrozen;
if (--numGrowable == 0) {
@ -1327,7 +1346,8 @@ struct nsGridContainerFrame::Tracks
void GrowSelectedTracksUnlimited(nscoord aAvailableSpace,
nsTArray<TrackSize>& aPlan,
const nsTArray<uint32_t>& aGrowableTracks,
TrackSize::StateBits aSelector) const
TrackSize::StateBits aSelector,
FitContentClamper aFitContentClamper) const
{
MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0);
uint32_t numGrowable = aGrowableTracks.Length();
@ -1352,22 +1372,37 @@ struct nsGridContainerFrame::Tracks
}
}
nscoord space = aAvailableSpace;
while (true) {
DebugOnly<bool> didClamp = false;
while (numGrowable) {
nscoord spacePerTrack = std::max<nscoord>(space / numGrowable, 1);
for (uint32_t track : aGrowableTracks) {
TrackSize& sz = aPlan[track];
if (sz.mState & TrackSize::eSkipGrowUnlimited) {
continue; // an excluded track
}
sz.mBase += spacePerTrack;
space -= spacePerTrack;
nscoord delta = spacePerTrack;
nscoord newBase = sz.mBase + delta;
if (MOZ_UNLIKELY((sz.mState & TrackSize::eFitContent) &&
aFitContentClamper)) {
// Clamp newBase to the fit-content() size, for §12.5.2 step 5/6.
if (aFitContentClamper(track, sz.mBase, &newBase)) {
didClamp = true;
delta = newBase - sz.mBase;
MOZ_ASSERT(delta >= 0, "track size shouldn't shrink");
sz.mState |= TrackSize::eSkipGrowUnlimited1;
--numGrowable;
}
}
sz.mBase = newBase;
space -= delta;
MOZ_ASSERT(space >= 0);
if (space == 0) {
return;
}
}
}
MOZ_ASSERT_UNREACHABLE("we don't exit the loop above except by return");
MOZ_ASSERT(didClamp, "we don't exit the loop above except by return, "
"unless we clamped some track's size");
}
/**
@ -1380,9 +1415,9 @@ struct nsGridContainerFrame::Tracks
TrackSize::StateBits aSelector)
{
SetupGrowthPlan(aPlan, aGrowableTracks);
nscoord space = GrowTracksToLimit(aAvailableSpace, aPlan, aGrowableTracks);
nscoord space = GrowTracksToLimit(aAvailableSpace, aPlan, aGrowableTracks, nullptr);
if (space > 0) {
GrowSelectedTracksUnlimited(space, aPlan, aGrowableTracks, aSelector);
GrowSelectedTracksUnlimited(space, aPlan, aGrowableTracks, aSelector, nullptr);
}
CopyPlanToBase(aPlan, aGrowableTracks);
}
@ -1392,12 +1427,26 @@ struct nsGridContainerFrame::Tracks
*/
void DistributeToTrackLimits(nscoord aAvailableSpace,
nsTArray<TrackSize>& aPlan,
nsTArray<uint32_t>& aGrowableTracks)
nsTArray<uint32_t>& aGrowableTracks,
const TrackSizingFunctions& aFunctions,
nscoord aPercentageBasis)
{
nscoord space = GrowTracksToLimit(aAvailableSpace, aPlan, aGrowableTracks);
auto fitContentClamper = [&aFunctions, aPercentageBasis] (uint32_t aTrack,
nscoord aMinSize,
nscoord* aSize) {
nscoord fitContentLimit =
::ResolveToDefiniteSize(aFunctions.MaxSizingFor(aTrack), aPercentageBasis);
if (*aSize > fitContentLimit) {
*aSize = std::max(aMinSize, fitContentLimit);
return true;
}
return false;
};
nscoord space = GrowTracksToLimit(aAvailableSpace, aPlan, aGrowableTracks,
fitContentClamper);
if (space > 0) {
GrowSelectedTracksUnlimited(aAvailableSpace, aPlan, aGrowableTracks,
TrackSize::StateBits(0));
TrackSize::StateBits(0), fitContentClamper);
}
CopyPlanToLimit(aPlan, aGrowableTracks);
}
@ -3685,6 +3734,13 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSizeStep1(
} else {
sz.mLimit = std::max(sz.mLimit, maxContentContribution.value());
}
if (MOZ_UNLIKELY(sz.mState & TrackSize::eFitContent)) {
// Clamp mLimit to the fit-content() size, for §12.5.1.
auto maxCoord = aFunctions.MaxSizingFor(aRange.mStart);
nscoord fitContentClamp =
nsRuleNode::ComputeCoordPercentCalc(maxCoord, aPercentageBasis);
sz.mLimit = std::min(sz.mLimit, fitContentClamp);
}
}
if (sz.mLimit < sz.mBase) {
sz.mLimit = sz.mBase;
@ -4164,7 +4220,8 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
TrackSize::eIntrinsicMaxSizing,
tracks);
if (space > 0) {
DistributeToTrackLimits(space, plan, tracks);
DistributeToTrackLimits(space, plan, tracks, aFunctions,
aPercentageBasis);
}
}
for (size_t j = 0, len = mSizes.Length(); j < len; ++j) {
@ -4191,7 +4248,8 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
TrackSize::eAutoOrMaxContentMaxSizing,
tracks);
if (space > 0) {
DistributeToTrackLimits(space, plan, tracks);
DistributeToTrackLimits(space, plan, tracks, aFunctions,
aPercentageBasis);
}
}
}

Просмотреть файл

@ -277,6 +277,7 @@ CSS_KEY(farthest-corner, farthest_corner)
CSS_KEY(fill, fill)
CSS_KEY(filled, filled)
CSS_KEY(fill-box, fill_box)
CSS_KEY(fit-content, fit_content)
CSS_KEY(fixed, fixed)
CSS_KEY(flat, flat)
CSS_KEY(flex, flex)

Просмотреть файл

@ -8750,12 +8750,25 @@ CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue,
return result;
}
// Attempt to parse a minmax() function.
// Attempt to parse a minmax() or fit-content() function.
if (!GetToken(true)) {
return CSSParseResult::NotFound;
}
if (!(eCSSToken_Function == mToken.mType &&
mToken.mIdent.LowerCaseEqualsLiteral("minmax"))) {
if (eCSSToken_Function != mToken.mType) {
UngetToken();
return CSSParseResult::NotFound;
}
if (mToken.mIdent.LowerCaseEqualsLiteral("fit-content")) {
nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_fit_content, 1);
if (ParseGridTrackBreadth(func->Item(1)) == CSSParseResult::Ok &&
func->Item(1).IsLengthPercentCalcUnit() &&
ExpectSymbol(')', true)) {
return CSSParseResult::Ok;
}
SkipUntil(')');
return CSSParseResult::Error;
}
if (!mToken.mIdent.LowerCaseEqualsLiteral("minmax")) {
UngetToken();
return CSSParseResult::NotFound;
}
@ -8836,11 +8849,16 @@ CSSParserImpl::ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
list != firstRepeat; list = list->mNext) {
if (list->mValue.GetUnit() == eCSSUnit_Function) {
nsCSSValue::Array* func = list->mValue.GetArrayValue();
NS_ASSERTION(func->Item(0).GetKeywordValue() == eCSSKeyword_minmax,
"Expected minmax(), got another function name");
if (!func->Item(1).IsLengthPercentCalcUnit() &&
!func->Item(2).IsLengthPercentCalcUnit()) {
return false;
auto funcName = func->Item(0).GetKeywordValue();
if (funcName == eCSSKeyword_minmax) {
if (!func->Item(1).IsLengthPercentCalcUnit() &&
!func->Item(2).IsLengthPercentCalcUnit()) {
return false;
}
} else {
MOZ_ASSERT(funcName == eCSSKeyword_fit_content,
"Expected minmax() or fit-content() function");
return false; // fit-content() is not a <fixed-size>
}
} else if (!list->mValue.IsLengthPercentCalcUnit()) {
return false;

Просмотреть файл

@ -2531,6 +2531,21 @@ already_AddRefed<CSSValue>
nsComputedDOMStyle::GetGridTrackSize(const nsStyleCoord& aMinValue,
const nsStyleCoord& aMaxValue)
{
if (aMinValue.GetUnit() == eStyleUnit_None) {
// A fit-content() function.
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
nsAutoString argumentStr, fitContentStr;
fitContentStr.AppendLiteral("fit-content(");
MOZ_ASSERT(aMaxValue.IsCoordPercentCalcUnit(),
"unexpected unit for fit-content() argument value");
SetValueToCoord(val, aMaxValue, true);
val->GetCssText(argumentStr);
fitContentStr.Append(argumentStr);
fitContentStr.Append(char16_t(')'));
val->SetString(fitContentStr);
return val.forget();
}
if (aMinValue == aMaxValue) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, aMinValue, true,

Просмотреть файл

@ -8108,6 +8108,9 @@ SetGridTrackBreadth(const nsCSSValue& aValue,
aResult.SetFlexFractionValue(aValue.GetFloatValue());
} else if (unit == eCSSUnit_Auto) {
aResult.SetAutoValue();
} else if (unit == eCSSUnit_None) {
// For fit-content().
aResult.SetNoneValue();
} else {
MOZ_ASSERT(unit != eCSSUnit_Inherit && unit != eCSSUnit_Unset,
"Unexpected value that would use dummyParentCoord");
@ -8129,14 +8132,22 @@ SetGridTrackSize(const nsCSSValue& aValue,
RuleNodeCacheConditions& aConditions)
{
if (aValue.GetUnit() == eCSSUnit_Function) {
// A minmax() function.
nsCSSValue::Array* func = aValue.GetArrayValue();
NS_ASSERTION(func->Item(0).GetKeywordValue() == eCSSKeyword_minmax,
"Expected minmax(), got another function name");
SetGridTrackBreadth(func->Item(1), aResultMin,
aStyleContext, aPresContext, aConditions);
SetGridTrackBreadth(func->Item(2), aResultMax,
aStyleContext, aPresContext, aConditions);
auto funcName = func->Item(0).GetKeywordValue();
if (funcName == eCSSKeyword_minmax) {
SetGridTrackBreadth(func->Item(1), aResultMin,
aStyleContext, aPresContext, aConditions);
SetGridTrackBreadth(func->Item(2), aResultMax,
aStyleContext, aPresContext, aConditions);
} else if (funcName == eCSSKeyword_fit_content) {
// We represent fit-content(L) as 'none' min-sizing and L max-sizing.
SetGridTrackBreadth(nsCSSValue(eCSSUnit_None), aResultMin,
aStyleContext, aPresContext, aConditions);
SetGridTrackBreadth(func->Item(1), aResultMax,
aStyleContext, aPresContext, aConditions);
} else {
NS_ERROR("Expected minmax() or fit-content(), got another function name");
}
} else {
// A single <track-breadth>,
// specifies identical min and max sizing functions.

Просмотреть файл

@ -1650,6 +1650,8 @@ struct nsStyleGridLine
//
// A <track-size> specified as a single <track-breadth> is represented
// as identical min and max sizing functions.
// A 'fit-content(size)' <track-size> is represented as eStyleUnit_None
// in the min sizing function and 'size' in the max sizing function.
//
// The units for nsStyleCoord are:
// * eStyleUnit_Percent represents a <percentage>