Bug 957915 - Handle calc(%) on tables. r=mats

This patch makes us handle calc with percentages when we can convert to
percentages the same way we handle plain percentages in table layout.

We still treat length + percentage as auto (this matches Blink / WebKit as
well). There's one case we differ with Blink / WebKit, which is calc(% + 0px),
which they'd treat as auto instead of a percentage.

I think this is a bug on them (or at least worth some spec clarification). I
filed https://github.com/w3c/csswg-drafts/issues/3482 for that.

In practice what that'd means for us if the WG decides that Blink / WebKit is
right in that case is that we'd need to keep track of whether the calc()
specifies lengths, and return false from ConvertsToPercent if so.

In any case, nothing that would massively change this patch, and I think enough
of an edge case that is not worth blocking on the CSSWG decision here. Though I
could be convinced otherwise of course.

Differential Revision: https://phabricator.services.mozilla.com/D15719
This commit is contained in:
Emilio Cobos Álvarez
2019-01-15 19:39:23 +00:00
parent ab7e297952
commit 7f0ace82b0
10 changed files with 87 additions and 60 deletions

View File

@@ -14,6 +14,11 @@
<td>x</td>
<td style="width: 100px">y</td>
</table>
<table border>
<tr>
<td style="width: 50%">x</td>
<td style="width: 100px">y</td>
</table>
<table border>
<tr>
<td>x</td>

View File

@@ -11,6 +11,11 @@
<td style="width: calc(500px)">x</td>
<td style="width: 100px">y</td>
</table>
<table border>
<tr>
<td style="width: calc(50% + 1px)">x</td>
<td style="width: 100px">y</td>
</table>
<table border>
<tr>
<td style="width: calc(50%)">x</td>

View File

@@ -19,6 +19,11 @@ table { table-layout: fixed; width: 500px; border-spacing: 0 }
<td>x</td>
<td style="width: 100px">y</td>
</table>
<table border>
<tr>
<td style="width: 50%">x</td>
<td style="width: 100px">y</td>
</table>
<table border>
<tr>
<td>x</td>

View File

@@ -16,6 +16,11 @@ table { table-layout: fixed; width: 500px; border-spacing: 0 }
<td style="width: calc(500px)">x</td>
<td style="width: 100px">y</td>
</table>
<table border>
<tr>
<td style="width: calc(50% + 1px)">x</td>
<td style="width: 100px">y</td>
</table>
<table border>
<tr>
<td style="width: calc(50%)">x</td>

View File

