Commit 1d6711a6 authored by fs's avatar fs Committed by Commit bot

Implement specced parsing algorithm for <area coords>

This implements the parsing algorithm for "list of floating point
numbers" and uses it to parse the 'coords' attribute on <area>.
The fractional part of numbers are no longer discarded.
The old parsing code in platform/Length.cpp is no longer used and is
removed.

BUG=578114

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

Cr-Commit-Position: refs/heads/master@{#371940}
parent 7a8e19c9
This is a testharness.js-based test.
PASS COMMA: "2,2,10,10" (rect)
PASS SEMICOLON: "2;2;10;10" (rect)
PASS SPACE: "2 2 10 10" (rect)
PASS TAB: "2\t2\t10\t10" (rect)
PASS FORM FEED: "2\f2\f10\f10" (rect)
PASS LINE FEED: "2\n2\n10\n10" (rect)
PASS CARRIGAGE RETURN: "2\r2\r10\r10" (rect)
FAIL LINE TABULATION: "2\v2\v10\v10" (rect) assert_equals: elementFromPoint(3, 3) expected Element node <img src="resources/images/blue-border.png" usemap="#x" i... but got Element node <area id="area" shape="rect" coords="2 2 10 10"></area>
FAIL LINE NEXT: "2…2…10…10" (rect) assert_equals: elementFromPoint(3, 3) expected Element node <img src="resources/images/blue-border.png" usemap="#x" i... but got Element node <area id="area" shape="rect" coords="2…2…10…10"></area>
FAIL EN QUAD: "2 2 10 10" (rect) assert_equals: elementFromPoint(3, 3) expected Element node <img src="resources/images/blue-border.png" usemap="#x" i... but got Element node <area id="area" shape="rect" coords="2 2 10 10"></area>
FAIL abc between numbers: "2a2b20c20,2,10,10" (rect) assert_equals: elementFromPoint(11, 9) expected Element node <img src="resources/images/blue-border.png" usemap="#x" i... but got Element node <area id="area" shape="rect" coords="2a2b20c20,2,10,10"><...
FAIL COLON between numbers: "2:2:20:20,2,10,10" (rect) assert_equals: elementFromPoint(11, 9) expected Element node <img src="resources/images/blue-border.png" usemap="#x" i... but got Element node <area id="area" shape="rect" coords="2:2:20:20,2,10,10"><...
FAIL U+0000 between numbers: "2\02\020\020,2,10,10" (rect) assert_equals: elementFromPoint(11, 9) expected Element node <img src="resources/images/blue-border.png" usemap="#x" i... but got Element node <area id="area" shape="rect" coords="2\02\020\020,2,10,10"><...
PASS leading COMMA: ",2,2,10,10" (rect)
PASS leading SPACE: " 2,2,10,10" (rect)
PASS leading SEMICOLON: ";2,2,10,10" (rect)
PASS trailing COMMA: "2,2,10," (rect)
PASS trailing SPACE: "2,2,10 " (rect)
PASS trailing SEMICOLON: "2,2,10;" (rect)
PASS PERCENT: "2%,2%,10%,10%" (rect)
PASS CSS units: "2in,2in,10cm,10cm" (rect)
FAIL float: "1.4,1.4,10,10" (rect) assert_equals: elementFromPoint(1, 3) expected Element node <img src="resources/images/blue-border.png" usemap="#x" i... but got Element node <area id="area" shape="rect" coords="1.4,1.4,10,10"></area>
FAIL number starting with PERIOD: ".4,.4,10,10" (rect) assert_equals: elementFromPoint(0, 0) expected Element node <img src="resources/images/blue-border.png" usemap="#x" i... but got Element node <area id="area" shape="rect" coords=".4,.4,10,10"></area>
FAIL sci-not: "2,2,1e1,1e1" (rect) assert_equals: elementFromPoint(3, 3) expected Element node <area id="area" shape="rect" coords="2,2,1e1,1e1"></area> but got Element node <img src="resources/images/blue-border.png" usemap="#x" i...
PASS leading/trailing garbage: "='2,2,10,10' " (rect)
PASS non-ascii garbage: "“2,2,10,10\"" (rect)
FAIL URL garbage with number: "2,2,10ls/spain/holidays/regions/10/Canary+Islands/Canary+Islands.html" (rect) assert_equals: elementFromPoint(3, 3) expected Element node <img src="resources/images/blue-border.png" usemap="#x" i... but got Element node <area id="area" shape="rect" coords="2,2,10ls/spain/holid...
PASS consecutive COMMAs: "2,,10,10" (rect)
PASS consecutive SPACEs: "2 10,10" (rect)
PASS consecutive SEMICOLONs: "2;;10,10" (rect)
PASS several consecutive separators: ",,2;,;2,;,10 \t\r\n10;;" (rect)
PASS one too many numbers, trailing COMMA: "100,100,120,100,100,120,300," (poly)
Harness: the test ran to completion.
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "core/dom/ElementTraversal.h" #include "core/dom/ElementTraversal.h"
#include "core/html/HTMLImageElement.h" #include "core/html/HTMLImageElement.h"
#include "core/html/HTMLMapElement.h" #include "core/html/HTMLMapElement.h"
#include "core/html/parser/HTMLParserIdioms.h"
#include "core/layout/HitTestResult.h" #include "core/layout/HitTestResult.h"
#include "core/layout/LayoutImage.h" #include "core/layout/LayoutImage.h"
#include "core/layout/LayoutView.h" #include "core/layout/LayoutView.h"
...@@ -35,11 +36,10 @@ namespace blink { ...@@ -35,11 +36,10 @@ namespace blink {
namespace { namespace {
// Adapt a Length to the allowed range of a LayoutUnit. // Adapt a double to the allowed range of a LayoutUnit and narrow it to float precision.
float clampCoordinate(const Length& length) float clampCoordinate(double value)
{ {
ASSERT(length.isFixed()); return LayoutUnit(value).toFloat();
return LayoutUnit(length.value()).toFloat();
} }
} }
...@@ -80,7 +80,7 @@ void HTMLAreaElement::parseAttribute(const QualifiedName& name, const AtomicStri ...@@ -80,7 +80,7 @@ void HTMLAreaElement::parseAttribute(const QualifiedName& name, const AtomicStri
} }
invalidateCachedRegion(); invalidateCachedRegion();
} else if (name == coordsAttr) { } else if (name == coordsAttr) {
m_coords = parseHTMLAreaElementCoords(value.string()); m_coords = parseHTMLListOfFloatingPointNumbers(value.string());
invalidateCachedRegion(); invalidateCachedRegion();
} else if (name == altAttr || name == accesskeyAttr) { } else if (name == altAttr || name == accesskeyAttr) {
// Do nothing. // Do nothing.
...@@ -152,7 +152,7 @@ Path HTMLAreaElement::getRegion(const LayoutSize& size) const ...@@ -152,7 +152,7 @@ Path HTMLAreaElement::getRegion(const LayoutSize& size) const
} }
break; break;
case Circle: case Circle:
if (m_coords.size() >= 3 && m_coords[2].value() > 0) { if (m_coords.size() >= 3 && m_coords[2] > 0) {
float r = clampCoordinate(m_coords[2]); float r = clampCoordinate(m_coords[2]);
path.addEllipse(FloatRect(clampCoordinate(m_coords[0]) - r, clampCoordinate(m_coords[1]) - r, 2 * r, 2 * r)); path.addEllipse(FloatRect(clampCoordinate(m_coords[0]) - r, clampCoordinate(m_coords[1]) - r, 2 * r, 2 * r));
} }
......
...@@ -64,7 +64,7 @@ private: ...@@ -64,7 +64,7 @@ private:
void invalidateCachedRegion(); void invalidateCachedRegion();
OwnPtr<Path> m_region; OwnPtr<Path> m_region;
Vector<Length> m_coords; Vector<double> m_coords;
LayoutSize m_lastSize; LayoutSize m_lastSize;
Shape m_shape; Shape m_shape;
}; };
......
...@@ -25,13 +25,15 @@ ...@@ -25,13 +25,15 @@
#include "core/html/parser/HTMLParserIdioms.h" #include "core/html/parser/HTMLParserIdioms.h"
#include "core/HTMLNames.h" #include "core/HTMLNames.h"
#include <limits> #include "platform/ParsingUtilities.h"
#include "wtf/MathExtras.h" #include "wtf/MathExtras.h"
#include "wtf/text/AtomicString.h" #include "wtf/text/AtomicString.h"
#include "wtf/text/StringBuilder.h" #include "wtf/text/StringBuilder.h"
#include "wtf/text/StringHash.h" #include "wtf/text/StringHash.h"
#include "wtf/text/TextEncoding.h" #include "wtf/text/TextEncoding.h"
#include <limits>
namespace blink { namespace blink {
using namespace HTMLNames; using namespace HTMLNames;
...@@ -113,18 +115,8 @@ Decimal parseToDecimalForNumberType(const String& string, const Decimal& fallbac ...@@ -113,18 +115,8 @@ Decimal parseToDecimalForNumberType(const String& string, const Decimal& fallbac
return value.isZero() ? Decimal(0) : value; return value.isZero() ? Decimal(0) : value;
} }
double parseToDoubleForNumberType(const String& string, double fallbackValue) static double checkDoubleValue(double value, bool valid, double fallbackValue)
{ {
// http://www.whatwg.org/specs/web-apps/current-work/#floating-point-numbers
// String::toDouble() accepts leading + and whitespace characters, which are not valid here.
UChar firstCharacter = string[0];
if (firstCharacter != '-' && firstCharacter != '.' && !isASCIIDigit(firstCharacter))
return fallbackValue;
if (string.endsWith('.'))
return fallbackValue;
bool valid = false;
double value = string.toDouble(&valid);
if (!valid) if (!valid)
return fallbackValue; return fallbackValue;
...@@ -140,6 +132,21 @@ double parseToDoubleForNumberType(const String& string, double fallbackValue) ...@@ -140,6 +132,21 @@ double parseToDoubleForNumberType(const String& string, double fallbackValue)
return value ? value : 0; return value ? value : 0;
} }
double parseToDoubleForNumberType(const String& string, double fallbackValue)
{
// http://www.whatwg.org/specs/web-apps/current-work/#floating-point-numbers
// String::toDouble() accepts leading + and whitespace characters, which are not valid here.
UChar firstCharacter = string[0];
if (firstCharacter != '-' && firstCharacter != '.' && !isASCIIDigit(firstCharacter))
return fallbackValue;
if (string.endsWith('.'))
return fallbackValue;
bool valid = false;
double value = string.toDouble(&valid);
return checkDoubleValue(value, valid, fallbackValue);
}
template <typename CharacterType> template <typename CharacterType>
static bool parseHTMLIntegerInternal(const CharacterType* position, const CharacterType* end, int& value) static bool parseHTMLIntegerInternal(const CharacterType* position, const CharacterType* end, int& value)
{ {
...@@ -265,6 +272,48 @@ bool parseHTMLNonNegativeInteger(const String& input, unsigned& value) ...@@ -265,6 +272,48 @@ bool parseHTMLNonNegativeInteger(const String& input, unsigned& value)
return parseHTMLNonNegativeIntegerInternal(start, start + length, value); return parseHTMLNonNegativeIntegerInternal(start, start + length, value);
} }
template<typename CharacterType>
static bool isSpaceOrDelimiter(CharacterType c)
{
return isHTMLSpace(c) || c == ',' || c == ';';
}
template<typename CharacterType>
static bool isNotSpaceDelimiterOrNumberStart(CharacterType c)
{
return !(isSpaceOrDelimiter(c) || isASCIIDigit(c) || c == '.' || c == '-');
}
template<typename CharacterType>
static Vector<double> parseHTMLListOfFloatingPointNumbersInternal(
const CharacterType* position, const CharacterType* end)
{
Vector<double> numbers;
skipWhile<CharacterType, isSpaceOrDelimiter>(position, end);
while (position < end) {
skipWhile<CharacterType, isNotSpaceDelimiterOrNumberStart>(position, end);
const CharacterType* unparsedNumberStart = position;
skipUntil<CharacterType, isSpaceOrDelimiter>(position, end);
size_t parsedLength = 0;
double number = charactersToDouble(unparsedNumberStart, position - unparsedNumberStart, parsedLength);
numbers.append(checkDoubleValue(number, parsedLength != 0, 0));
skipWhile<CharacterType, isSpaceOrDelimiter>(position, end);
}
return numbers;
}
// https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-a-list-of-floating-point-numbers
Vector<double> parseHTMLListOfFloatingPointNumbers(const String& input)
{
if (input.is8Bit())
return parseHTMLListOfFloatingPointNumbersInternal(input.characters8(), input.characters8() + input.length());
return parseHTMLListOfFloatingPointNumbersInternal(input.characters16(), input.characters16() + input.length());
}
static const char charsetString[] = "charset"; static const char charsetString[] = "charset";
static const size_t charsetLength = sizeof("charset") - 1; static const size_t charsetLength = sizeof("charset") - 1;
......
...@@ -60,6 +60,9 @@ bool parseHTMLInteger(const String&, int&); ...@@ -60,6 +60,9 @@ bool parseHTMLInteger(const String&, int&);
// http://www.whatwg.org/specs/web-apps/current-work/#rules-for-parsing-non-negative-integers // http://www.whatwg.org/specs/web-apps/current-work/#rules-for-parsing-non-negative-integers
bool parseHTMLNonNegativeInteger(const String&, unsigned&); bool parseHTMLNonNegativeInteger(const String&, unsigned&);
// https://html.spec.whatwg.org/multipage/infrastructure.html#rules-for-parsing-a-list-of-floating-point-numbers
Vector<double> parseHTMLListOfFloatingPointNumbers(const String&);
typedef Vector<std::pair<String, String>> HTMLAttributeList; typedef Vector<std::pair<String, String>> HTMLAttributeList;
// The returned encoding might not be valid. // The returned encoding might not be valid.
WTF::TextEncoding encodingFromMetaAttributes(const HTMLAttributeList&); WTF::TextEncoding encodingFromMetaAttributes(const HTMLAttributeList&);
......
...@@ -26,85 +26,11 @@ ...@@ -26,85 +26,11 @@
#include "platform/CalculationValue.h" #include "platform/CalculationValue.h"
#include "platform/animation/AnimationUtilities.h" #include "platform/animation/AnimationUtilities.h"
#include "wtf/ASCIICType.h"
#include "wtf/text/StringBuffer.h"
#include "wtf/text/WTFString.h"
using namespace WTF; using namespace WTF;
namespace blink { namespace blink {
template<typename CharType>
static unsigned splitLength(const CharType* data, unsigned length, unsigned& intLength, unsigned& doubleLength)
{
ASSERT(length);
unsigned i = 0;
while (i < length && isSpaceOrNewline(data[i]))
++i;
if (i < length && (data[i] == '+' || data[i] == '-'))
++i;
while (i < length && isASCIIDigit(data[i]))
++i;
intLength = i;
while (i < length && (isASCIIDigit(data[i]) || data[i] == '.'))
++i;
doubleLength = i;
// IE quirk: Skip whitespace between the number and the % character (20 % => 20%).
while (i < length && isSpaceOrNewline(data[i]))
++i;
return i;
}
template<typename CharType>
static Length parseHTMLAreaCoordinate(const CharType* data, unsigned length)
{
unsigned intLength;
unsigned doubleLength;
splitLength(data, length, intLength, doubleLength);
return Length(charactersToIntStrict(data, intLength), Fixed);
}
// FIXME: Per HTML5, this should follow the "rules for parsing a list of integers".
Vector<Length> parseHTMLAreaElementCoords(const String& string)
{
unsigned length = string.length();
StringBuffer<LChar> spacified(length);
for (unsigned i = 0; i < length; i++) {
UChar cc = string[i];
if (cc > '9' || (cc < '0' && cc != '-' && cc != '.'))
spacified[i] = ' ';
else
spacified[i] = cc;
}
RefPtr<StringImpl> str = spacified.release();
str = str->simplifyWhiteSpace();
ASSERT(str->is8Bit());
if (!str->length())
return Vector<Length>();
unsigned len = str->count(' ') + 1;
Vector<Length> r(len);
unsigned i = 0;
unsigned pos = 0;
size_t pos2;
while ((pos2 = str->find(' ', pos)) != kNotFound) {
r[i++] = parseHTMLAreaCoordinate(str->characters8() + pos, pos2 - pos);
pos = pos2 + 1;
}
r[i] = parseHTMLAreaCoordinate(str->characters8() + pos, str->length() - pos);
ASSERT(i == len - 1);
return r;
}
class CalculationValueHandleMap { class CalculationValueHandleMap {
USING_FAST_MALLOC(CalculationValueHandleMap); USING_FAST_MALLOC(CalculationValueHandleMap);
WTF_MAKE_NONCOPYABLE(CalculationValueHandleMap); WTF_MAKE_NONCOPYABLE(CalculationValueHandleMap);
......
...@@ -322,8 +322,6 @@ private: ...@@ -322,8 +322,6 @@ private:
bool m_isFloat; bool m_isFloat;
}; };
PLATFORM_EXPORT Vector<Length> parseHTMLAreaElementCoords(const String&);
} // namespace blink } // namespace blink
#endif // Length_h #endif // Length_h
...@@ -1139,6 +1139,16 @@ double charactersToDouble(const UChar* data, size_t length, bool* ok) ...@@ -1139,6 +1139,16 @@ double charactersToDouble(const UChar* data, size_t length, bool* ok)
return toDoubleType<UChar, DisallowTrailingJunk>(data, length, ok, parsedLength); return toDoubleType<UChar, DisallowTrailingJunk>(data, length, ok, parsedLength);
} }
double charactersToDouble(const LChar* data, size_t length, size_t& parsedLength)
{
return toDoubleType<LChar, AllowTrailingJunk>(data, length, nullptr, parsedLength);
}
double charactersToDouble(const UChar* data, size_t length, size_t& parsedLength)
{
return toDoubleType<UChar, AllowTrailingJunk>(data, length, nullptr, parsedLength);
}
float charactersToFloat(const LChar* data, size_t length, bool* ok) float charactersToFloat(const LChar* data, size_t length, bool* ok)
{ {
// FIXME: This will return ok even when the string fits into a double but // FIXME: This will return ok even when the string fits into a double but
......
...@@ -70,6 +70,8 @@ WTF_EXPORT uint64_t charactersToUInt64(const UChar*, size_t, bool* ok = 0); // i ...@@ -70,6 +70,8 @@ WTF_EXPORT uint64_t charactersToUInt64(const UChar*, size_t, bool* ok = 0); // i
// consistent with the above functions instead. // consistent with the above functions instead.
WTF_EXPORT double charactersToDouble(const LChar*, size_t, bool* ok = 0); WTF_EXPORT double charactersToDouble(const LChar*, size_t, bool* ok = 0);
WTF_EXPORT double charactersToDouble(const UChar*, size_t, bool* ok = 0); WTF_EXPORT double charactersToDouble(const UChar*, size_t, bool* ok = 0);
WTF_EXPORT double charactersToDouble(const LChar*, size_t, size_t& parsedLength);
WTF_EXPORT double charactersToDouble(const UChar*, size_t, size_t& parsedLength);
WTF_EXPORT float charactersToFloat(const LChar*, size_t, bool* ok = 0); WTF_EXPORT float charactersToFloat(const LChar*, size_t, bool* ok = 0);
WTF_EXPORT float charactersToFloat(const UChar*, size_t, bool* ok = 0); WTF_EXPORT float charactersToFloat(const UChar*, size_t, bool* ok = 0);
WTF_EXPORT float charactersToFloat(const LChar*, size_t, size_t& parsedLength); WTF_EXPORT float charactersToFloat(const LChar*, size_t, size_t& parsedLength);
......
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