зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1281320 - [css-grid] Implement 'fit-content([ <length> | <percentage> ])' value for <track-size>. r=dholbert
This commit is contained in:
Родитель
60eb164682
Коммит
15b4f3793a
|
@ -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>
|
||||
|
|
Загрузка…
Ссылка в новой задаче