@@ -172,6 +172,30 @@ class nsStyleCoord {
return mUnit == eStyleUnit_Percent || (IsCalcUnit() && CalcHasPercent());
}
static bool ConvertsToPercent(const nsStyleUnit aUnit,
const nsStyleUnion aValue) {
if (aUnit == eStyleUnit_Percent) {
return true;
}
if (!IsCalcUnit(aUnit)) {
return false;
}
auto* calc = AsCalcValue(aValue);
return calc->mLength == 0 && calc->mHasPercent;
}
bool ConvertsToPercent() const { return ConvertsToPercent(mUnit, mValue); }
float ToPercent() const {
MOZ_ASSERT(ConvertsToPercent());
if (IsCalcUnit()) {
MOZ_ASSERT(CalcHasPercent() && GetCalcValue()->mLength == 0);
return GetCalcValue()->mPercent;
}
return mValue.mFloat;
}
static bool ConvertsToLength(const nsStyleUnit aUnit,
const nsStyleUnion aValue) {
return aUnit == eStyleUnit_Coord ||

View File

@@ -123,9 +123,9 @@ static CellISizeInfo GetISizeInfo(gfxContext *aRenderingContext,
const nsStyleCoord &iSize = stylePos->ISize(aWM);
nsStyleUnit unit = iSize.GetUnit();
// NOTE: We're ignoring calc() units with percentages here, for lack of a
// sensible idea for what to do with them. This means calc() with
// percentages is basically handled like 'auto' for table cells and
// NOTE: We're ignoring calc() units with both lengths and percentages here,
// for lack of a sensible idea for what to do with them. This means calc()
// with percentages is basically handled like 'auto' for table cells and
// columns.
if (iSize.ConvertsToLength()) {
hasSpecifiedISize = true;
@@ -145,8 +145,8 @@ static CellISizeInfo GetISizeInfo(gfxContext *aRenderingContext,
minCoord = c;
}
prefCoord = std::max(c, minCoord);
} else if (unit == eStyleUnit_Percent) {
prefPercent = iSize.GetPercentValue();
} else if (iSize.ConvertsToPercent()) {
prefPercent = iSize.ToPercent();
} else if (unit == eStyleUnit_Enumerated && aIsCell) {
switch (iSize.GetIntValue()) {
case NS_STYLE_WIDTH_MAX_CONTENT:
@@ -181,13 +181,12 @@ static CellISizeInfo GetISizeInfo(gfxContext *aRenderingContext,
nscoord c = aFrame->ComputeISizeValue(aRenderingContext, 0, 0, 0, maxISize);
minCoord = std::min(c, minCoord);
prefCoord = std::min(c, prefCoord);
} else if (unit == eStyleUnit_Percent) {
float p = stylePos->MaxISize(aWM).GetPercentValue();
} else if (maxISize.ConvertsToPercent()) {
float p = maxISize.ToPercent();
if (p < prefPercent) {
prefPercent = p;
}
}
// treat calc() with percentages on max-inline-size just like 'none'.
nsStyleCoord minISize(stylePos->MinISize(aWM));
if (minISize.GetUnit() == eStyleUnit_Enumerated) {
@@ -203,13 +202,12 @@ static CellISizeInfo GetISizeInfo(gfxContext *aRenderingContext,
nscoord c = aFrame->ComputeISizeValue(aRenderingContext, 0, 0, 0, minISize);
minCoord = std::max(c, minCoord);
prefCoord = std::max(c, prefCoord);
} else if (unit == eStyleUnit_Percent) {
float p = stylePos->MinISize(aWM).GetPercentValue();
} else if (minISize.ConvertsToPercent()) {
float p = minISize.ToPercent();
if (p > prefPercent) {
prefPercent = p;
}
}
// treat calc() with percentages on min-inline-size just like '0'.
// XXX Should col frame have border/padding considered?
if (aIsCell) {

View File

@@ -71,13 +71,13 @@ FixedTableLayoutStrategy::~FixedTableLayoutStrategy() {}
if (styleISize->ConvertsToLength()) {
result +=
colFrame->ComputeISizeValue(aRenderingContext, 0, 0, 0, *styleISize);
} else if (styleISize->GetUnit() == eStyleUnit_Percent) {
} else if (styleISize->ConvertsToPercent()) {
// do nothing
} else {
NS_ASSERTION(
styleISize->GetUnit() == eStyleUnit_Auto ||
styleISize->GetUnit() == eStyleUnit_Enumerated ||
(styleISize->IsCalcUnit() && styleISize->CalcHasPercent()),
(styleISize->IsCalcUnit() && !styleISize->ConvertsToPercent()),
"bad inline size");
// The 'table-layout: fixed' algorithm considers only cells in the
@@ -102,14 +102,14 @@ FixedTableLayoutStrategy::~FixedTableLayoutStrategy() {}
cellISize = ((cellISize + spacing) / colSpan) - spacing;
}
result += cellISize;
} else if (styleISize->GetUnit() == eStyleUnit_Percent) {
} else if (styleISize->ConvertsToPercent()) {
if (colSpan > 1) {
// XXX Can this force columns to negative inline sizes?
result -= spacing * (colSpan - 1);
}
}
// else, for 'auto', '-moz-available', '-moz-fit-content',
// and 'calc()' with percentages, do nothing
// and 'calc()' with both lengths and percentages, do nothing
}
}
}
@@ -209,8 +209,8 @@ static inline nscoord AllocateUnassigned(nscoord aUnassignedSpace,
colISize = colFrame->ComputeISizeValue(aReflowInput.mRenderingContext, 0,
0, 0, *styleISize);
specTotal += colISize;
} else if (styleISize->GetUnit() == eStyleUnit_Percent) {
float pct = styleISize->GetPercentValue();
} else if (styleISize->ConvertsToPercent()) {
float pct = styleISize->ToPercent();
colISize = NSToCoordFloor(pct * float(tableISize));
colFrame->AddPrefPercent(pct);
pctTotal += pct;
@@ -218,7 +218,7 @@ static inline nscoord AllocateUnassigned(nscoord aUnassignedSpace,
NS_ASSERTION(
styleISize->GetUnit() == eStyleUnit_Auto ||
styleISize->GetUnit() == eStyleUnit_Enumerated ||
(styleISize->IsCalcUnit() && styleISize->CalcHasPercent()),
(styleISize->IsCalcUnit() && !styleISize->ConvertsToPercent()),
"bad inline size");
// The 'table-layout: fixed' algorithm considers only cells in the
@@ -242,9 +242,9 @@ static inline nscoord AllocateUnassigned(nscoord aUnassignedSpace,
colISize = nsLayoutUtils::IntrinsicForContainer(
aReflowInput.mRenderingContext, cellFrame,
nsLayoutUtils::MIN_ISIZE);
} else if (styleISize->GetUnit() == eStyleUnit_Percent) {
} else if (styleISize->ConvertsToPercent()) {
// XXX This should use real percentage padding
float pct = styleISize->GetPercentValue();
float pct = styleISize->ToPercent();
colISize = NSToCoordFloor(pct * float(tableISize));
if (cellStylePos->mBoxSizing == StyleBoxSizing::Content) {
@@ -273,7 +273,7 @@ static inline nscoord AllocateUnassigned(nscoord aUnassignedSpace,
colISize = 0;
}
}
if (styleISize->GetUnit() != eStyleUnit_Percent) {
if (!styleISize->ConvertsToPercent()) {
specTotal += colISize;
}
}

View File

@@ -503,10 +503,9 @@ nscoord nsTableRowFrame::CalcBSize(const ReflowInput& aReflowInput) {
const nsStyleCoord& bsizeStyleCoord = position->BSize(wm);
if (bsizeStyleCoord.ConvertsToLength()) {
SetFixedBSize(bsizeStyleCoord.ComputeCoordPercentCalc(0));
} else if (eStyleUnit_Percent == bsizeStyleCoord.GetUnit()) {
SetPctBSize(bsizeStyleCoord.GetPercentValue());
} else if (bsizeStyleCoord.ConvertsToPercent()) {
SetPctBSize(bsizeStyleCoord.ToPercent());
}
// calc() with percentages is treated like 'auto' on table rows.
for (nsIFrame* kidFrame : mFrames) {
nsTableCellFrame* cellFrame = do_QueryFrame(kidFrame);
@@ -569,44 +568,26 @@ nsresult nsTableRowFrame::CalculateCellActualBSize(nsTableCellFrame* aCellFrame,
int32_t rowSpan = GetTableFrame()->GetEffectiveRowSpan(*aCellFrame);
const nsStyleCoord& bsizeStyleCoord = position->BSize(aWM);
switch (bsizeStyleCoord.GetUnit()) {
case eStyleUnit_Calc: {
if (bsizeStyleCoord.CalcHasPercent()) {
// Treat this like "auto"
break;
}
// Fall through to the coord case
MOZ_FALLTHROUGH;
if (bsizeStyleCoord.ConvertsToLength()) {
// In quirks mode, table cell isize should be content-box, but bsize
// should be border-box.
// Because of this historic anomaly, we do not use quirk.css
// (since we can't specify one value of box-sizing for isize and another
// for bsize)
specifiedBSize = bsizeStyleCoord.ToLength();
if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks &&
position->mBoxSizing == StyleBoxSizing::Content) {
specifiedBSize +=
aCellFrame->GetLogicalUsedBorderAndPadding(aWM).BStartEnd(aWM);
}
case eStyleUnit_Coord: {
// In quirks mode, table cell isize should be content-box, but bsize
// should be border-box.
// Because of this historic anomaly, we do not use quirk.css
// (since we can't specify one value of box-sizing for isize and another
// for bsize)
specifiedBSize = bsizeStyleCoord.ComputeCoordPercentCalc(0);
if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks &&
position->mBoxSizing == StyleBoxSizing::Content) {
specifiedBSize +=
aCellFrame->GetLogicalUsedBorderAndPadding(aWM).BStartEnd(aWM);
}
if (1 == rowSpan) {
SetFixedBSize(specifiedBSize);
}
break;
if (1 == rowSpan) {
SetFixedBSize(specifiedBSize);
}
case eStyleUnit_Percent: {
if (1 == rowSpan) {
SetPctBSize(bsizeStyleCoord.GetPercentValue());
}
// pct bsizes are handled when all of the cells are finished,
// so don't set specifiedBSize
break;
} else if (bsizeStyleCoord.ConvertsToPercent()) {
if (1 == rowSpan) {
SetPctBSize(bsizeStyleCoord.ToPercent());
}
case eStyleUnit_Auto:
default:
break;
}
// If the specified bsize is greater than the desired bsize,
@@ -1357,8 +1338,8 @@ void nsTableRowFrame::InitHasCellWithStyleBSize(nsTableFrame* aTableFrame) {
const nsStyleCoord& cellBSize = cellFrame->StylePosition()->BSize(wm);
if (aTableFrame->GetEffectiveRowSpan(*cellFrame) == 1 &&
cellBSize.GetUnit() != eStyleUnit_Auto &&
/* calc() with percentages treated like 'auto' */
(!cellBSize.IsCalcUnit() || !cellBSize.HasPercent())) {
/* calc() with both percentages and lengths treated like 'auto' */
(cellBSize.ConvertsToLength() || cellBSize.ConvertsToPercent())) {
AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE);
return;
}

View File

@@ -0,0 +1,2 @@
[calc-width-table-auto-1.html]
expected: FAIL

View File

@@ -0,0 +1,2 @@
[calc-width-table-fixed-1.html]
expected: FAIL