WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
52542
SVG CSS property types with <number> don't support exponents
https://bugs.webkit.org/show_bug.cgi?id=52542
Summary
SVG CSS property types with <number> don't support exponents
Dirk Schulze
Reported
2011-01-16 12:39:03 PST
In SVG the type <length> can have exponents: integer ([e|E] interger)? see
http://www.w3.org/TR/SVG/types.html#DataTypeNumber
We support this for SVG attributes, but not for SVG CSS properties like 'stroke-width', because we use the CSS parser and that parser doesn't support this. See attached link for an example.
Attachments
Patch
(20.91 KB, patch)
2012-03-31 09:11 PDT
,
Dirk Schulze
no flags
Details
Formatted Diff
Diff
Patch
(20.94 KB, patch)
2012-03-31 09:18 PDT
,
Dirk Schulze
no flags
Details
Formatted Diff
Diff
Patch
(26.56 KB, patch)
2012-03-31 20:25 PDT
,
Dirk Schulze
no flags
Details
Formatted Diff
Diff
Patch
(22.93 KB, patch)
2012-04-01 08:33 PDT
,
Dirk Schulze
no flags
Details
Formatted Diff
Diff
Patch
(14.14 KB, patch)
2012-07-11 10:07 PDT
,
Dirk Schulze
zimmermann
: review+
Details
Formatted Diff
Diff
Show Obsolete
(4)
View All
Add attachment
proposed patch, testcase, etc.
Dirk Schulze
Comment 1
2011-01-16 12:39:33 PST
Rob do you want to take a look at this as well? ;-)
Rob Buis
Comment 2
2011-01-16 23:08:11 PST
Hi Dirk, (In reply to
comment #1
)
> Rob do you want to take a look at this as well? ;-)
Good find! This is very interesting to work on, you can assign to me if you are busy with the animation stuff, I can look into it this week. Cheers, Rob.
Dirk Schulze
Comment 3
2011-01-17 00:21:00 PST
(In reply to
comment #2
)
> Hi Dirk, > > (In reply to
comment #1
) > > Rob do you want to take a look at this as well? ;-) > > Good find! This is very interesting to work on, you can assign to me if you are busy with the animation stuff, I can look into it this week.
Sure, I'm happy if you're working on it :-)
Dirk Schulze
Comment 4
2011-01-21 01:14:22 PST
Have to extend this bug. It's not only <length> that can take exponents, all CSS properties with <number> can take exponents: <angle>, <length>, <frequency>, <icccolor>, <percentage>, <time>. Some of them are defined by CSS3 as well, but without of exponents. Means we have to differ <number> for CSS/HTML and for CSS/SVG during parsing numbers. CSS3:
http://www.w3.org/TR/css3-values/#ltnumbergt
SVG:
http://www.w3.org/TR/SVG/types.html#DataTypeNumber
Dirk Schulze
Comment 5
2011-01-21 03:19:42 PST
(In reply to
comment #4
)
> CSS3:
http://www.w3.org/TR/css3-values/#ltnumbergt
> SVG:
http://www.w3.org/TR/SVG/types.html#DataTypeNumber
Ouch! Only if the value gets applied to the SVG _attribute_ it must allow exponent numbers, but it should _not_ on setting the property with CSS! <rect width="100" height="100" stroke-width="1e1px"> is allowed <rect width="100" height="100" style="stroke-width:1e1px;"> is not allowed.
Dirk Schulze
Comment 6
2012-03-30 20:13:33 PDT
Will upload a fix for that soon. Have it running in a local build. Just needs more testing.
Dirk Schulze
Comment 7
2012-03-31 09:11:30 PDT
Created
attachment 134958
[details]
Patch
Dirk Schulze
Comment 8
2012-03-31 09:18:54 PDT
Created
attachment 134960
[details]
Patch Forgot to add original author of the patch to the changelog :P.
Nikolas Zimmermann
Comment 9
2012-03-31 11:03:11 PDT
Comment on
attachment 134960
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=134960&action=review
Nice job, still some caveats, r- for test coverage:
> Source/WebCore/css/CSSParser.cpp:8603 > + int offset = 1;
why int? I'd use unsigned.
> Source/WebCore/css/CSSParser.cpp:8605 > + if (m_currentCharacter[offset] == '+') {
Is this safe to access? Is it guaranteed that m_currentCharacter isn't accessed out of boundaries, aka. that 'e' is always followed by another character.
> Source/WebCore/css/CSSParser.cpp:8606 > + offset++;
++offset.
> Source/WebCore/css/CSSParser.cpp:8609 > + offset++;
Ditto.
> Source/WebCore/css/CSSParser.cpp:8612 > + double exponent;
No need for this local here, just declare it in the if (offset > digits) branch directly,
> Source/WebCore/css/CSSParser.cpp:8616 > + exponent = charactersToDouble(m_currentCharacter + digits, offset - digits);
Can this yield "invalid" doubles, is this clamped somehow? Shall we sanitize the parsed exponent here? (thinking of NaN handling, or Inf)
> LayoutTests/svg/css/script-tests/scientific-numbers.js:13 > +shouldBeEqualToString("document.defaultView.getComputedStyle(text, null).fontSize", "16px");
You can just use "getComputedStyle(text).fontSize".
> LayoutTests/svg/css/script-tests/scientific-numbers.js:232 > +
This needs to be extended; leading whitespace, trailing whitespace, mid white-space (" 5e10", " 5e1 0", etc. (also including negative values, for both number and exponent). Arbitary characters inside of exponent, exponents leading to double overflow, etc.
Dirk Schulze
Comment 10
2012-03-31 20:25:49 PDT
Created
attachment 134986
[details]
Patch New approach: Use SVG parser to parse numbers. The logic there is well tested and has a lot of more checks to make sure that we don't miss a case. Added more tests for some edge cases as well as overflow tests. isASCIIDigit() and equal checks prevent us from reading data outside the buffer.
Nikolas Zimmermann
Comment 11
2012-04-01 04:43:24 PDT
(In reply to
comment #10
)
> Created an attachment (id=134986) [details] > Patch > > New approach: Use SVG parser to parse numbers. The logic there is well tested and has a lot of more checks to make sure that we don't miss a case. Added more tests for some edge cases as well as overflow tests.
Great, I'll look at thew new patch soon.
> isASCIIDigit() and equal checks prevent us from reading data outside the buffer.
This still worries me. I'll check if the new patch is safer.
Nikolas Zimmermann
Comment 12
2012-04-01 04:53:11 PDT
Comment on
attachment 134986
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=134986&action=review
I have some new comments:
> Source/WebCore/css/CSSParser.cpp:79 > +#if ENABLE(SVG) > +#include "SVGParserUtilities.h" > +#endif
I don't think you need to guard this.
> Source/WebCore/css/CSSParser.cpp:8606 > + if (isASCIIAlphaCaselessEqual(*m_currentCharacter, 'e')) {
At this point it's guaranteed that m_currentCharacter exists okay. So dereferencing the UChar* pointer is fine.
> Source/WebCore/css/CSSParser.cpp:8607 > + if (m_currentCharacter[1] == '-' || m_currentCharacter[1] == '+' || isASCIIDigit(m_currentCharacter[1])) {
But who guarantees that the UChar* m_currentCharacter + 1 that you're dereferencing here exists? The old code passes m_tokenStart to charactersToDouble as const UChar* parameter, and m_currentCharacter - m_tokenStart as size. I'd strongly suggest to reuse that way of accessing the string. const UChar* currentCharacter = m_tokenStart; unsigned length = m_currentCharacter - m_tokenStart; if (length > 1 && isASCIIAlphaCaselessEqual(*currentCharacter, 'e')) { // Now its guaranteed that the passed in string is at least 'eX' (e + another item) } If I'm missing something, and this is already guaranteed by the CSSParser code, you should at least assert that the length is > 1.
> Source/WebCore/css/CSSParser.cpp:8612 > + while (isASCIIDigit(m_currentCharacter[offset]))
If the branch above isn't taken that means the first character following the 'e' is not '+', not '-' nor an ascii digit - why do you continue parsing at this point? You could have exited already.
> Source/WebCore/css/CSSParser.cpp:8615 > + if (!parseSVGNumber(m_tokenStart, m_currentCharacter + offset - m_tokenStart, yylval->number))
This would then read as if (!parseSVGNumber(m_tokenStart, length + offset, yylval->number).
> Source/WebCore/css/CSSParser.cpp:8616 > + break;
If parseSVGNumber() fails, is yylval->number reset to a specific value? (Not sure about that, that's why I'm asking).
> Source/WebCore/svg/SVGParserUtilities.cpp:152 > +
One newline too much.
> LayoutTests/svg/css/script-tests/scientific-numbers.js:13 > +shouldBeEqualToString("document.defaultView.getComputedStyle(text).fontSize", "16px");
You still use the long form here, getComputedStyle(text).fontSize should work, without the document.defaultView prefix :-)
Dirk Schulze
Comment 13
2012-04-01 08:20:37 PDT
(In reply to
comment #12
)
> (From update of
attachment 134986
[details]
) > View in context:
https://bugs.webkit.org/attachment.cgi?id=134986&action=review
> > I have some new comments: > > > Source/WebCore/css/CSSParser.cpp:79 > > +#if ENABLE(SVG) > > +#include "SVGParserUtilities.h" > > +#endif > > I don't think you need to guard this.
It is not necessary but prevent including the file on compiling. That was the intention.
> > > Source/WebCore/css/CSSParser.cpp:8606 > > + if (isASCIIAlphaCaselessEqual(*m_currentCharacter, 'e')) { > > At this point it's guaranteed that m_currentCharacter exists okay. So dereferencing the UChar* pointer is fine. > > > Source/WebCore/css/CSSParser.cpp:8607 > > + if (m_currentCharacter[1] == '-' || m_currentCharacter[1] == '+' || isASCIIDigit(m_currentCharacter[1])) { > > But who guarantees that the UChar* m_currentCharacter + 1 that you're dereferencing here exists?
That is the code before these lines: while (true) { if (!isASCIIDigit(m_currentCharacter[0])) { // Only one dot is allowed for a number, // and it must be followed by a digit. if (m_currentCharacter[0] != '.' || dotSeen || !isASCIIDigit(m_currentCharacter[1])) break; dotSeen = true; } ++m_currentCharacter; } So no extra check. The string gets terminated by a \n. Just checked it. Therefore is should be save to check the value for 1, since 0 was no /n.
> > The old code passes m_tokenStart to charactersToDouble as const UChar* parameter, and m_currentCharacter - m_tokenStart as size. > I'd strongly suggest to reuse that way of accessing the string. > const UChar* currentCharacter = m_tokenStart; > unsigned length = m_currentCharacter - m_tokenStart; > if (length > 1 && isASCIIAlphaCaselessEqual(*currentCharacter, 'e')) { > // Now its guaranteed that the passed in string is at least 'eX' (e + another item) > }
But just for the first char after the 'e'. Like I said, this is not necessary, since the last char is a \n on the string.
> > If I'm missing something, and this is already guaranteed by the CSSParser code, you should at least assert that the length is > 1.
Not necessary.
> > > Source/WebCore/css/CSSParser.cpp:8612 > > + while (isASCIIDigit(m_currentCharacter[offset])) > > If the branch above isn't taken that means the first character following the 'e' is not '+', not '-' nor an ascii digit - why do you continue parsing at this point? You could have exited already.
Well, not exiting. If I break I would break stuff. The unit is included in the token (10em wouldn't work, which gets tested by the new test as well). I could put the while loop in the else tree.
> > > Source/WebCore/css/CSSParser.cpp:8615 > > + if (!parseSVGNumber(m_tokenStart, m_currentCharacter + offset - m_tokenStart, yylval->number)) > > This would then read as if (!parseSVGNumber(m_tokenStart, length + offset, yylval->number). > > > Source/WebCore/css/CSSParser.cpp:8616 > > + break; > > If parseSVGNumber() fails, is yylval->number reset to a specific value? (Not sure about that, that's why I'm asking).
m_token get's not set and therefore the value is invalid.
> > > Source/WebCore/svg/SVGParserUtilities.cpp:152 > > + > > One newline too much.
Thanks. Will fix that.
> > > LayoutTests/svg/css/script-tests/scientific-numbers.js:13 > > +shouldBeEqualToString("document.defaultView.getComputedStyle(text).fontSize", "16px"); > > You still use the long form here, getComputedStyle(text).fontSize should work, without the document.defaultView prefix :-)
That was a misunderstanding. I thought you meant the ", null)" on computed style which I fixed. Will fix that.
Dirk Schulze
Comment 14
2012-04-01 08:33:56 PDT
Created
attachment 135000
[details]
Patch Patch. See previous comment.
Nikolas Zimmermann
Comment 15
2012-04-25 09:57:49 PDT
Comment on
attachment 135000
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=135000&action=review
Next round of comments, poor Dirk :-)
> Source/WebCore/css/CSSParser.cpp:8604 > + if (isASCIIAlphaCaselessEqual(*m_currentCharacter, 'e')) {
Are we sure that at least one element follows the 'e' ? Can we ASSERT(m_currentCharacter - m_tokenStart > 1) here? Before accessing: if (m_currentCharacter[1].. below
> Source/WebCore/css/CSSParser.cpp:8612 > + if (m_currentCharacter[1] == '-' || m_currentCharacter[1] == '+' || isASCIIDigit(m_currentCharacter[1])) { > + offset += 2; > + while (isASCIIDigit(m_currentCharacter[offset])) > + ++offset; > + // Use FLOATTOKEN if we have exponents. > + dotSeen = true; > + } > + }
++m_currentCharacter; if (*m_currentCharacter == '-' || *m_currentCharacter == '+' || isASCIIDigit(*m_currentCharacter)) { ++m_currentCharacter; while (isASCIIDigit(*m_currentCharacter)) ++m_currentCharacter; // Use FLOATTOKEN if the string contains exponents. dotSeen = true; } if (!parseNumber(m_tokenStart, m_currentCharacter, yylval->number, false)) break; You'll need a new parseNumber() variant taking a double&, but you can remove the new parseSVGNumber() method that you've added.
> Source/WebCore/svg/SVGParserUtilities.h:36 > +bool parseSVGNumber(UChar*& ptr, size_t length, double& number);
Can be avoided.
> LayoutTests/svg/css/script-tests/scientific-numbers.js:274 > +text.setAttribute("font-size", "50e0.0"); > +shouldBeEqualToString("getComputedStyle(text).fontSize", "16px");
Hard to understand the test, without seeing what you're trying to assign. I propose following: function setFontSize(valueString) { text.setAttribute("font-size", valueString); } function test(valueString, expectedString) { debug("text.setAttribute('font-size', '" + valueString + "');"); setFontSize(valueString); shouldBeEqualToString("getComputedStyle(text).fontSize", expectedString); } and use: debug("Valid values"); test("50px", "50px"); test("100px", "100px"); setFontSize("16px"); debug(""); debug("Invalid values"); test("50e0.0", "16px"); test("50e 0", "16px"); .... setFontSize("16px"); debug(""); debug("Next category...");
Dirk Schulze
Comment 16
2012-07-11 01:26:40 PDT
(In reply to
comment #15
) Sorry. I am looking at this after a long time.
> if (!parseNumber(m_tokenStart, m_currentCharacter, yylval->number, false)) > break; >
IIRC the problem was that m_tokenStart and m_currentCharacter are of type UChar*, while parseNumber takes const UChar* (and const UChar*&). So I would need a type conversion which does not look pretty.
> You'll need a new parseNumber() variant taking a double&, but you can remove the new parseSVGNumber() method that you've added.
How could I avoid it? Even for parseNumber with a double I would need a new method. I choose parseSVGNumber to indicate from the name that it will be a number that is parsed according to the SVG rules. I still think that this is the proper solution. Especially here where it is not obvious that we call the method in SVGParserUtilities.
> Hard to understand the test, without seeing what you're trying to assign.
Thanks, I'll change the test and the other code suggestions.
Dirk Schulze
Comment 17
2012-07-11 10:07:17 PDT
Created
attachment 151721
[details]
Patch
Nikolas Zimmermann
Comment 18
2012-07-18 03:23:38 PDT
Comment on
attachment 151721
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=151721&action=review
Looks reasonable, r=me.
> Source/WebCore/ChangeLog:10 > + The SVG parser is already well tested and has some extra checks for edge cases that prevent > + us e.g for overflows.
prevent from overflow, or rather protect from overflow
Dirk Schulze
Comment 19
2012-07-18 08:43:02 PDT
Committed
r122978
: <
http://trac.webkit.org/changeset/122978
>
Tony Chang
Comment 20
2012-07-18 10:31:09 PDT
Did you mean for this to be a pixel test? It looks like including SVGTestCase.js sets "window.enablePixelTesting = true" which causes pixel results to be dumped.
Dirk Schulze
Comment 21
2012-07-18 10:40:06 PDT
(In reply to
comment #20
)
> Did you mean for this to be a pixel test? It looks like including SVGTestCase.js sets "window.enablePixelTesting = true" which causes pixel results to be dumped.
No, should be dumpAsText. thanks
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug