Fix app-unit rounding for border width edges

This commit is contained in:
Basilisk-Dev 2026-05-09 18:30:47 -04:00 committed by wuggy
commit cf8b773656
2 changed files with 127 additions and 21 deletions

View file

@ -319,6 +319,11 @@ nsRuleNode::EnsureInlineDisplay(StyleDisplay& display)
} }
} }
enum class AppUnitRounding {
Default,
TowardZero
};
static nscoord CalcLengthWith(const nsCSSValue& aValue, static nscoord CalcLengthWith(const nsCSSValue& aValue,
nscoord aFontSize, nscoord aFontSize,
const nsStyleFont* aStyleFont, const nsStyleFont* aStyleFont,
@ -326,7 +331,9 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool aUseProvidedRootEmSize, bool aUseProvidedRootEmSize,
bool aUseUserFontSet, bool aUseUserFontSet,
RuleNodeCacheConditions& aConditions); RuleNodeCacheConditions& aConditions,
AppUnitRounding aRounding =
AppUnitRounding::Default);
struct CalcLengthCalcOps : public css::BasicCoordCalcOps, struct CalcLengthCalcOps : public css::BasicCoordCalcOps,
public css::NumbersAlreadyNormalizedOps public css::NumbersAlreadyNormalizedOps
@ -339,18 +346,21 @@ struct CalcLengthCalcOps : public css::BasicCoordCalcOps,
const bool mUseProvidedRootEmSize; const bool mUseProvidedRootEmSize;
const bool mUseUserFontSet; const bool mUseUserFontSet;
RuleNodeCacheConditions& mConditions; RuleNodeCacheConditions& mConditions;
const AppUnitRounding mRounding;
CalcLengthCalcOps(nscoord aFontSize, const nsStyleFont* aStyleFont, CalcLengthCalcOps(nscoord aFontSize, const nsStyleFont* aStyleFont,
nsStyleContext* aStyleContext, nsPresContext* aPresContext, nsStyleContext* aStyleContext, nsPresContext* aPresContext,
bool aUseProvidedRootEmSize, bool aUseUserFontSet, bool aUseProvidedRootEmSize, bool aUseUserFontSet,
RuleNodeCacheConditions& aConditions) RuleNodeCacheConditions& aConditions,
AppUnitRounding aRounding)
: mFontSize(aFontSize), : mFontSize(aFontSize),
mStyleFont(aStyleFont), mStyleFont(aStyleFont),
mStyleContext(aStyleContext), mStyleContext(aStyleContext),
mPresContext(aPresContext), mPresContext(aPresContext),
mUseProvidedRootEmSize(aUseProvidedRootEmSize), mUseProvidedRootEmSize(aUseProvidedRootEmSize),
mUseUserFontSet(aUseUserFontSet), mUseUserFontSet(aUseUserFontSet),
mConditions(aConditions) mConditions(aConditions),
mRounding(aRounding)
{ {
} }
@ -358,15 +368,84 @@ struct CalcLengthCalcOps : public css::BasicCoordCalcOps,
{ {
return CalcLengthWith(aValue, mFontSize, mStyleFont, return CalcLengthWith(aValue, mFontSize, mStyleFont,
mStyleContext, mPresContext, mUseProvidedRootEmSize, mStyleContext, mPresContext, mUseProvidedRootEmSize,
mUseUserFontSet, mConditions); mUseUserFontSet, mConditions, mRounding);
} }
}; };
static inline nscoord ScaleCoordRound(const nsCSSValue& aValue, float aFactor) static inline nscoord
ScaleCoordWithRounding(const nsCSSValue& aValue, float aFactor,
AppUnitRounding aRounding)
{ {
if (aRounding == AppUnitRounding::TowardZero) {
return NSToCoordTruncClamped(aValue.GetFloatValue() * aFactor);
}
return NSToCoordRoundWithClamp(aValue.GetFloatValue() * aFactor); return NSToCoordRoundWithClamp(aValue.GetFloatValue() * aFactor);
} }
static inline nscoord
CSSPixelsToAppUnitsWithRounding(float aPixels, AppUnitRounding aRounding)
{
if (aRounding == AppUnitRounding::TowardZero) {
return NSToCoordTruncClamped(double(aPixels) *
nsPresContext::AppUnitsPerCSSPixel());
}
return nsPresContext::CSSPixelsToAppUnits(aPixels);
}
static nscoord
GetFixedLengthWithRounding(const nsCSSValue& aValue,
nsPresContext* aPresContext,
AppUnitRounding aRounding)
{
if (aRounding == AppUnitRounding::Default) {
return aValue.GetFixedLength(aPresContext);
}
MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_PhysicalMillimeter,
"not a fixed length unit");
double inches = double(aValue.GetFloatValue()) / MM_PER_INCH_FLOAT;
return NSToCoordTruncClamped(
inches * aPresContext->DeviceContext()->AppUnitsPerPhysicalInch());
}
static nscoord
GetPixelLengthWithRounding(const nsCSSValue& aValue,
AppUnitRounding aRounding)
{
MOZ_ASSERT(aValue.IsPixelLengthUnit(), "not a pixel length unit");
double scaleFactor;
switch (aValue.GetUnit()) {
case eCSSUnit_Pixel:
return CSSPixelsToAppUnitsWithRounding(aValue.GetFloatValue(),
aRounding);
case eCSSUnit_Pica:
scaleFactor = 16.0;
break;
case eCSSUnit_Point:
scaleFactor = 4.0 / 3.0;
break;
case eCSSUnit_Inch:
scaleFactor = 96.0;
break;
case eCSSUnit_Millimeter:
scaleFactor = 96.0 / 25.4;
break;
case eCSSUnit_Centimeter:
scaleFactor = 96.0 / 2.54;
break;
case eCSSUnit_Quarter:
scaleFactor = 96.0 / 101.6;
break;
default:
NS_ERROR("should never get here");
return 0;
}
return CSSPixelsToAppUnitsWithRounding(
float(double(aValue.GetFloatValue()) * scaleFactor), aRounding);
}
static inline nscoord ScaleViewportCoordTrunc(const nsCSSValue& aValue, static inline nscoord ScaleViewportCoordTrunc(const nsCSSValue& aValue,
nscoord aViewportSize) nscoord aViewportSize)
{ {
@ -465,7 +544,8 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
// except when called from // except when called from
// CalcLengthWithInitialFont. // CalcLengthWithInitialFont.
bool aUseUserFontSet, bool aUseUserFontSet,
RuleNodeCacheConditions& aConditions) RuleNodeCacheConditions& aConditions,
AppUnitRounding aRounding)
{ {
NS_ASSERTION(aValue.IsLengthUnit() || aValue.IsCalcUnit(), NS_ASSERTION(aValue.IsLengthUnit() || aValue.IsCalcUnit(),
"not a length or calc unit"); "not a length or calc unit");
@ -476,10 +556,10 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
NS_ASSERTION(aPresContext, "Must have prescontext"); NS_ASSERTION(aPresContext, "Must have prescontext");
if (aValue.IsFixedLengthUnit()) { if (aValue.IsFixedLengthUnit()) {
return aValue.GetFixedLength(aPresContext); return GetFixedLengthWithRounding(aValue, aPresContext, aRounding);
} }
if (aValue.IsPixelLengthUnit()) { if (aValue.IsPixelLengthUnit()) {
return aValue.GetPixelLength(); return GetPixelLengthWithRounding(aValue, aRounding);
} }
if (aValue.IsCalcUnit()) { if (aValue.IsCalcUnit()) {
// For properties for which lengths are the *only* units accepted in // For properties for which lengths are the *only* units accepted in
@ -490,7 +570,7 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
CalcLengthCalcOps ops(aFontSize, aStyleFont, CalcLengthCalcOps ops(aFontSize, aStyleFont,
aStyleContext, aPresContext, aStyleContext, aPresContext,
aUseProvidedRootEmSize, aUseUserFontSet, aUseProvidedRootEmSize, aUseUserFontSet,
aConditions); aConditions, aRounding);
return css::ComputeCalc(aValue, ops); return css::ComputeCalc(aValue, ops);
} }
switch (aValue.GetUnit()) { switch (aValue.GetUnit()) {
@ -623,7 +703,7 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
rootFontSize = rootStyleFont->mFont.size; rootFontSize = rootStyleFont->mFont.size;
} }
return ScaleCoordRound(aValue, float(rootFontSize)); return ScaleCoordWithRounding(aValue, float(rootFontSize), aRounding);
} }
default: default:
// Fall through to the code for units that can't be stored in the // Fall through to the code for units that can't be stored in the
@ -648,7 +728,7 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
// CSS2.1 specifies that this unit scales to the computed font // CSS2.1 specifies that this unit scales to the computed font
// size, not the em-width in the font metrics, despite the name. // size, not the em-width in the font metrics, despite the name.
aConditions.SetFontSizeDependency(aFontSize); aConditions.SetFontSizeDependency(aFontSize);
return ScaleCoordRound(aValue, float(aFontSize)); return ScaleCoordWithRounding(aValue, float(aFontSize), aRounding);
} }
case eCSSUnit_XHeight: { case eCSSUnit_XHeight: {
aPresContext->SetUsesExChUnits(true); aPresContext->SetUsesExChUnits(true);
@ -656,7 +736,7 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
GetMetricsFor(aPresContext, aStyleContext, styleFont, GetMetricsFor(aPresContext, aStyleContext, styleFont,
aFontSize, aUseUserFontSet); aFontSize, aUseUserFontSet);
aConditions.SetUncacheable(); aConditions.SetUncacheable();
return ScaleCoordRound(aValue, float(fm->XHeight())); return ScaleCoordWithRounding(aValue, float(fm->XHeight()), aRounding);
} }
case eCSSUnit_Char: { case eCSSUnit_Char: {
aPresContext->SetUsesExChUnits(true); aPresContext->SetUsesExChUnits(true);
@ -668,8 +748,9 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
GetMetrics(fm->Orientation()).zeroOrAveCharWidth; GetMetrics(fm->Orientation()).zeroOrAveCharWidth;
aConditions.SetUncacheable(); aConditions.SetUncacheable();
return ScaleCoordRound(aValue, ceil(aPresContext->AppUnitsPerDevPixel() * return ScaleCoordWithRounding(
zeroWidth)); aValue, ceil(aPresContext->AppUnitsPerDevPixel() * zeroWidth),
aRounding);
} }
default: default:
NS_NOTREACHED("unexpected unit"); NS_NOTREACHED("unexpected unit");
@ -701,6 +782,20 @@ static inline nscoord CalcLength(const nsCSSValue& aValue,
aPresContext, aConditions); aPresContext, aConditions);
} }
static inline nscoord
CalcLengthTowardZero(const nsCSSValue& aValue,
nsStyleContext* aStyleContext,
nsPresContext* aPresContext,
RuleNodeCacheConditions& aConditions)
{
NS_ASSERTION(aStyleContext, "Must have style data");
return CalcLengthWith(aValue, -1, nullptr,
aStyleContext, aPresContext,
false, true, aConditions,
AppUnitRounding::TowardZero);
}
/* static */ nscoord /* static */ nscoord
nsRuleNode::CalcLengthWithInitialFont(nsPresContext* aPresContext, nsRuleNode::CalcLengthWithInitialFont(nsPresContext* aPresContext,
const nsCSSValue& aValue) const nsCSSValue& aValue)
@ -8048,13 +8143,11 @@ nsRuleNode::ComputeBorderData(void* aStartStruct,
"Unexpected enum value"); "Unexpected enum value");
border->SetBorderWidth(side, border->SetBorderWidth(side,
(mPresContext->GetBorderWidthTable())[value.GetIntValue()]); (mPresContext->GetBorderWidthTable())[value.GetIntValue()]);
// OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT } else if (value.IsLengthUnit() || value.IsCalcUnit()) {
} else if (SetCoord(value, coord, nsStyleCoord(),
SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY,
aContext, mPresContext, conditions)) {
NS_ASSERTION(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
// clamp negative calc() to 0. // clamp negative calc() to 0.
border->SetBorderWidth(side, std::max(coord.GetCoordValue(), 0)); border->SetBorderWidth(side,
std::max(CalcLengthTowardZero(value, aContext, mPresContext,
conditions), 0));
} else if (eCSSUnit_Inherit == value.GetUnit()) { } else if (eCSSUnit_Inherit == value.GetUnit()) {
conditions.SetUncacheable(); conditions.SetUncacheable();
border->SetBorderWidth(side, border->SetBorderWidth(side,
@ -8336,6 +8429,11 @@ nsRuleNode::ComputeOutlineData(void* aStartStruct,
eCSSUnit_Revert == outlineWidthValue->GetUnit()) { eCSSUnit_Revert == outlineWidthValue->GetUnit()) {
outline->mOutlineWidth = outline->mOutlineWidth =
nsStyleCoord(NS_STYLE_BORDER_WIDTH_MEDIUM, eStyleUnit_Enumerated); nsStyleCoord(NS_STYLE_BORDER_WIDTH_MEDIUM, eStyleUnit_Enumerated);
} else if (outlineWidthValue->IsLengthUnit() ||
outlineWidthValue->IsCalcUnit()) {
outline->mOutlineWidth.SetCoordValue(
CalcLengthTowardZero(*outlineWidthValue, aContext, mPresContext,
conditions));
} else { } else {
SetCoord(*outlineWidthValue, outline->mOutlineWidth, SetCoord(*outlineWidthValue, outline->mOutlineWidth,
parentOutline->mOutlineWidth, parentOutline->mOutlineWidth,
@ -8346,7 +8444,13 @@ nsRuleNode::ComputeOutlineData(void* aStartStruct,
// outline-offset: length, inherit // outline-offset: length, inherit
nsStyleCoord tempCoord; nsStyleCoord tempCoord;
const nsCSSValue* outlineOffsetValue = aRuleData->ValueForOutlineOffset(); const nsCSSValue* outlineOffsetValue = aRuleData->ValueForOutlineOffset();
if (SetCoord(*outlineOffsetValue, tempCoord, if (outlineOffsetValue->IsLengthUnit() || outlineOffsetValue->IsCalcUnit()) {
outline->mOutlineOffset =
NS_ROUND_OFFSET_TO_PIXELS(
CalcLengthTowardZero(*outlineOffsetValue, aContext, mPresContext,
conditions),
mPresContext->AppUnitsPerDevPixel());
} else if (SetCoord(*outlineOffsetValue, tempCoord,
nsStyleCoord(parentOutline->mOutlineOffset, nsStyleCoord(parentOutline->mOutlineOffset,
nsStyleCoord::CoordConstructor), nsStyleCoord::CoordConstructor),
SETCOORD_LH | SETCOORD_INITIAL_ZERO | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_LH | SETCOORD_INITIAL_ZERO | SETCOORD_CALC_LENGTH_ONLY |

View file

@ -24,6 +24,8 @@ var tests = [
["2px", "2px"], ["2px", "2px"],
["2.75px", "2px"], ["2.75px", "2px"],
["2.999px", "2px"], ["2.999px", "2px"],
["3px", "3px"],
["3.001px", "3px"],
]; ];
function checkWidth(property, setupProperty, setupValue) { function checkWidth(property, setupProperty, setupValue) {