Bug 785140 - Correct SVG whitespace handling in various spots. r=smaug,dholbert

This commit is contained in:
Robert Longson 2013-09-17 13:52:39 +01:00
parent c75fdb1ad8
commit 4bc18dc4e1
20 changed files with 321 additions and 155 deletions

View File

@ -6096,7 +6096,7 @@ HTMLInputElement::IsValidEmailAddressList(const nsAString& aValue)
}
}
return !tokenizer.lastTokenEndedWithSeparator();
return !tokenizer.separatorAfterCurrentToken();
}
//static

View File

@ -432,7 +432,7 @@ DecoderTraits::CanHandleMediaType(const char* aMIMEType,
// Totally unsupported codec
return CANPLAY_NO;
}
expectMoreTokens = tokenizer.lastTokenEndedWithSeparator();
expectMoreTokens = tokenizer.separatorAfterCurrentToken();
}
if (expectMoreTokens) {
// Last codec name was empty

View File

@ -6,12 +6,12 @@
#include "mozilla/Util.h"
#include "SVGAnimatedPreserveAspectRatio.h"
#include "nsWhitespaceTokenizer.h"
#include "mozilla/dom/SVGAnimatedPreserveAspectRatioBinding.h"
#include "nsSMILValue.h"
#include "nsSVGAttrTearoffTable.h"
#include "nsWhitespaceTokenizer.h"
#include "SMILEnumType.h"
#include "nsAttrValueInlines.h"
#include "mozilla/dom/SVGAnimatedPreserveAspectRatioBinding.h"
#include "SVGContentUtils.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -135,12 +135,9 @@ static nsresult
ToPreserveAspectRatio(const nsAString &aString,
SVGPreserveAspectRatio *aValue)
{
if (aString.IsEmpty() || NS_IsAsciiWhitespace(aString[0])) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
nsWhitespaceTokenizer tokenizer(aString);
if (!tokenizer.hasMoreTokens()) {
nsWhitespaceTokenizerTemplate<IsSVGWhitespace> tokenizer(aString);
if (tokenizer.whitespaceBeforeFirstToken() ||
!tokenizer.hasMoreTokens()) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
const nsAString &token = tokenizer.nextToken();
@ -172,7 +169,7 @@ ToPreserveAspectRatio(const nsAString &aString,
val.SetMeetOrSlice(SVG_MEETORSLICE_MEET);
}
if (tokenizer.hasMoreTokens()) {
if (tokenizer.whitespaceAfterCurrentToken()) {
return NS_ERROR_DOM_SYNTAX_ERR;
}

View File

@ -67,7 +67,7 @@ SVGLengthList::SetValueFromString(const nsAString& aValue)
return NS_ERROR_OUT_OF_MEMORY;
}
}
if (tokenizer.lastTokenEndedWithSeparator()) {
if (tokenizer.separatorAfterCurrentToken()) {
return NS_ERROR_DOM_SYNTAX_ERR; // trailing comma
}
return CopyFrom(temp);

View File

@ -4,15 +4,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SVGMotionSMILAnimationFunction.h"
#include "nsSMILParserUtils.h"
#include "nsSVGAngle.h"
#include "SVGMotionSMILType.h"
#include "SVGMotionSMILPathUtils.h"
#include "nsSVGPathDataParser.h"
#include "mozilla/dom/SVGAnimationElement.h"
#include "mozilla/dom/SVGPathElement.h" // for nsSVGPathList
#include "mozilla/dom/SVGMPathElement.h"
#include "nsAttrValue.h"
#include "nsAttrValueInlines.h"
#include "nsSMILParserUtils.h"
#include "nsSVGAngle.h"
#include "nsSVGPathDataParser.h"
#include "SVGMotionSMILType.h"
#include "SVGMotionSMILPathUtils.h"
namespace mozilla {

View File

@ -106,7 +106,7 @@ SVGMotionSMILPathUtils::PathGenerator::
return false;
}
if (tokenizer.lastTokenEndedWithSeparator() || // Trailing comma.
if (tokenizer.separatorAfterCurrentToken() || // Trailing comma.
tokenizer.hasMoreTokens()) { // More text remains
return false;
}

View File

@ -72,7 +72,7 @@ SVGNumberList::SetValueFromString(const nsAString& aValue)
return NS_ERROR_OUT_OF_MEMORY;
}
}
if (tokenizer.lastTokenEndedWithSeparator()) {
if (tokenizer.separatorAfterCurrentToken()) {
return NS_ERROR_DOM_SYNTAX_ERR; // trailing comma
}
return CopyFrom(temp);

View File

@ -110,7 +110,7 @@ SVGPointList::SetValueFromString(const nsAString& aValue)
temp.AppendItem(SVGPoint(x, y));
}
if (tokenizer.lastTokenEndedWithSeparator()) {
if (tokenizer.separatorAfterCurrentToken()) {
rv = NS_ERROR_DOM_SYNTAX_ERR; // trailing comma
}
nsresult rv2 = CopyFrom(temp);

View File

@ -54,11 +54,11 @@ SVGStringList::SetValue(const nsAString& aValue)
return NS_ERROR_OUT_OF_MEMORY;
}
}
if (tokenizer.lastTokenEndedWithSeparator()) {
if (tokenizer.separatorAfterCurrentToken()) {
return NS_ERROR_DOM_SYNTAX_ERR; // trailing comma
}
} else {
nsWhitespaceTokenizer tokenizer(aValue);
nsWhitespaceTokenizerTemplate<IsSVGWhitespace> tokenizer(aValue);
while (tokenizer.hasMoreTokens()) {
if (!temp.AppendItem(tokenizer.nextToken())) {

View File

@ -6,11 +6,10 @@
#include "mozilla/dom/SVGTransform.h"
#include "mozilla/dom/SVGMatrix.h"
#include "mozilla/dom/SVGTransformBinding.h"
#include "nsError.h"
#include "nsAttrValueInlines.h"
#include "nsSVGAnimatedTransformList.h"
#include "nsSVGAttrTearoffTable.h"
#include "mozilla/dom/SVGTransformBinding.h"
namespace mozilla {
namespace dom {

View File

@ -6,17 +6,15 @@
#include "mozilla/Util.h"
#include "nsSVGAngle.h"
#include "prdtoa.h"
#include "nsTextFormatter.h"
#include "nsSVGAttrTearoffTable.h"
#include "mozilla/dom/SVGMarkerElement.h"
#include "nsMathUtils.h"
#include "nsContentUtils.h" // NS_ENSURE_FINITE
#include "nsSMILValue.h"
#include "SVGOrientSMILType.h"
#include "nsAttrValueInlines.h"
#include "nsSVGAttrTearoffTable.h"
#include "nsTextFormatter.h"
#include "prdtoa.h"
#include "SVGAngle.h"
#include "SVGAnimatedAngle.h"
#include "SVGOrientSMILType.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -104,7 +102,7 @@ GetValueFromString(const nsAString &aValueAsString,
NS_ConvertUTF16toUTF8 value(aValueAsString);
const char *str = value.get();
if (NS_IsAsciiWhitespace(*str))
if (IsSVGWhitespace(*str))
return NS_ERROR_DOM_SYNTAX_ERR;
char *rest;

View File

@ -8,6 +8,7 @@
#include "nsSVGInteger.h"
#include "nsSMILValue.h"
#include "SMILIntegerType.h"
#include "SVGContentUtils.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -24,7 +25,7 @@ GetValueFromString(const nsAString &aValueAsString,
NS_ConvertUTF16toUTF8 value(aValueAsString);
const char *str = value.get();
if (NS_IsAsciiWhitespace(*str))
if (IsSVGWhitespace(*str))
return NS_ERROR_DOM_SYNTAX_ERR;
char *rest;

View File

@ -29,7 +29,7 @@ ParseIntegerOptionalInteger(const nsAString& aValue,
nsCharSeparatedTokenizerTemplate<IsSVGWhitespace>
tokenizer(aValue, ',',
nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
if (tokenizer.firstTokenBeganWithWhitespace()) {
if (tokenizer.whitespaceBeforeFirstToken()) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
@ -53,8 +53,8 @@ ParseIntegerOptionalInteger(const nsAString& aValue,
if (i == 0 || // Too few values.
tokenizer.hasMoreTokens() || // Too many values.
tokenizer.lastTokenEndedWithWhitespace() || // Trailing whitespace.
tokenizer.lastTokenEndedWithSeparator()) { // Trailing comma.
tokenizer.whitespaceAfterCurrentToken() || // Trailing whitespace.
tokenizer.separatorAfterCurrentToken()) { // Trailing comma.
return NS_ERROR_DOM_SYNTAX_ERR;
}

View File

@ -6,17 +6,16 @@
#include "mozilla/Util.h"
#include "nsSVGLength2.h"
#include "prdtoa.h"
#include "nsTextFormatter.h"
#include "mozilla/dom/SVGSVGElement.h"
#include "nsIFrame.h"
#include "nsSVGIntegrationUtils.h"
#include "nsSVGAttrTearoffTable.h"
#include "nsContentUtils.h" // NS_ENSURE_FINITE
#include "nsSMILValue.h"
#include "nsSMILFloatType.h"
#include "nsAttrValueInlines.h"
#include "mozilla/dom/SVGAnimatedLength.h"
#include "mozilla/dom/SVGSVGElement.h"
#include "nsContentUtils.h" // NS_ENSURE_FINITE
#include "nsIFrame.h"
#include "nsSMILFloatType.h"
#include "nsSMILValue.h"
#include "nsSVGAttrTearoffTable.h"
#include "nsSVGIntegrationUtils.h"
#include "nsTextFormatter.h"
#include "prdtoa.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -131,7 +130,7 @@ GetValueFromString(const nsAString &aValueAsString,
NS_ConvertUTF16toUTF8 value(aValueAsString);
const char *str = value.get();
if (NS_IsAsciiWhitespace(*str))
if (IsSVGWhitespace(*str))
return NS_ERROR_DOM_SYNTAX_ERR;
char *rest;

View File

@ -3,16 +3,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsError.h"
#include "nsSVGAttrTearoffTable.h"
#include "nsSVGNumber2.h"
#include "prdtoa.h"
#include "nsMathUtils.h"
#include "nsContentUtils.h" // NS_ENSURE_FINITE
#include "nsSMILValue.h"
#include "nsSMILFloatType.h"
#include "nsIDOMSVGNumber.h"
#include "mozilla/Attributes.h"
#include "nsContentUtils.h" // NS_ENSURE_FINITE
#include "nsError.h"
#include "nsIDOMSVGNumber.h"
#include "nsSMILFloatType.h"
#include "nsSMILValue.h"
#include "nsSVGAttrTearoffTable.h"
#include "prdtoa.h"
#include "SVGContentUtils.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -58,7 +58,7 @@ GetValueFromString(const nsAString &aValueAsString,
NS_ConvertUTF16toUTF8 value(aValueAsString);
const char *str = value.get();
if (NS_IsAsciiWhitespace(*str))
if (IsSVGWhitespace(*str))
return NS_ERROR_DOM_SYNTAX_ERR;
char *rest;

View File

@ -28,7 +28,7 @@ ParseNumberOptionalNumber(const nsAString& aValue,
nsCharSeparatedTokenizerTemplate<IsSVGWhitespace>
tokenizer(aValue, ',',
nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
if (tokenizer.firstTokenBeganWithWhitespace()) {
if (tokenizer.whitespaceBeforeFirstToken()) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
@ -52,8 +52,8 @@ ParseNumberOptionalNumber(const nsAString& aValue,
if (i == 0 || // Too few values.
tokenizer.hasMoreTokens() || // Too many values.
tokenizer.lastTokenEndedWithWhitespace() || // Trailing whitespace.
tokenizer.lastTokenEndedWithSeparator()) { // Trailing comma.
tokenizer.whitespaceAfterCurrentToken() || // Trailing whitespace.
tokenizer.separatorAfterCurrentToken()) { // Trailing comma.
return NS_ERROR_DOM_SYNTAX_ERR;
}

View File

@ -7,11 +7,9 @@
#include "prdtoa.h"
#include "nsTextFormatter.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsMathUtils.h"
#include "nsSMILValue.h"
#include "SVGContentUtils.h"
#include "SVGViewBoxSMILType.h"
#include "nsAttrValueInlines.h"
#define NUM_VIEWBOX_COMPONENTS 4
using namespace mozilla;
@ -139,7 +137,7 @@ ToSVGViewBoxRect(const nsAString& aStr, nsSVGViewBoxRect *aViewBox)
if (i != NUM_VIEWBOX_COMPONENTS || // Too few values.
tokenizer.hasMoreTokens() || // Too many values.
tokenizer.lastTokenEndedWithSeparator()) { // Trailing comma.
tokenizer.separatorAfterCurrentToken()) { // Trailing comma.
return NS_ERROR_DOM_SYNTAX_ERR;
}

View File

@ -198,11 +198,14 @@ function runTests()
// enum attribute
is(1, SVGFEConvolveMatrixElement.SVG_EDGEMODE_DUPLICATE, "SVG_EDGEMODE_DUPLICATE value");
is(2, SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP, "SVG_EDGEMODE_WRAP value");
convolve.setAttribute("edgeMode", "wrap");
is(convolve.edgeMode.baseVal, 2, "enum baseVal");
is(convolve.edgeMode.animVal, 2, "enum animVal");
convolve.edgeMode.baseVal = 1;
is(convolve.edgeMode.animVal, 1, "enum animVal");
is(convolve.edgeMode.baseVal, SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP, "enum baseVal");
is(convolve.edgeMode.animVal, SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP, "enum animVal");
convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_DUPLICATE;
is(convolve.edgeMode.animVal, SVGFEConvolveMatrixElement.SVG_EDGEMODE_DUPLICATE, "enum animVal");
is(convolve.getAttribute("edgeMode"), "duplicate", "enum attribute");
convolve.setAttribute("edgeMode", "");
ok(convolve.getAttribute("edgeMode") === "", "empty enum attribute");
@ -224,26 +227,58 @@ function runTests()
// preserveAspectRatio attribute
is(0, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_UNKNOWN, "SVG_PRESERVEASPECTRATIO_UNKNOWN value");
is(1, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_NONE, "SVG_PRESERVEASPECTRATIO_NONE value");
is(3, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN, "SVG_PRESERVEASPECTRATIO_XMIDYMIN value");
is(5, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMID, "SVG_PRESERVEASPECTRATIO_XMINYMID value");
is(7, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMID, "SVG_PRESERVEASPECTRATIO_XMAXYMID value");
is(10, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMAX , "SVG_PRESERVEASPECTRATIO_XMAXYMAX value");
is(0, SVGPreserveAspectRatio.SVG_MEETORSLICE_UNKNOWN, "SVG_MEETORSLICE_UNKNOWN value");
is(1, SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET, "SVG_MEETORSLICE_MEET value");
is(2, SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "SVG_MEETORSLICE_SLICE value");
marker.setAttribute("preserveAspectRatio", "xMinYMid slice");
is(marker.preserveAspectRatio.baseVal.align, 5, "preserveAspectRatio.align baseVal");
is(marker.preserveAspectRatio.animVal.align, 5, "preserveAspectRatio.align animVal");
is(marker.preserveAspectRatio.baseVal.meetOrSlice, 2, "preserveAspectRatio.meetOrSlice baseVal");
is(marker.preserveAspectRatio.animVal.meetOrSlice, 2, "preserveAspectRatio.meetOrSlice animVal");
marker.preserveAspectRatio.baseVal.align = 3;
is(marker.preserveAspectRatio.animVal.align, 3, "preserveAspectRatio animVal");
is(marker.preserveAspectRatio.animVal.meetOrSlice, 2, "preserveAspectRatio.meetOrSlice animVal");
marker.preserveAspectRatio.baseVal.meetOrSlice = 1;
is(marker.preserveAspectRatio.animVal.align, 3, "preserveAspectRatio animVal");
is(marker.preserveAspectRatio.animVal.meetOrSlice, 1, "preserveAspectRatio.meetOrSlice animVal");
is(marker.preserveAspectRatio.baseVal.align,
SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMID, "preserveAspectRatio.align baseVal");
is(marker.preserveAspectRatio.animVal.align,
SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMID, "preserveAspectRatio.align animVal");
is(marker.preserveAspectRatio.baseVal.meetOrSlice,
SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice baseVal");
is(marker.preserveAspectRatio.animVal.meetOrSlice,
SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice animVal");
marker.preserveAspectRatio.baseVal.align =
SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN;
is(marker.preserveAspectRatio.animVal.align,
SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN, "preserveAspectRatio animVal");
is(marker.preserveAspectRatio.animVal.meetOrSlice,
SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice animVal");
marker.preserveAspectRatio.baseVal.meetOrSlice = SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET;
is(marker.preserveAspectRatio.animVal.align,
SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN, "preserveAspectRatio animVal");
is(marker.preserveAspectRatio.animVal.meetOrSlice,
SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET, "preserveAspectRatio.meetOrSlice animVal");
is(marker.getAttribute("preserveAspectRatio"), "xMidYMin meet", "preserveAspectRatio attribute");
var basePreserveAspectRatio = marker.preserveAspectRatio.baseVal;
var animPreserveAspectRatio = marker.preserveAspectRatio.animVal;
marker.setAttribute("preserveAspectRatio", "xMaxYMid slice");
is(basePreserveAspectRatio.align, 7, "preserveAspectRatio.align baseVal");
is(animPreserveAspectRatio.align, 7, "preserveAspectRatio.align animVal");
is(basePreserveAspectRatio.meetOrSlice, 2, "preserveAspectRatio.meetOrSlice baseVal");
is(animPreserveAspectRatio.meetOrSlice, 2, "preserveAspectRatio.meetOrSlice animVal");
is(basePreserveAspectRatio.align,
SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMID, "preserveAspectRatio.align baseVal");
is(animPreserveAspectRatio.align,
SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMID, "preserveAspectRatio.align animVal");
is(basePreserveAspectRatio.meetOrSlice,
SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice baseVal");
is(animPreserveAspectRatio.meetOrSlice,
SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice animVal");
marker.setAttribute("preserveAspectRatio", " none"); // invalid, space at beginning
is(basePreserveAspectRatio.align, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMID,
"default preserveAspectRatio attribute");
marker.setAttribute("preserveAspectRatio", "none "); // invalid, space at end
is(basePreserveAspectRatio.align, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMID,
"default preserveAspectRatio attribute");
marker.setAttribute("preserveAspectRatio", "");
ok(marker.getAttribute("preserveAspectRatio") === "",

View File

@ -34,7 +34,7 @@ class nsCharSeparatedTokenizerTemplate
{
public:
// Flags -- only one for now. If we need more, they should be defined to
// be 1<<1, 1<<2, etc. (They're masks, and aFlags/mFlags are bitfields.)
// be 1 << 1, 1 << 2, etc. (They're masks, and aFlags is a bitfield.)
enum {
SEPARATOR_OPTIONAL = 1
};
@ -45,15 +45,15 @@ public:
: mIter(aSource.Data(), aSource.Length()),
mEnd(aSource.Data() + aSource.Length(), aSource.Data(),
aSource.Length()),
mFirstTokenBeganWithWhitespace(false),
mLastTokenEndedWithWhitespace(false),
mLastTokenEndedWithSeparator(false),
mSeparatorChar(aSeparatorChar),
mFlags(aFlags)
mWhitespaceBeforeFirstToken(false),
mWhitespaceAfterCurrentToken(false),
mSeparatorAfterCurrentToken(false),
mSeparatorOptional(aFlags & SEPARATOR_OPTIONAL)
{
// Skip initial whitespace
while (mIter < mEnd && IsWhitespace(*mIter)) {
mFirstTokenBeganWithWhitespace = true;
mWhitespaceBeforeFirstToken = true;
++mIter;
}
}
@ -61,27 +61,38 @@ public:
/**
* Checks if any more tokens are available.
*/
bool hasMoreTokens()
bool hasMoreTokens() const
{
NS_ASSERTION(mIter == mEnd || !IsWhitespace(*mIter),
"Should be at beginning of token if there is one");
MOZ_ASSERT(mIter == mEnd || !IsWhitespace(*mIter),
"Should be at beginning of token if there is one");
return mIter < mEnd;
}
bool firstTokenBeganWithWhitespace() const
/*
* Returns true if there is whitespace prior to the first token.
*/
bool whitespaceBeforeFirstToken() const
{
return mFirstTokenBeganWithWhitespace;
return mWhitespaceBeforeFirstToken;
}
bool lastTokenEndedWithSeparator() const
/*
* Returns true if there is a separator after the current token.
* Useful if you want to check whether the last token has a separator
* after it which may not be valid.
*/
bool separatorAfterCurrentToken() const
{
return mLastTokenEndedWithSeparator;
return mSeparatorAfterCurrentToken;
}
bool lastTokenEndedWithWhitespace() const
/*
* Returns true if there is any whitespace after the current token.
*/
bool whitespaceAfterCurrentToken() const
{
return mLastTokenEndedWithWhitespace;
return mWhitespaceAfterCurrentToken;
}
/**
@ -91,45 +102,46 @@ public:
{
mozilla::RangedPtr<const PRUnichar> tokenStart = mIter, tokenEnd = mIter;
NS_ASSERTION(mIter == mEnd || !IsWhitespace(*mIter),
"Should be at beginning of token if there is one");
MOZ_ASSERT(mIter == mEnd || !IsWhitespace(*mIter),
"Should be at beginning of token if there is one");
// Search until we hit separator or end (or whitespace, if separator
// Search until we hit separator or end (or whitespace, if a separator
// isn't required -- see clause with 'break' below).
while (mIter < mEnd && *mIter != mSeparatorChar) {
// Skip to end of current word.
// Skip to end of the current word.
while (mIter < mEnd &&
!IsWhitespace(*mIter) && *mIter != mSeparatorChar) {
++mIter;
}
tokenEnd = mIter;
// Skip whitespace after current word.
mLastTokenEndedWithWhitespace = false;
// Skip whitespace after the current word.
mWhitespaceAfterCurrentToken = false;
while (mIter < mEnd && IsWhitespace(*mIter)) {
mLastTokenEndedWithWhitespace = true;
mWhitespaceAfterCurrentToken = true;
++mIter;
}
if (mFlags & SEPARATOR_OPTIONAL) {
if (mSeparatorOptional) {
// We've hit (and skipped) whitespace, and that's sufficient to end
// our token, regardless of whether we've reached a SeparatorChar.
break;
} // (else, we'll keep looping until we hit mEnd or SeparatorChar)
}
mLastTokenEndedWithSeparator = (mIter != mEnd &&
*mIter == mSeparatorChar);
NS_ASSERTION((mFlags & SEPARATOR_OPTIONAL) ||
(mLastTokenEndedWithSeparator == (mIter < mEnd)),
"If we require a separator and haven't hit the end of "
"our string, then we shouldn't have left the loop "
"unless we hit a separator");
mSeparatorAfterCurrentToken = (mIter != mEnd &&
*mIter == mSeparatorChar);
MOZ_ASSERT(mSeparatorOptional ||
(mSeparatorAfterCurrentToken == (mIter < mEnd)),
"If we require a separator and haven't hit the end of "
"our string, then we shouldn't have left the loop "
"unless we hit a separator");
// Skip separator (and any whitespace after it), if we're at one.
if (mLastTokenEndedWithSeparator) {
if (mSeparatorAfterCurrentToken) {
++mIter;
while (mIter < mEnd && IsWhitespace(*mIter)) {
mWhitespaceAfterCurrentToken = true;
++mIter;
}
}
@ -140,11 +152,11 @@ public:
private:
mozilla::RangedPtr<const PRUnichar> mIter;
const mozilla::RangedPtr<const PRUnichar> mEnd;
bool mFirstTokenBeganWithWhitespace;
bool mLastTokenEndedWithWhitespace;
bool mLastTokenEndedWithSeparator;
PRUnichar mSeparatorChar;
uint32_t mFlags;
bool mWhitespaceBeforeFirstToken;
bool mWhitespaceAfterCurrentToken;
bool mSeparatorAfterCurrentToken;
bool mSeparatorOptional;
};
class nsCharSeparatedTokenizer: public nsCharSeparatedTokenizerTemplate<>
@ -158,18 +170,31 @@ public:
}
};
class nsCCharSeparatedTokenizer
template<bool IsWhitespace(PRUnichar) = NS_IsAsciiWhitespace>
class nsCCharSeparatedTokenizerTemplate
{
public:
nsCCharSeparatedTokenizer(const nsCSubstring& aSource,
char aSeparatorChar)
// Flags -- only one for now. If we need more, they should be defined to
// be 1 << 1, 1 << 2, etc. (They're masks, and aFlags is a bitfield.)
enum {
SEPARATOR_OPTIONAL = 1
};
nsCCharSeparatedTokenizerTemplate(const nsCSubstring& aSource,
char aSeparatorChar,
uint32_t aFlags = 0)
: mIter(aSource.Data(), aSource.Length()),
mEnd(aSource.Data() + aSource.Length(), aSource.Data(),
aSource.Length()),
mSeparatorChar(aSeparatorChar)
mSeparatorChar(aSeparatorChar),
mWhitespaceBeforeFirstToken(false),
mWhitespaceAfterCurrentToken(false),
mSeparatorAfterCurrentToken(false),
mSeparatorOptional(aFlags & SEPARATOR_OPTIONAL)
{
while (mIter < mEnd && isWhitespace(*mIter)) {
// Skip initial whitespace
while (mIter < mEnd && IsWhitespace(*mIter)) {
mWhitespaceBeforeFirstToken = true;
++mIter;
}
}
@ -177,11 +202,40 @@ public:
/**
* Checks if any more tokens are available.
*/
bool hasMoreTokens()
bool hasMoreTokens() const
{
MOZ_ASSERT(mIter == mEnd || !IsWhitespace(*mIter),
"Should be at beginning of token if there is one");
return mIter < mEnd;
}
/*
* Returns true if there is whitespace prior to the first token.
*/
bool whitespaceBeforeFirstToken() const
{
return mWhitespaceBeforeFirstToken;
}
/*
* Returns true if there is a separator after the current token.
* Useful if you want to check whether the last token has a separator
* after it which may not be valid.
*/
bool separatorAfterCurrentToken() const
{
return mSeparatorAfterCurrentToken;
}
/*
* Returns true if there is any whitespace after the current token.
*/
bool whitespaceAfterCurrentToken() const
{
return mWhitespaceAfterCurrentToken;
}
/**
* Returns the next token.
*/
@ -189,25 +243,46 @@ public:
{
mozilla::RangedPtr<const char> tokenStart = mIter, tokenEnd = mIter;
// Search until we hit separator or end.
MOZ_ASSERT(mIter == mEnd || !IsWhitespace(*mIter),
"Should be at beginning of token if there is one");
// Search until we hit separator or end (or whitespace, if a separator
// isn't required -- see clause with 'break' below).
while (mIter < mEnd && *mIter != mSeparatorChar) {
// Skip to end of the current word.
while (mIter < mEnd &&
!isWhitespace(*mIter) && *mIter != mSeparatorChar) {
!IsWhitespace(*mIter) && *mIter != mSeparatorChar) {
++mIter;
}
tokenEnd = mIter;
while (mIter < mEnd && isWhitespace(*mIter)) {
// Skip whitespace after the current word.
mWhitespaceAfterCurrentToken = false;
while (mIter < mEnd && IsWhitespace(*mIter)) {
mWhitespaceAfterCurrentToken = true;
++mIter;
}
if (mSeparatorOptional) {
// We've hit (and skipped) whitespace, and that's sufficient to end
// our token, regardless of whether we've reached a SeparatorChar.
break;
} // (else, we'll keep looping until we hit mEnd or SeparatorChar)
}
// Skip separator (and any whitespace after it).
if (mIter < mEnd) {
NS_ASSERTION(*mIter == mSeparatorChar, "Ended loop too soon");
mSeparatorAfterCurrentToken = (mIter != mEnd &&
*mIter == mSeparatorChar);
MOZ_ASSERT(mSeparatorOptional ||
(mSeparatorAfterCurrentToken == (mIter < mEnd)),
"If we require a separator and haven't hit the end of "
"our string, then we shouldn't have left the loop "
"unless we hit a separator");
// Skip separator (and any whitespace after it), if we're at one.
if (mSeparatorAfterCurrentToken) {
++mIter;
while (mIter < mEnd && isWhitespace(*mIter)) {
while (mIter < mEnd && IsWhitespace(*mIter)) {
mWhitespaceAfterCurrentToken = true;
++mIter;
}
}
@ -219,12 +294,20 @@ private:
mozilla::RangedPtr<const char> mIter;
const mozilla::RangedPtr<const char> mEnd;
char mSeparatorChar;
bool mWhitespaceBeforeFirstToken;
bool mWhitespaceAfterCurrentToken;
bool mSeparatorAfterCurrentToken;
bool mSeparatorOptional;
};
bool isWhitespace(unsigned char aChar)
class nsCCharSeparatedTokenizer: public nsCCharSeparatedTokenizerTemplate<>
{
public:
nsCCharSeparatedTokenizer(const nsCSubstring& aSource,
char aSeparatorChar,
uint32_t aFlags = 0)
: nsCCharSeparatedTokenizerTemplate<>(aSource, aSeparatorChar, aFlags)
{
return aChar <= ' ' &&
(aChar == ' ' || aChar == '\n' ||
aChar == '\r'|| aChar == '\t');
}
};

View File

@ -8,16 +8,21 @@
#include "mozilla/RangedPtr.h"
#include "nsDependentSubstring.h"
#include "nsCRT.h"
class nsWhitespaceTokenizer
template<bool IsWhitespace(PRUnichar) = NS_IsAsciiWhitespace>
class nsWhitespaceTokenizerTemplate
{
public:
nsWhitespaceTokenizer(const nsSubstring& aSource)
nsWhitespaceTokenizerTemplate(const nsSubstring& aSource)
: mIter(aSource.Data(), aSource.Length()),
mEnd(aSource.Data() + aSource.Length(), aSource.Data(),
aSource.Length())
aSource.Length()),
mWhitespaceBeforeFirstToken(false),
mWhitespaceAfterCurrentToken(false)
{
while (mIter < mEnd && isWhitespace(*mIter)) {
while (mIter < mEnd && IsWhitespace(*mIter)) {
mWhitespaceBeforeFirstToken = true;
++mIter;
}
}
@ -25,22 +30,41 @@ public:
/**
* Checks if any more tokens are available.
*/
bool hasMoreTokens()
bool hasMoreTokens() const
{
return mIter < mEnd;
}
/*
* Returns true if there is whitespace prior to the first token.
*/
bool whitespaceBeforeFirstToken() const
{
return mWhitespaceBeforeFirstToken;
}
/*
* Returns true if there is any whitespace after the current token.
* This is always true unless we're reading the last token.
*/
bool whitespaceAfterCurrentToken() const
{
return mWhitespaceAfterCurrentToken;
}
/**
* Returns the next token.
*/
const nsDependentSubstring nextToken()
{
const mozilla::RangedPtr<const PRUnichar> tokenStart = mIter;
while (mIter < mEnd && !isWhitespace(*mIter)) {
while (mIter < mEnd && !IsWhitespace(*mIter)) {
++mIter;
}
const mozilla::RangedPtr<const PRUnichar> tokenEnd = mIter;
while (mIter < mEnd && isWhitespace(*mIter)) {
mWhitespaceAfterCurrentToken = false;
while (mIter < mEnd && IsWhitespace(*mIter)) {
mWhitespaceAfterCurrentToken = true;
++mIter;
}
return Substring(tokenStart.get(), tokenEnd.get());
@ -49,24 +73,32 @@ public:
private:
mozilla::RangedPtr<const PRUnichar> mIter;
const mozilla::RangedPtr<const PRUnichar> mEnd;
bool mWhitespaceBeforeFirstToken;
bool mWhitespaceAfterCurrentToken;
};
bool isWhitespace(PRUnichar aChar)
class nsWhitespaceTokenizer: public nsWhitespaceTokenizerTemplate<>
{
public:
nsWhitespaceTokenizer(const nsSubstring& aSource)
: nsWhitespaceTokenizerTemplate<>(aSource)
{
return aChar <= ' ' &&
(aChar == ' ' || aChar == '\n' ||
aChar == '\r'|| aChar == '\t');
}
};
class nsCWhitespaceTokenizer
template<bool IsWhitespace(PRUnichar) = NS_IsAsciiWhitespace>
class nsCWhitespaceTokenizerTemplate
{
public:
nsCWhitespaceTokenizer(const nsCSubstring& aSource)
nsCWhitespaceTokenizerTemplate(const nsCSubstring& aSource)
: mIter(aSource.Data(), aSource.Length()),
mEnd(aSource.Data() + aSource.Length(), aSource.Data(),
aSource.Length())
aSource.Length()),
mWhitespaceBeforeFirstToken(false),
mWhitespaceAfterCurrentToken(false)
{
while (mIter < mEnd && isWhitespace(*mIter)) {
while (mIter < mEnd && IsWhitespace(*mIter)) {
mWhitespaceBeforeFirstToken = true;
++mIter;
}
}
@ -74,22 +106,41 @@ public:
/**
* Checks if any more tokens are available.
*/
bool hasMoreTokens()
bool hasMoreTokens() const
{
return mIter < mEnd;
}
/*
* Returns true if there is whitespace prior to the first token.
*/
bool whitespaceBeforeFirstToken() const
{
return mWhitespaceBeforeFirstToken;
}
/*
* Returns true if there is any whitespace after the current token.
* This is always true unless we're reading the last token.
*/
bool whitespaceAfterCurrentToken() const
{
return mWhitespaceAfterCurrentToken;
}
/**
* Returns the next token.
*/
const nsDependentCSubstring nextToken()
{
const mozilla::RangedPtr<const char> tokenStart = mIter;
while (mIter < mEnd && !isWhitespace(*mIter)) {
while (mIter < mEnd && !IsWhitespace(*mIter)) {
++mIter;
}
const mozilla::RangedPtr<const char> tokenEnd = mIter;
while (mIter < mEnd && isWhitespace(*mIter)) {
mWhitespaceAfterCurrentToken = false;
while (mIter < mEnd && IsWhitespace(*mIter)) {
mWhitespaceAfterCurrentToken = true;
++mIter;
}
return Substring(tokenStart.get(), tokenEnd.get());
@ -98,12 +149,16 @@ public:
private:
mozilla::RangedPtr<const char> mIter;
const mozilla::RangedPtr<const char> mEnd;
bool mWhitespaceBeforeFirstToken;
bool mWhitespaceAfterCurrentToken;
};
bool isWhitespace(char aChar)
class nsCWhitespaceTokenizer: public nsCWhitespaceTokenizerTemplate<>
{
public:
nsCWhitespaceTokenizer(const nsCSubstring& aSource)
: nsCWhitespaceTokenizerTemplate<>(aSource)
{
return aChar <= ' ' &&
(aChar == ' ' || aChar == '\n' ||
aChar == '\r'|| aChar == '\t');
}
};