Commit 77c8625d authored by ed@opera.com's avatar ed@opera.com

[SVG2] Allow leading and trailing whitespace in svg attributes using

<integer>, <angle>, <number>, <length> and <percentage>.

BUG=377503,339899

Committed: https://src.chromium.org/viewvc/blink?view=rev&revision=175574

Review URL: https://codereview.chromium.org/302643004

git-svn-id: svn://svn.chromium.org/blink/trunk@175785 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent eb69d642
/**
* Tests attribute parsing and handling of whitespace in attribute values.
*
* @param type Name of the type being tested (only for test output)
* @param target The element that should be tested
* @param attribute The name of the attribute that should be tested
* @param expected The fallback/default value that is the expectation for invalid values
* @param whitespace An array of strings that are valid whitespace characters
* @param valid An array of strings containing valid attribute values
* @param invalid An array of strings containing invalid attribute values
* @param garbage An array of strings containing values that would make a valid value invalid when concatenated
* @param assert_valid_custom A function for asserting validity of a valid value, arguments passed to this function: the element and the string from valid values array
* @param assert_invalid_custom A function for asserting that an invalid value results in the expected default value, arguments passed to this function: the element and the expected value
*/
function testType(type, target, attribute, expected, whitespace, valid, invalid, validunits, garbage, assert_valid_custom, assert_invalid_custom) {
whitespace.forEach(function(leading) {
whitespace.forEach(function(trailing) {
valid.forEach(function(value) {
validunits.forEach(function(unit) {
var valueStr = leading + value + unit + trailing;
var escapedValueStr = valueStr.replace(/(\r)/g, '\\r').replace(/(\n)/g, '\\n').replace(/(\t)/g, '\\t').replace(/(\f)/g, '\\f');
test(function() {
try {
target.setAttribute(attribute, valueStr);
assert_equals(target.getAttribute(attribute), valueStr);
assert_valid_custom(target, value);
}
finally {
target.removeAttribute(attribute);
}
}, "Test " + type + " valid value: " + escapedValueStr );
});
});
// test invalid values
invalid.forEach(function(value) {
validunits.forEach(function(unit) {
var valueStr = leading + value + unit + trailing;
var escapedValueStr = valueStr.replace(/(\r)/g, '\\r').replace(/(\n)/g, '\\n').replace(/(\t)/g, '\\t').replace(/(\f)/g, '\\f');
test(function() {
try {
target.setAttribute(attribute, valueStr);
assert_equals(target.getAttribute(attribute), valueStr);
assert_invalid_custom(target, expected);
}
finally {
target.removeAttribute(attribute);
}
}, "Test " + type + " invalid value: " + escapedValueStr);
});
});
});
// test whitespace between value and unit
validunits.forEach(function(unit) {
if (unit == "" || leading == "")
return;
valid.forEach(function(value) {
var valueStr = value + leading + unit;
var escapedValueStr = valueStr.replace(/(\r)/g, '\\r').replace(/(\n)/g, '\\n').replace(/(\t)/g, '\\t').replace(/(\f)/g, '\\f');
test(function() {
try {
target.setAttribute(attribute, valueStr);
assert_equals(target.getAttribute(attribute), valueStr);
assert_invalid_custom(target, expected);
}
finally {
target.removeAttribute(attribute);
}
}, "Test " + type + " WS invalid value: " + escapedValueStr);
});
});
// test trailing garbage
garbage.forEach(function(trailing) {
valid.forEach(function(value) {
var valueStr = leading + value + trailing;
var escapedValueStr = valueStr.replace(/(\r)/g, '\\r').replace(/(\n)/g, '\\n').replace(/(\t)/g, '\\t').replace(/(\f)/g, '\\f');
test(function() {
try {
target.setAttribute(attribute, valueStr);
assert_equals(target.getAttribute(attribute), valueStr);
assert_invalid_custom(target, expected);
}
finally {
target.removeAttribute(attribute);
}
}, "Test " + type + " trailing garbage, value: " + escapedValueStr);
});
});
});
}
<!doctype html>
<title>Whitespace in <angle> attribute values</title>
<script src=../../resources/testharness.js></script>
<script src=../../resources/testharnessreport.js></script>
<script src=resources/whitespace-helper.js></script>
<svg id="testcontainer">
<defs>
<marker/>
</defs>
</svg>
<div id=log></div>
<script>
var svg = document.querySelector("svg");
// test length values
var EPSILON = Math.pow(2, -24); // float epsilon
var whitespace = [ "", " ", " ", "\r\n\t ", "\f" ];
var garbage = [ "a", "e", "foo", ")90" ];
var validunits = [ "", "em", "ex", "px", "in", "cm", "mm", "pt", "pc", "%" ];
testType("<angle>",
document.querySelector("marker"),
"orient",
0, // expected default value
whitespace,
[ "-47", ".1", "0.35", "1e-10", "+32", "+17E-1", "17e+2" ], // valid
[ Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, "fnord", "E", "e", "e+", "E-", "-", "+", "-.", ".-", ".", "+.", ".E0", "e1" ], // invalid
[ "", "deg", "rad", "grad" ], // valid units
garbage,
function(elm, value) { assert_approx_equals(elm.orientAngle.baseVal.valueInSpecifiedUnits, parseFloat(value), EPSILON); },
function(elm, expected) { assert_approx_equals(elm.orientAngle.baseVal.value, expected, EPSILON); } );
</script>
<!doctype html>
<title>Whitespace in attribute values tests</title>
<script src=../../resources/testharness.js></script>
<script src=../../resources/testharnessreport.js></script>
<script src=resources/whitespace-helper.js></script>
<svg id="testcontainer">
<defs>
<marker/>
<stop/>
<filter>
<feTurbulence></feTurbulence>
</filter>
</defs>
</svg>
<div id=log></div>
<script>
var svg = document.querySelector("svg");
// test length values
var EPSILON = Math.pow(2, -24); // float epsilon
var whitespace = [ "", " ", " ", "\r\n\t ", "\f" ];
var garbage = [ "a", "e", "foo", ")90" ];
var validunits = [ "", "em", "ex", "px", "in", "cm", "mm", "pt", "pc", "%" ];
testType("<integer>",
document.getElementsByTagName("feTurbulence")[0], // workaround for broken querySelector on camelcased elements
"numOctaves",
0, // expected default value (FIXME: should be 1)
whitespace,
[ "-47", "0", "+32", "1241245" ],
[ Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, "fnord", "E", "e", "e+", "E-", "-", "+", "-.", ".-", ".", "+.", ".E0", "e1", "+17E-1", "17e+2", "0.35", "1e-10", ".1" ],
[""], // valid units
garbage,
function(elm, value) { assert_equals(elm.numOctaves.baseVal, parseInt(value)); },
function(elm, expected) { assert_equals(elm.numOctaves.baseVal, expected); } );
</script>
This source diff could not be displayed because it is too large. You can view the blob instead.
<!doctype html>
<title>Whitespace in attribute values tests</title>
<script src=../../resources/testharness.js></script>
<script src=../../resources/testharnessreport.js></script>
<script src=resources/whitespace-helper.js></script>
<svg id="testcontainer">
<defs>
<marker/>
<stop/>
<filter>
<feTurbulence></feTurbulence>
</filter>
</defs>
</svg>
<div id=log></div>
<script>
var svg = document.querySelector("svg");
// test length values
var EPSILON = Math.pow(2, -24); // float epsilon
var whitespace = [ "", " ", " ", "\r\n\t ", "\f" ];
var validunits = [ "", "em", "ex", "px", "in", "cm", "mm", "pt", "pc", "%" ];
// This test was split out from whitespace-length.html because the trybots were too slow.
testType("<length>",
svg,
"x",
0, // expected default value
whitespace,
[], // valid
[ Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, "fnord", "E", "e", "e+", "E-", "-", "+", "-.", ".-", ".", "+.", ".E0", "e1" ], // invalid
validunits,
[], // garbage
function(elm, value) { assert_approx_equals(elm.x.baseVal.valueInSpecifiedUnits, parseFloat(value), EPSILON); },
function(elm, expected) { assert_approx_equals(elm.x.baseVal.value, expected, EPSILON); } );
</script>
<!doctype html>
<title>Whitespace in attribute values tests</title>
<script src=../../resources/testharness.js></script>
<script src=../../resources/testharnessreport.js></script>
<script src=resources/whitespace-helper.js></script>
<svg id="testcontainer">
<defs>
<marker/>
<stop/>
<filter>
<feTurbulence></feTurbulence>
</filter>
</defs>
</svg>
<div id=log></div>
<script>
var svg = document.querySelector("svg");
// test length values
var EPSILON = Math.pow(2, -24); // float epsilon
var whitespace = [ "", " ", " ", "\r\n\t ", "\f" ];
var garbage = [ "a", "e", "foo", ")90" ];
var validunits = [ "", "em", "ex", "px", "in", "cm", "mm", "pt", "pc", "%" ];
testType("<length>",
svg,
"x",
0, // expected default value
whitespace,
[ "-47", ".1", "0.35", "1e-10", "+32", "+17E-1", "17e+2" ], // valid
[], // invalid (split out the next line to whitespace-length-invalid.html because trybots were too slow )
// [ Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, "fnord", "E", "e", "e+", "E-", "-", "+", "-.", ".-", ".", "+.", ".E0", "e1" ],
validunits,
garbage,
function(elm, value) { assert_approx_equals(elm.x.baseVal.valueInSpecifiedUnits, parseFloat(value), EPSILON); },
function(elm, expected) { assert_approx_equals(elm.x.baseVal.value, expected, EPSILON); } );
</script>
<!doctype html>
<title>Whitespace in attribute values tests</title>
<script src=../../resources/testharness.js></script>
<script src=../../resources/testharnessreport.js></script>
<script src=resources/whitespace-helper.js></script>
<svg id="testcontainer">
<defs>
<marker/>
<stop/>
<filter>
<feTurbulence></feTurbulence>
</filter>
</defs>
</svg>
<div id=log></div>
<script>
var svg = document.querySelector("svg");
// test length values
var EPSILON = Math.pow(2, -24); // float epsilon
var whitespace = [ "", " ", " ", "\r\n\t ", "\f" ];
var garbage = [ "a", "e", "foo", ")90" ];
var validunits = [ "", "em", "ex", "px", "in", "cm", "mm", "pt", "pc", "%" ];
testType("<number>",
document.querySelector("stop"),
"offset",
0, // expected default value
whitespace,
[ "-47", ".1", "0.35", "1e-10", "+32", "+17E-1", "17e+2" ], // valid
[ Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, "fnord", "E", "e", "e+", "E-", "-", "+", "-.", ".-", ".", "+.", ".E0", "e1" ], // invalid
[ "" ], // valid units
garbage,
function(elm, value) { assert_approx_equals(elm.offset.baseVal, parseFloat(value), EPSILON); },
function(elm, expected) { assert_approx_equals(elm.offset.baseVal, expected, EPSILON); } );
testType("<percentage>",
document.querySelector("stop"),
"offset",
0, // expected default value
whitespace,
[ "-47", ".1", "0.35", "1e-10", "+32", "+17E-1", "17e+2" ], // valid
[ Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, "fnord", "E", "e", "e+", "E-", "-", "+", "-.", ".-", ".", "+.", ".E0", "e1" ], // invalid
[ "%" ], // valid units
garbage,
function(elm, value) { assert_approx_equals(elm.offset.baseVal, parseFloat(value)/ 100, EPSILON); },
function(elm, expected) { assert_approx_equals(elm.offset.baseVal, expected, EPSILON); } );
</script>
...@@ -15,8 +15,8 @@ animate.setAttribute("id", "animation"); ...@@ -15,8 +15,8 @@ animate.setAttribute("id", "animation");
animate.setAttribute("attributeName", "width"); animate.setAttribute("attributeName", "width");
animate.setAttribute("begin", "click"); animate.setAttribute("begin", "click");
animate.setAttribute("dur", "4s"); animate.setAttribute("dur", "4s");
animate.setAttribute("from", " 100px"); animate.setAttribute("from", " 100pxERROR");
animate.setAttribute("to", " 200px"); animate.setAttribute("to", " 200pxERROR");
rect.appendChild(animate); rect.appendChild(animate);
rootSVGElement.appendChild(rect); rootSVGElement.appendChild(rect);
......
...@@ -15,8 +15,8 @@ animate.setAttribute("id", "animation"); ...@@ -15,8 +15,8 @@ animate.setAttribute("id", "animation");
animate.setAttribute("attributeName", "width"); animate.setAttribute("attributeName", "width");
animate.setAttribute("begin", "click"); animate.setAttribute("begin", "click");
animate.setAttribute("dur", "4s"); animate.setAttribute("dur", "4s");
animate.setAttribute("from", "100px "); animate.setAttribute("from", "100px ERROR");
animate.setAttribute("to", "200px "); animate.setAttribute("to", "200px ERROR");
rect.appendChild(animate); rect.appendChild(animate);
rootSVGElement.appendChild(rect); rootSVGElement.appendChild(rect);
......
...@@ -2,8 +2,6 @@ CONSOLE ERROR: Error: Invalid value for <rect> attribute x=" " ...@@ -2,8 +2,6 @@ CONSOLE ERROR: Error: Invalid value for <rect> attribute x=" "
CONSOLE ERROR: Error: Invalid value for <rect> attribute x="foo" CONSOLE ERROR: Error: Invalid value for <rect> attribute x="foo"
CONSOLE ERROR: Error: Invalid value for <rect> attribute x="10foo" CONSOLE ERROR: Error: Invalid value for <rect> attribute x="10foo"
CONSOLE ERROR: Error: Invalid value for <rect> attribute x="px" CONSOLE ERROR: Error: Invalid value for <rect> attribute x="px"
CONSOLE ERROR: Error: Invalid value for <rect> attribute x="10px "
CONSOLE ERROR: Error: Invalid value for <rect> attribute x="10% "
CONSOLE ERROR: Error: Invalid value for <rect> attribute x="10 % " CONSOLE ERROR: Error: Invalid value for <rect> attribute x="10 % "
CONSOLE ERROR: Error: Invalid value for <rect> attribute x="10 %" CONSOLE ERROR: Error: Invalid value for <rect> attribute x="10 %"
CONSOLE ERROR: Error: Invalid value for <rect> attribute x="10 px " CONSOLE ERROR: Error: Invalid value for <rect> attribute x="10 px "
...@@ -18,8 +16,8 @@ PASS rect.setAttribute('x', ' '); rect.x.baseVal.valueAsString is '0' ...@@ -18,8 +16,8 @@ PASS rect.setAttribute('x', ' '); rect.x.baseVal.valueAsString is '0'
PASS rect.setAttribute('x', 'foo'); rect.x.baseVal.valueAsString is '0' PASS rect.setAttribute('x', 'foo'); rect.x.baseVal.valueAsString is '0'
PASS rect.setAttribute('x', '10foo'); rect.x.baseVal.valueAsString is '0' PASS rect.setAttribute('x', '10foo'); rect.x.baseVal.valueAsString is '0'
PASS rect.setAttribute('x', 'px'); rect.x.baseVal.valueAsString is '0' PASS rect.setAttribute('x', 'px'); rect.x.baseVal.valueAsString is '0'
PASS rect.setAttribute('x', '10px '); rect.x.baseVal.valueAsString is '0' PASS rect.setAttribute('x', '10px '); rect.x.baseVal.valueAsString is '10px'
PASS rect.setAttribute('x', '10% '); rect.x.baseVal.valueAsString is '0' PASS rect.setAttribute('x', '10% '); rect.x.baseVal.valueAsString is '10%'
PASS rect.setAttribute('x', '10 % '); rect.x.baseVal.valueAsString is '0' PASS rect.setAttribute('x', '10 % '); rect.x.baseVal.valueAsString is '0'
PASS rect.setAttribute('x', '10 %'); rect.x.baseVal.valueAsString is '0' PASS rect.setAttribute('x', '10 %'); rect.x.baseVal.valueAsString is '0'
PASS rect.setAttribute('x', '10 px '); rect.x.baseVal.valueAsString is '0' PASS rect.setAttribute('x', '10 px '); rect.x.baseVal.valueAsString is '0'
......
...@@ -19,8 +19,8 @@ trySettingLength(" ", "'0'"); ...@@ -19,8 +19,8 @@ trySettingLength(" ", "'0'");
trySettingLength("foo", "'0'"); trySettingLength("foo", "'0'");
trySettingLength("10foo", "'0'"); trySettingLength("10foo", "'0'");
trySettingLength("px", "'0'"); trySettingLength("px", "'0'");
trySettingLength("10px ", "'0'"); trySettingLength("10px ", "'10px'");
trySettingLength("10% ", "'0'"); trySettingLength("10% ", "'10%'");
trySettingLength("10 % ", "'0'"); trySettingLength("10 % ", "'0'");
trySettingLength("10 %", "'0'"); trySettingLength("10 %", "'0'");
trySettingLength("10 px ", "'0'"); trySettingLength("10 px ", "'0'");
......
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="200">
<rect x="0" y="0" width="300" height="200" fill="green"/>
<!-- This next line should be hidden since it has an unsupported
value for the "width" attribute (it has a leading space, which
isn't allowed by any prose in the spec as far as I can tell. -->
<rect x="0" y="0" width=" 300" height="200" fill="red"/>
</svg>
...@@ -152,35 +152,27 @@ static SVGAngle::SVGAngleType stringToAngleType(const CharType*& ptr, const Char ...@@ -152,35 +152,27 @@ static SVGAngle::SVGAngleType stringToAngleType(const CharType*& ptr, const Char
if (ptr == end) if (ptr == end)
return SVGAngle::SVG_ANGLETYPE_UNSPECIFIED; return SVGAngle::SVG_ANGLETYPE_UNSPECIFIED;
const CharType firstChar = *ptr; SVGAngle::SVGAngleType type = SVGAngle::SVG_ANGLETYPE_UNKNOWN;
const CharType firstChar = *ptr++;
// If the unit contains only one character, the angle type is unknown.
++ptr; if (isHTMLSpace<CharType>(firstChar)) {
if (ptr == end) type = SVGAngle::SVG_ANGLETYPE_UNSPECIFIED;
return SVGAngle::SVG_ANGLETYPE_UNKNOWN; } else if (end - ptr >= 2) {
const CharType secondChar = *ptr++;
const CharType secondChar = *ptr; const CharType thirdChar = *ptr++;
if (firstChar == 'd' && secondChar == 'e' && thirdChar == 'g') {
// If the unit contains only two characters, the angle type is unknown. type = SVGAngle::SVG_ANGLETYPE_DEG;
++ptr; } else if (firstChar == 'r' && secondChar == 'a' && thirdChar == 'd') {
if (ptr == end) type = SVGAngle::SVG_ANGLETYPE_RAD;
return SVGAngle::SVG_ANGLETYPE_UNKNOWN; } else if (ptr != end) {
const CharType fourthChar = *ptr++;
const CharType thirdChar = *ptr; if (firstChar == 'g' && secondChar == 'r' && thirdChar == 'a' && fourthChar == 'd')
if (firstChar == 'd' && secondChar == 'e' && thirdChar == 'g') type = SVGAngle::SVG_ANGLETYPE_GRAD;
return SVGAngle::SVG_ANGLETYPE_DEG; }
if (firstChar == 'r' && secondChar == 'a' && thirdChar == 'd') }
return SVGAngle::SVG_ANGLETYPE_RAD;
// If the unit contains three characters, but is not deg or rad, then it's unknown.
++ptr;
if (ptr == end)
return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
const CharType fourthChar = *ptr;
if (firstChar == 'g' && secondChar == 'r' && thirdChar == 'a' && fourthChar == 'd') if (!skipOptionalSVGSpaces(ptr, end))
return SVGAngle::SVG_ANGLETYPE_GRAD; return type;
return SVGAngle::SVG_ANGLETYPE_UNKNOWN; return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
} }
...@@ -215,7 +207,7 @@ static bool parseValue(const String& value, float& valueInSpecifiedUnits, SVGAng ...@@ -215,7 +207,7 @@ static bool parseValue(const String& value, float& valueInSpecifiedUnits, SVGAng
const CharType* ptr = value.getCharacters<CharType>(); const CharType* ptr = value.getCharacters<CharType>();
const CharType* end = ptr + value.length(); const CharType* end = ptr + value.length();
if (!parseNumber(ptr, end, valueInSpecifiedUnits, false)) if (!parseNumber(ptr, end, valueInSpecifiedUnits, AllowLeadingWhitespace))
return false; return false;
unitType = stringToAngleType(ptr, end); unitType = stringToAngleType(ptr, end);
......
...@@ -110,7 +110,7 @@ static void parseKeySplinesInternal(const String& string, Vector<UnitBezier>& re ...@@ -110,7 +110,7 @@ static void parseKeySplinesInternal(const String& string, Vector<UnitBezier>& re
} }
float posD = 0; float posD = 0;
if (!parseNumber(ptr, end, posD, false)) { if (!parseNumber(ptr, end, posD, DisallowWhitespace)) {
result.clear(); result.clear();
return; return;
} }
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "config.h" #include "config.h"
#include "core/svg/SVGInteger.h" #include "core/svg/SVGInteger.h"
#include "core/html/parser/HTMLParserIdioms.h"
#include "core/svg/SVGAnimationElement.h" #include "core/svg/SVGAnimationElement.h"
...@@ -66,7 +67,7 @@ void SVGInteger::setValueAsString(const String& string, ExceptionState& exceptio ...@@ -66,7 +67,7 @@ void SVGInteger::setValueAsString(const String& string, ExceptionState& exceptio
} }
bool valid = true; bool valid = true;
m_value = string.toIntStrict(&valid); m_value = stripLeadingAndTrailingHTMLSpaces(string).toIntStrict(&valid);
if (!valid) { if (!valid) {
exceptionState.throwDOMException(SyntaxError, "The value provided ('" + string + "') is invalid."); exceptionState.throwDOMException(SyntaxError, "The value provided ('" + string + "') is invalid.");
......
...@@ -73,32 +73,41 @@ SVGLengthType stringToLengthType(const CharType*& ptr, const CharType* end) ...@@ -73,32 +73,41 @@ SVGLengthType stringToLengthType(const CharType*& ptr, const CharType* end)
if (ptr == end) if (ptr == end)
return LengthTypeNumber; return LengthTypeNumber;
const UChar firstChar = *ptr; SVGLengthType type = LengthTypeUnknown;
const CharType firstChar = *ptr++;
if (++ptr == end)
return firstChar == '%' ? LengthTypePercentage : LengthTypeUnknown; if (firstChar == '%') {
type = LengthTypePercentage;
const UChar secondChar = *ptr; } else if (isHTMLSpace<CharType>(firstChar)) {
type = LengthTypeNumber;
if (++ptr != end) } else if (ptr < end) {
return LengthTypeUnknown; const CharType secondChar = *ptr++;
if (firstChar == 'e' && secondChar == 'm') if (firstChar == 'p') {
return LengthTypeEMS; if (secondChar == 'x')
if (firstChar == 'e' && secondChar == 'x') type = LengthTypePX;
return LengthTypeEXS; if (secondChar == 't')
if (firstChar == 'p' && secondChar == 'x') type = LengthTypePT;
return LengthTypePX; if (secondChar == 'c')
if (firstChar == 'c' && secondChar == 'm') type = LengthTypePC;
return LengthTypeCM; } else if (firstChar == 'e') {
if (firstChar == 'm' && secondChar == 'm') if (secondChar == 'm')
return LengthTypeMM; type = LengthTypeEMS;
if (firstChar == 'i' && secondChar == 'n') if (secondChar == 'x')
return LengthTypeIN; type = LengthTypeEXS;
if (firstChar == 'p' && secondChar == 't') } else if (firstChar == 'c' && secondChar == 'm') {
return LengthTypePT; type = LengthTypeCM;
if (firstChar == 'p' && secondChar == 'c') } else if (firstChar == 'm' && secondChar == 'm') {
return LengthTypePC; type = LengthTypeMM;
} else if (firstChar == 'i' && secondChar == 'n') {
type = LengthTypeIN;
} else if (isHTMLSpace<CharType>(firstChar) && isHTMLSpace<CharType>(secondChar)) {
type = LengthTypeNumber;
}
}
if (!skipOptionalSVGSpaces(ptr, end))
return type;
return LengthTypeUnknown; return LengthTypeUnknown;
} }
...@@ -189,7 +198,7 @@ static bool parseValueInternal(const String& string, float& convertedNumber, SVG ...@@ -189,7 +198,7 @@ static bool parseValueInternal(const String& string, float& convertedNumber, SVG
const CharType* ptr = string.getCharacters<CharType>(); const CharType* ptr = string.getCharacters<CharType>();
const CharType* end = ptr + string.length(); const CharType* end = ptr + string.length();
if (!parseNumber(ptr, end, convertedNumber, false)) if (!parseNumber(ptr, end, convertedNumber, AllowLeadingWhitespace))
return false; return false;
type = stringToLengthType(ptr, end); type = stringToLengthType(ptr, end);
......
...@@ -83,7 +83,7 @@ void SVGLengthList::parseInternal(const CharType*& ptr, const CharType* end, Exc ...@@ -83,7 +83,7 @@ void SVGLengthList::parseInternal(const CharType*& ptr, const CharType* end, Exc
clear(); clear();
while (ptr < end) { while (ptr < end) {
const CharType* start = ptr; const CharType* start = ptr;
while (ptr < end && *ptr != ',' && !isSVGSpace(*ptr)) while (ptr < end && *ptr != ',' && !isHTMLSpace<CharType>(*ptr))
ptr++; ptr++;
if (ptr == start) if (ptr == start)
break; break;
......
...@@ -62,7 +62,7 @@ String SVGNumber::valueAsString() const ...@@ -62,7 +62,7 @@ String SVGNumber::valueAsString() const
template<typename CharType> template<typename CharType>
bool SVGNumber::parse(const CharType*& ptr, const CharType* end) bool SVGNumber::parse(const CharType*& ptr, const CharType* end)
{ {
if (!parseNumber(ptr, end, m_value, false)) { if (!parseNumber(ptr, end, m_value, AllowLeadingAndTrailingWhitespace)) {
m_value = 0; m_value = 0;
return false; return false;
} }
...@@ -127,21 +127,12 @@ PassRefPtr<SVGNumber> SVGNumberAcceptPercentage::clone() const ...@@ -127,21 +127,12 @@ PassRefPtr<SVGNumber> SVGNumberAcceptPercentage::clone() const
void SVGNumberAcceptPercentage::setValueAsString(const String& string, ExceptionState& exceptionState) void SVGNumberAcceptPercentage::setValueAsString(const String& string, ExceptionState& exceptionState)
{ {
if (string.isEmpty()) { bool valid = parseNumberOrPercentage(string, m_value);
m_value = 0;
return;
}
if (string.endsWith('%')) {
SVGNumber::setValueAsString(string.left(string.length() - 1), exceptionState);
if (exceptionState.hadException())
return;
m_value /= 100.0f; if (!valid) {
return; exceptionState.throwDOMException(SyntaxError, "The value provided ('" + string + "') is invalid.");
m_value = 0;
} }
SVGNumber::setValueAsString(string, exceptionState);
} }
SVGNumberAcceptPercentage::SVGNumberAcceptPercentage(float value) SVGNumberAcceptPercentage::SVGNumberAcceptPercentage(float value)
......
...@@ -43,7 +43,7 @@ static inline bool isValidRange(const FloatType& x) ...@@ -43,7 +43,7 @@ static inline bool isValidRange(const FloatType& x)
// at a higher precision internally, without any unnecessary runtime cost or code // at a higher precision internally, without any unnecessary runtime cost or code
// complexity. // complexity.
template <typename CharType, typename FloatType> template <typename CharType, typename FloatType>
static bool genericParseNumber(const CharType*& ptr, const CharType* end, FloatType& number, bool skip) static bool genericParseNumber(const CharType*& ptr, const CharType* end, FloatType& number, WhitespaceMode mode)
{ {
FloatType integer, decimal, frac, exponent; FloatType integer, decimal, frac, exponent;
int sign, expsign; int sign, expsign;
...@@ -56,6 +56,9 @@ static bool genericParseNumber(const CharType*& ptr, const CharType* end, FloatT ...@@ -56,6 +56,9 @@ static bool genericParseNumber(const CharType*& ptr, const CharType* end, FloatT
sign = 1; sign = 1;
expsign = 1; expsign = 1;
if (mode & AllowLeadingWhitespace)
skipOptionalSVGSpaces(ptr, end);
// read the sign // read the sign
if (ptr < end && *ptr == '+') if (ptr < end && *ptr == '+')
ptr++; ptr++;
...@@ -136,7 +139,7 @@ static bool genericParseNumber(const CharType*& ptr, const CharType* end, FloatT ...@@ -136,7 +139,7 @@ static bool genericParseNumber(const CharType*& ptr, const CharType* end, FloatT
if (start == ptr) if (start == ptr)
return false; return false;
if (skip) if (mode & AllowTrailingWhitespace)
skipOptionalSVGSpacesOrDelimiter(ptr, end); skipOptionalSVGSpacesOrDelimiter(ptr, end);
return true; return true;
...@@ -147,21 +150,21 @@ bool parseSVGNumber(CharType* begin, size_t length, double& number) ...@@ -147,21 +150,21 @@ bool parseSVGNumber(CharType* begin, size_t length, double& number)
{ {
const CharType* ptr = begin; const CharType* ptr = begin;
const CharType* end = ptr + length; const CharType* end = ptr + length;
return genericParseNumber(ptr, end, number, false); return genericParseNumber(ptr, end, number, AllowLeadingAndTrailingWhitespace);
} }
// Explicitly instantiate the two flavors of parseSVGNumber() to satisfy external callers // Explicitly instantiate the two flavors of parseSVGNumber() to satisfy external callers
template bool parseSVGNumber(LChar* begin, size_t length, double&); template bool parseSVGNumber(LChar* begin, size_t length, double&);
template bool parseSVGNumber(UChar* begin, size_t length, double&); template bool parseSVGNumber(UChar* begin, size_t length, double&);
bool parseNumber(const LChar*& ptr, const LChar* end, float& number, bool skip) bool parseNumber(const LChar*& ptr, const LChar* end, float& number, WhitespaceMode mode)
{ {
return genericParseNumber(ptr, end, number, skip); return genericParseNumber(ptr, end, number, mode);
} }
bool parseNumber(const UChar*& ptr, const UChar* end, float& number, bool skip) bool parseNumber(const UChar*& ptr, const UChar* end, float& number, WhitespaceMode mode)
{ {
return genericParseNumber(ptr, end, number, skip); return genericParseNumber(ptr, end, number, mode);
} }
// only used to parse largeArcFlag and sweepFlag which must be a "0" or "1" // only used to parse largeArcFlag and sweepFlag which must be a "0" or "1"
...@@ -202,7 +205,7 @@ static bool genericParseNumberOptionalNumber(const CharType*& ptr, const CharTyp ...@@ -202,7 +205,7 @@ static bool genericParseNumberOptionalNumber(const CharType*& ptr, const CharTyp
if (ptr == end) if (ptr == end)
y = x; y = x;
else if (!parseNumber(ptr, end, y, false)) else if (!parseNumber(ptr, end, y, AllowLeadingAndTrailingWhitespace))
return false; return false;
return ptr == end; return ptr == end;
...@@ -212,6 +215,7 @@ bool parseNumberOptionalNumber(const String& string, float& x, float& y) ...@@ -212,6 +215,7 @@ bool parseNumberOptionalNumber(const String& string, float& x, float& y)
{ {
if (string.isEmpty()) if (string.isEmpty())
return false; return false;
if (string.is8Bit()) { if (string.is8Bit()) {
const LChar* ptr = string.characters8(); const LChar* ptr = string.characters8();
const LChar* end = ptr + string.length(); const LChar* end = ptr + string.length();
...@@ -222,6 +226,43 @@ bool parseNumberOptionalNumber(const String& string, float& x, float& y) ...@@ -222,6 +226,43 @@ bool parseNumberOptionalNumber(const String& string, float& x, float& y)
return genericParseNumberOptionalNumber(ptr, end, x, y); return genericParseNumberOptionalNumber(ptr, end, x, y);
} }
template<typename CharType>
bool genericParseNumberOrPercentage(const CharType*& ptr, const CharType* end, float& number)
{
if (genericParseNumber(ptr, end, number, AllowLeadingWhitespace)) {
if (ptr == end)
return true;
bool isPercentage = (*ptr == '%');
if (isPercentage)
ptr++;
skipOptionalSVGSpaces(ptr, end);
if (isPercentage)
number /= 100.f;
return ptr == end;
}
return false;
}
bool parseNumberOrPercentage(const String& string, float& number)
{
if (string.isEmpty())
return false;
if (string.is8Bit()) {
const LChar* ptr = string.characters8();
const LChar* end = ptr + string.length();
return genericParseNumberOrPercentage(ptr, end, number);
}
const UChar* ptr = string.characters16();
const UChar* end = ptr + string.length();
return genericParseNumberOrPercentage(ptr, end, number);
}
template<typename CharType> template<typename CharType>
static bool parseGlyphName(const CharType*& ptr, const CharType* end, HashSet<String>& values) static bool parseGlyphName(const CharType*& ptr, const CharType* end, HashSet<String>& values)
{ {
...@@ -238,7 +279,7 @@ static bool parseGlyphName(const CharType*& ptr, const CharType* end, HashSet<St ...@@ -238,7 +279,7 @@ static bool parseGlyphName(const CharType*& ptr, const CharType* end, HashSet<St
// walk backwards from the ; to ignore any whitespace // walk backwards from the ; to ignore any whitespace
const CharType* inputEnd = ptr - 1; const CharType* inputEnd = ptr - 1;
while (inputStart < inputEnd && isSVGSpace(*inputEnd)) while (inputStart < inputEnd && isHTMLSpace<CharType>(*inputEnd))
--inputEnd; --inputEnd;
values.add(String(inputStart, inputEnd - inputStart + 1)); values.add(String(inputStart, inputEnd - inputStart + 1));
...@@ -390,7 +431,7 @@ static Vector<String> genericParseDelimitedString(const CharType*& ptr, const Ch ...@@ -390,7 +431,7 @@ static Vector<String> genericParseDelimitedString(const CharType*& ptr, const Ch
// walk backwards from the ; to ignore any whitespace // walk backwards from the ; to ignore any whitespace
const CharType* inputEnd = ptr - 1; const CharType* inputEnd = ptr - 1;
while (inputStart < inputEnd && isSVGSpace(*inputEnd)) while (inputStart < inputEnd && isHTMLSpace<CharType>(*inputEnd))
inputEnd--; inputEnd--;
values.append(String(inputStart, inputEnd - inputStart + 1)); values.append(String(inputStart, inputEnd - inputStart + 1));
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#ifndef SVGParserUtilities_h #ifndef SVGParserUtilities_h
#define SVGParserUtilities_h #define SVGParserUtilities_h
#include "core/html/parser/HTMLParserIdioms.h"
#include "core/svg/SVGTransform.h" #include "core/svg/SVGTransform.h"
#include "platform/text/ParserUtilities.h" #include "platform/text/ParserUtilities.h"
#include "wtf/HashSet.h" #include "wtf/HashSet.h"
...@@ -35,11 +36,19 @@ class FloatPoint; ...@@ -35,11 +36,19 @@ class FloatPoint;
class FloatRect; class FloatRect;
class SVGPointList; class SVGPointList;
enum WhitespaceMode {
DisallowWhitespace = 0,
AllowLeadingWhitespace = 0x1,
AllowTrailingWhitespace = 0x2,
AllowLeadingAndTrailingWhitespace = AllowLeadingWhitespace | AllowTrailingWhitespace
};
template <typename CharType> template <typename CharType>
bool parseSVGNumber(CharType* ptr, size_t length, double& number); bool parseSVGNumber(CharType* ptr, size_t length, double& number);
bool parseNumber(const LChar*& ptr, const LChar* end, float& number, bool skip = true); bool parseNumber(const LChar*& ptr, const LChar* end, float& number, WhitespaceMode = AllowLeadingAndTrailingWhitespace);
bool parseNumber(const UChar*& ptr, const UChar* end, float& number, bool skip = true); bool parseNumber(const UChar*& ptr, const UChar* end, float& number, WhitespaceMode = AllowLeadingAndTrailingWhitespace);
bool parseNumberOptionalNumber(const String& s, float& h, float& v); bool parseNumberOptionalNumber(const String& s, float& h, float& v);
bool parseNumberOrPercentage(const String& s, float& number);
bool parseArcFlag(const LChar*& ptr, const LChar* end, bool& flag); bool parseArcFlag(const LChar*& ptr, const LChar* end, bool& flag);
bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag); bool parseArcFlag(const UChar*& ptr, const UChar* end, bool& flag);
...@@ -50,18 +59,10 @@ bool parseFloatPoint2(const CharType*& current, const CharType* end, FloatPoint& ...@@ -50,18 +59,10 @@ bool parseFloatPoint2(const CharType*& current, const CharType* end, FloatPoint&
template <typename CharType> template <typename CharType>
bool parseFloatPoint3(const CharType*& current, const CharType* end, FloatPoint&, FloatPoint&, FloatPoint&); bool parseFloatPoint3(const CharType*& current, const CharType* end, FloatPoint&, FloatPoint&, FloatPoint&);
// SVG allows several different whitespace characters:
// http://www.w3.org/TR/SVG/paths.html#PathDataBNF
template <typename CharType>
inline bool isSVGSpace(CharType c)
{
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
}
template <typename CharType> template <typename CharType>
inline bool skipOptionalSVGSpaces(const CharType*& ptr, const CharType* end) inline bool skipOptionalSVGSpaces(const CharType*& ptr, const CharType* end)
{ {
while (ptr < end && isSVGSpace(*ptr)) while (ptr < end && isHTMLSpace<CharType>(*ptr))
ptr++; ptr++;
return ptr < end; return ptr < end;
} }
...@@ -69,7 +70,7 @@ inline bool skipOptionalSVGSpaces(const CharType*& ptr, const CharType* end) ...@@ -69,7 +70,7 @@ inline bool skipOptionalSVGSpaces(const CharType*& ptr, const CharType* end)
template <typename CharType> template <typename CharType>
inline bool skipOptionalSVGSpacesOrDelimiter(const CharType*& ptr, const CharType* end, char delimiter = ',') inline bool skipOptionalSVGSpacesOrDelimiter(const CharType*& ptr, const CharType* end, char delimiter = ',')
{ {
if (ptr < end && !isSVGSpace(*ptr) && *ptr != delimiter) if (ptr < end && !isHTMLSpace<CharType>(*ptr) && *ptr != delimiter)
return false; return false;
if (skipOptionalSVGSpaces(ptr, end)) { if (skipOptionalSVGSpaces(ptr, end)) {
if (ptr < end && *ptr == delimiter) { if (ptr < end && *ptr == delimiter) {
......
...@@ -74,7 +74,7 @@ void SVGPoint::parse(const CharType*& ptr, const CharType* end, ExceptionState& ...@@ -74,7 +74,7 @@ void SVGPoint::parse(const CharType*& ptr, const CharType* end, ExceptionState&
float x = 0.0f; float x = 0.0f;
float y = 0.0f; float y = 0.0f;
bool valid = parseNumber(ptr, end, x) && parseNumber(ptr, end, y, false); bool valid = parseNumber(ptr, end, x) && parseNumber(ptr, end, y, DisallowWhitespace);
if (!valid) { if (!valid) {
exceptionState.throwDOMException(SyntaxError, "Problem parsing point \"" + String(start, end - start) + "\""); exceptionState.throwDOMException(SyntaxError, "Problem parsing point \"" + String(start, end - start) + "\"");
......
...@@ -89,7 +89,7 @@ bool SVGPointList::parse(const CharType*& ptr, const CharType* end) ...@@ -89,7 +89,7 @@ bool SVGPointList::parse(const CharType*& ptr, const CharType* end)
for (;;) { for (;;) {
float x = 0.0f; float x = 0.0f;
float y = 0.0f; float y = 0.0f;
bool valid = parseNumber(ptr, end, x) && parseNumber(ptr, end, y, false); bool valid = parseNumber(ptr, end, x) && parseNumber(ptr, end, y, DisallowWhitespace);
if (!valid) { if (!valid) {
return false; return false;
} }
......
...@@ -74,7 +74,7 @@ void SVGRect::parse(const CharType*& ptr, const CharType* end, ExceptionState& e ...@@ -74,7 +74,7 @@ void SVGRect::parse(const CharType*& ptr, const CharType* end, ExceptionState& e
float y = 0.0f; float y = 0.0f;
float width = 0.0f; float width = 0.0f;
float height = 0.0f; float height = 0.0f;
bool valid = parseNumber(ptr, end, x) && parseNumber(ptr, end, y) && parseNumber(ptr, end, width) && parseNumber(ptr, end, height, false); bool valid = parseNumber(ptr, end, x) && parseNumber(ptr, end, y) && parseNumber(ptr, end, width) && parseNumber(ptr, end, height, DisallowWhitespace);
if (!valid) { if (!valid) {
exceptionState.throwDOMException(SyntaxError, "Problem parsing rect \"" + String(start, end - start) + "\""); exceptionState.throwDOMException(SyntaxError, "Problem parsing rect \"" + String(start, end - start) + "\"");
......
...@@ -92,7 +92,7 @@ void SVGStringList::parseInternal(const CharType*& ptr, const CharType* end) ...@@ -92,7 +92,7 @@ void SVGStringList::parseInternal(const CharType*& ptr, const CharType* end)
while (ptr < end) { while (ptr < end) {
const CharType* start = ptr; const CharType* start = ptr;
while (ptr < end && *ptr != delimiter && !isSVGSpace(*ptr)) while (ptr < end && *ptr != delimiter && !isHTMLSpace<CharType>(*ptr))
ptr++; ptr++;
if (ptr == start) if (ptr == start)
break; break;
......
...@@ -93,7 +93,7 @@ int parseTransformParamList(const CharType*& ptr, const CharType* end, float* va ...@@ -93,7 +93,7 @@ int parseTransformParamList(const CharType*& ptr, const CharType* end, float* va
skipOptionalSVGSpaces(ptr, end); skipOptionalSVGSpaces(ptr, end);
while (parsedParams < maxPossibleParams) { while (parsedParams < maxPossibleParams) {
if (!parseNumber(ptr, end, values[parsedParams], false)) if (!parseNumber(ptr, end, values[parsedParams], DisallowWhitespace))
break; break;
++parsedParams; ++parsedParams;
......
...@@ -149,7 +149,7 @@ bool SVGViewSpec::parseViewSpecInternal(const CharType* ptr, const CharType* end ...@@ -149,7 +149,7 @@ bool SVGViewSpec::parseViewSpecInternal(const CharType* ptr, const CharType* end
float y = 0.0f; float y = 0.0f;
float width = 0.0f; float width = 0.0f;
float height = 0.0f; float height = 0.0f;
if (!(parseNumber(ptr, end, x) && parseNumber(ptr, end, y) && parseNumber(ptr, end, width) && parseNumber(ptr, end, height, false))) if (!(parseNumber(ptr, end, x) && parseNumber(ptr, end, y) && parseNumber(ptr, end, width) && parseNumber(ptr, end, height, DisallowWhitespace)))
return false; return false;
updateViewBox(FloatRect(x, y, width, height)); updateViewBox(FloatRect(x, y, width, height));
if (ptr >= end || *ptr != ')') if (ptr >= end || *ptr != ')')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment