Skip to content

Commit

Permalink
Bug 1836995 - Support q units for SVG lengths r=emilio
Browse files Browse the repository at this point in the history
  • Loading branch information
longsonr committed Jun 7, 2023
1 parent 056bb26 commit 017e8bb
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 56 deletions.
10 changes: 8 additions & 2 deletions dom/svg/DOMSVGLength.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,16 @@ uint16_t DOMSVGLength::UnitType() {
if (mIsAnimValItem) {
Element()->FlushAnimations();
}
uint16_t unitType;
if (nsCOMPtr<SVGElement> svg = do_QueryInterface(mOwner)) {
return svg->GetAnimatedLength(mAttrEnum)->GetSpecifiedUnitType();
unitType = svg->GetAnimatedLength(mAttrEnum)->GetSpecifiedUnitType();
} else {
unitType = HasOwner() ? InternalItem().GetUnit() : mUnit;
}
return HasOwner() ? InternalItem().GetUnit() : mUnit;

return SVGLength::IsValidUnitType(unitType)
? unitType
: SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN;
}

float DOMSVGLength::GetValue(ErrorResult& aRv) {
Expand Down
31 changes: 22 additions & 9 deletions dom/svg/SVGLength.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ using namespace mozilla::dom::SVGLength_Binding;

namespace mozilla {

const unsigned short SVG_LENGTHTYPE_Q = 11;

void SVGLength::GetValueAsString(nsAString& aValue) const {
nsTextFormatter::ssprintf(aValue, u"%g", (double)mValue);

Expand Down Expand Up @@ -57,7 +59,7 @@ bool SVGLength::SetValueFromString(const nsAString& aString) {
/*static*/
bool SVGLength::IsAbsoluteUnit(uint8_t aUnit) {
return aUnit == SVG_LENGTHTYPE_NUMBER ||
(aUnit >= SVG_LENGTHTYPE_PX && aUnit <= SVG_LENGTHTYPE_PC);
(aUnit >= SVG_LENGTHTYPE_PX && aUnit <= SVG_LENGTHTYPE_Q);
}

/**
Expand All @@ -75,24 +77,29 @@ float SVGLength::GetAbsUnitsPerAbsUnit(uint8_t aUnits, uint8_t aPerUnit) {
MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aUnits), "Not a CSS absolute unit");
MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit");

static const float CSSAbsoluteUnitConversionFactors[6][6] = {
// columns: px, cm, mm, in, pt, pc
static const float CSSAbsoluteUnitConversionFactors[7][7] = {
// columns: px, cm, mm, in, pt, pc, q
// px per...:
{1.0f, 37.7952755906f, 3.779528f, 96.0f, 1.33333333333333333f, 16.0f},
{1.0f, 37.7952755906f, 3.779528f, 96.0f, 1.33333333333333333f, 16.0f,
0.06614583333f},
// cm per...:
{0.02645833333f, 1.0f, 0.1f, 2.54f, 0.035277777777777778f,
0.42333333333333333f},
0.42333333333333333f, 2.5f},
// mm per...:
{0.26458333333f, 10.0f, 1.0f, 25.4f, 0.35277777777777778f,
4.2333333333333333f},
4.2333333333333333f, 0.25f},
// in per...:
{0.01041666666f, 0.39370078740157481f, 0.039370078740157481f, 1.0f,
0.013888888888888889f, 0.16666666666666667f},
0.013888888888888889f, 0.16666666666666667f, 6.35f},
// pt per...:
{0.75f, 28.346456692913386f, 2.8346456692913386f, 72.0f, 1.0f, 12.0f},
{0.75f, 28.346456692913386f, 2.8346456692913386f, 72.0f, 1.0f, 12.0f,
0.08819444444f},
// pc per...:
{0.0625f, 2.3622047244094489f, 0.23622047244094489f, 6.0f,
0.083333333333333333f, 1.0f}};
0.083333333333333333f, 1.0f, 1.05833333333f},
// q per...:
{15.118110237f, 40.0f, 4.0f, 0.15748031496f, 11.3385826777f,
0.94488188976f, 1.0f}};

auto ToIndex = [](uint8_t aUnit) {
return aUnit == SVG_LENGTHTYPE_NUMBER ? 0 : aUnit - 5;
Expand Down Expand Up @@ -195,6 +202,9 @@ void SVGLength::GetUnitString(nsAString& aUnit, uint16_t aUnitType) {
case SVG_LENGTHTYPE_PC:
aUnit.AssignLiteral("pc");
return;
case SVG_LENGTHTYPE_Q:
aUnit.AssignLiteral("q");
return;
}
MOZ_ASSERT_UNREACHABLE(
"Unknown unit type! Someone's using an SVGLength "
Expand Down Expand Up @@ -233,6 +243,9 @@ uint16_t SVGLength::GetUnitTypeForString(const nsAString& aUnit) {
if (aUnit.LowerCaseEqualsLiteral("pc")) {
return SVG_LENGTHTYPE_PC;
}
if (aUnit.LowerCaseEqualsLiteral("q")) {
return SVG_LENGTHTYPE_Q;
}
return SVG_LENGTHTYPE_UNKNOWN;
}

Expand Down
33 changes: 8 additions & 25 deletions dom/svg/SVGLength.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,9 @@ class SVGElement;
class SVGLength {
public:
SVGLength()
: mValue(0.0f),
mUnit(dom::SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN) // caught by
// IsValid()
{}
: mValue(0.0f), mUnit(dom::SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN) {}

SVGLength(float aValue, uint8_t aUnit) : mValue(aValue), mUnit(aUnit) {
NS_ASSERTION(IsValid(), "Constructed an invalid length");
}
SVGLength(float aValue, uint8_t aUnit) : mValue(aValue), mUnit(aUnit) {}

bool operator==(const SVGLength& rhs) const {
return mValue == rhs.mValue && mUnit == rhs.mUnit;
Expand All @@ -58,30 +53,24 @@ class SVGLength {

/**
* This will usually return a valid, finite number. There is one exception
* though - see the comment in SetValueAndUnit().
* though. If SVGLengthListSMILType has to convert between unit types and the
* unit conversion is undefined, it will end up passing in and setting
* numeric_limits<float>::quiet_NaN(). The painting code has to be
* able to handle NaN anyway, since conversion to user units may fail in
* general.
*/
float GetValueInCurrentUnits() const { return mValue; }

uint8_t GetUnit() const { return mUnit; }

void SetValueInCurrentUnits(float aValue) {
NS_ASSERTION(std::isfinite(aValue), "Set invalid SVGLength");
mValue = aValue;
NS_ASSERTION(IsValid(), "Set invalid SVGLength");
}

void SetValueAndUnit(float aValue, uint8_t aUnit) {
mValue = aValue;
mUnit = aUnit;

// IsValid() should always be true, with one exception: if
// SVGLengthListSMILType has to convert between unit types and the unit
// conversion is undefined, it will end up passing in and setting
// numeric_limits<float>::quiet_NaN(). Because of that we only check the
// unit here, and allow mValue to be invalid. The painting code has to be
// able to handle NaN anyway, since conversion to user units may fail in
// general.

NS_ASSERTION(IsValidUnitType(mUnit), "Set invalid SVGLength");
}

/**
Expand Down Expand Up @@ -131,12 +120,6 @@ class SVGLength {
uint8_t aUnitType, uint8_t aAxis);

private:
#ifdef DEBUG
bool IsValid() const {
return std::isfinite(mValue) && IsValidUnitType(mUnit);
}
#endif

float mValue;
uint8_t mUnit;
};
Expand Down
53 changes: 33 additions & 20 deletions testing/web-platform/tests/svg/types/scripted/SVGLength-px.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<script src="/resources/testharnessreport.js"></script>
<p></p>
<script>
var cssPixelsPerInch = 96;
const cssPixelsPerInch = 96;
setup(function() {
window.svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svgElement.setAttribute("style", "visibility: hidden; font-size: initial; font-family: initial;");
Expand All @@ -13,18 +13,18 @@

function calculateXHeight() {
// Crude hack to calculate the x-height
var divElement = document.createElement("div");
let divElement = document.createElement("div");
divElement.setAttribute("style", "height: 1ex; font-size: initial; font-family: initial;");
var pElement = document.querySelector("p");
let pElement = document.querySelector("p");
pElement.appendChild(divElement);
var xHeight = divElement.offsetHeight;
let xHeight = divElement.offsetHeight;
pElement.removeChild(divElement);
return xHeight;
}
});

test(function() {
var length = svgElement.createSVGLength();
let length = svgElement.createSVGLength();
length.valueAsString = "2px";
length.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(length.valueAsString, "2");
Expand All @@ -34,7 +34,7 @@
}, document.title + ", unitless");

test(function() {
var length = svgElement.createSVGLength();
let length = svgElement.createSVGLength();
length.valueAsString = "2px";
length.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PERCENTAGE);
assert_equals(length.valueAsString, "2%");
Expand All @@ -44,76 +44,89 @@
}, document.title + ", percentage");

test(function() {
var length = svgElement.createSVGLength();
let length = svgElement.createSVGLength();
length.valueAsString = "2px";
length.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_EMS);
var referenceValue = 2 / fontSize;
let referenceValue = 2 / fontSize;
assert_equals(length.valueAsString, referenceValue.toFixed(6) + "em");
assert_approx_equals(length.valueInSpecifiedUnits, referenceValue, 0.1);
assert_approx_equals(length.value, 2.0, 0.1);
assert_equals(length.unitType, SVGLength.SVG_LENGTHTYPE_EMS);
}, document.title + ", ems");

test(function() {
var length = svgElement.createSVGLength();
let length = svgElement.createSVGLength();
length.valueAsString = "2px";
length.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_EXS);
var referenceValue = 2 / xHeight;
let referenceValue = 2 / xHeight;
// Don't check valueAsString here, it's unreliable across browsers.
assert_approx_equals(length.valueInSpecifiedUnits, referenceValue, 0.1);
assert_approx_equals(length.value, 2.0, 0.1);
assert_equals(length.unitType, SVGLength.SVG_LENGTHTYPE_EXS);
}, document.title + ", exs");

test(function() {
var length = svgElement.createSVGLength();
let length = svgElement.createSVGLength();
length.valueAsString = "48px";
length.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_CM);
var referenceValue = 48 * 2.54 / cssPixelsPerInch;
let referenceValue = 48 * 2.54 / cssPixelsPerInch;
assert_equals(length.valueAsString, referenceValue.toFixed(2) + "cm");
assert_approx_equals(length.valueInSpecifiedUnits, referenceValue, 0.01);
assert_equals(length.value, 48);
assert_equals(length.unitType, SVGLength.SVG_LENGTHTYPE_CM);
}, document.title + ", cm");

test(function() {
var length = svgElement.createSVGLength();
let length = svgElement.createSVGLength();
length.valueAsString = "48px";
length.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_MM);
var referenceValue = 48 * 25.4 / cssPixelsPerInch;
let referenceValue = 48 * 25.4 / cssPixelsPerInch;
assert_equals(length.valueAsString, referenceValue.toFixed(1) + "mm");
assert_approx_equals(length.valueInSpecifiedUnits, referenceValue, 0.01);
assert_equals(length.value, 48);
assert_equals(length.unitType, SVGLength.SVG_LENGTHTYPE_MM);
}, document.title + ", mm");

test(function() {
var length = svgElement.createSVGLength();
let length = svgElement.createSVGLength();
length.valueAsString = "48q";
assert_equals(length.unitType, SVGLength.SVG_LENGTHTYPE_UNKNOWN);
assert_equals(length.valueAsString, "48q");
let referenceValue = 48 / 4 * 25.4 / cssPixelsPerInch;
assert_approx_equals(length.value, referenceValue, 0.01);
length.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_MM);
referenceValue = 48 / 4;
assert_equals(length.valueAsString, referenceValue + "mm");
assert_approx_equals(length.valueInSpecifiedUnits, referenceValue, 0.01);
}, document.title + ", q");

test(function() {
let length = svgElement.createSVGLength();
length.valueAsString = "48px";
length.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_IN);
var referenceValue = 48 / cssPixelsPerInch;
let referenceValue = 48 / cssPixelsPerInch;
assert_equals(length.valueAsString, referenceValue + "in");
assert_equals(length.valueInSpecifiedUnits, referenceValue);
assert_equals(length.value, 48);
assert_equals(length.unitType, SVGLength.SVG_LENGTHTYPE_IN);
}, document.title + ", in");

test(function() {
var length = svgElement.createSVGLength();
let length = svgElement.createSVGLength();
length.valueAsString = "4px";
length.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PT);
var referenceValue = 4 / cssPixelsPerInch * 72;
let referenceValue = 4 / cssPixelsPerInch * 72;
assert_equals(length.valueAsString, referenceValue + "pt");
assert_equals(length.valueInSpecifiedUnits, referenceValue);
assert_equals(length.value, 4);
assert_equals(length.unitType, SVGLength.SVG_LENGTHTYPE_PT);
}, document.title + ", pt");

test(function() {
var length = svgElement.createSVGLength();
let length = svgElement.createSVGLength();
length.valueAsString = "16px";
length.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PC);
var referenceValue = 16 / cssPixelsPerInch * 6;
let referenceValue = 16 / cssPixelsPerInch * 6;
// Don't check valueAsString here, it's unreliable across browsers.
assert_equals(length.valueInSpecifiedUnits, referenceValue);
assert_equals(length.value, 16);
Expand Down

0 comments on commit 017e8bb

Please sign in to comment.