Bug 1217643 part 1: Add partial support for parsing CSS -webkit-gradient expressions (if webkit prefix support is enabled). r=heycam

This commit is contained in:
Daniel Holbert 2015-11-20 14:46:08 -08:00
Родитель d0e03b7083
Коммит 8244546943
3 изменённых файлов: 553 добавлений и 3 удалений

Просмотреть файл

@ -9,11 +9,11 @@ fails == webkit-gradient-approx-linear-1.html webkit-gradient-approx-linear-1-re
fails == webkit-gradient-approx-radial-1.html webkit-gradient-approx-radial-1-ref.html
# Tests for -webkit-gradient(linear, ...)
fails == webkit-gradient-linear-1a.html webkit-gradient-linear-1-ref.html
== webkit-gradient-linear-1a.html webkit-gradient-linear-1-ref.html
fails == webkit-gradient-linear-1b.html webkit-gradient-linear-1-ref.html
fails == webkit-gradient-linear-1c.html webkit-gradient-linear-1-ref.html
fails == webkit-gradient-linear-1d.html webkit-gradient-linear-1-ref.html
fails == webkit-gradient-linear-2.html webkit-gradient-linear-2-ref.html
== webkit-gradient-linear-2.html webkit-gradient-linear-2-ref.html
# Tests for -webkit-gradient(radial, ...)
fails == webkit-gradient-radial-1a.html webkit-gradient-radial-1-ref.html

Просмотреть файл

@ -11,6 +11,8 @@
#include "mozilla/Move.h"
#include "mozilla/MathAlgorithms.h"
#include <algorithm> // for std::stable_sort
#include "nsCSSParser.h"
#include "nsCSSProps.h"
#include "nsCSSKeywords.h"
@ -1241,6 +1243,23 @@ protected:
bool ParseGradientColorStops(nsCSSValueGradient* aGradient,
nsCSSValue& aValue);
// For the ancient "-webkit-gradient(linear|radial, ...)" syntax:
bool ParseWebkitGradientPointComponent(nsCSSValue& aComponent,
bool aIsHorizontal);
bool ParseWebkitGradientPoint(nsCSSValuePair& aPoint);
bool ParseWebkitGradientRadius(float& aRadius);
bool ParseWebkitGradientColorStop(nsCSSValueGradient* aGradient);
bool ParseWebkitGradientColorStops(nsCSSValueGradient* aGradient);
void FinalizeLinearWebkitGradient(nsCSSValueGradient* aGradient,
const nsCSSValuePair& aStartPoint,
const nsCSSValuePair& aSecondPoint);
void FinalizeRadialWebkitGradient(nsCSSValueGradient* aGradient,
const nsCSSValuePair& aFirstCenter,
const nsCSSValuePair& aSecondCenter,
const float aFirstRadius,
const float aSecondRadius);
bool ParseWebkitGradient(nsCSSValue& aValue);
void SetParsingCompoundProperty(bool aBool) {
mParsingCompoundProperty = aBool;
}
@ -7647,6 +7666,18 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue,
}
return CSSParseResult::Ok;
}
if ((gradientFlags == eGradient_WebkitLegacy) &&
tmp.LowerCaseEqualsLiteral("gradient")) {
// Note: we check gradientFlags using '==' to select *exactly*
// eGradient_WebkitLegacy -- and exclude eGradient_Repeating -- because
// we don't want to accept -webkit-repeating-gradient() expressions.
// (This is not a recognized syntax.)
if (!ParseWebkitGradient(aValue)) {
return CSSParseResult::Error;
}
return CSSParseResult::Ok;
}
if (ShouldUseUnprefixingService() &&
!gradientFlags &&
StringBeginsWith(tmp, NS_LITERAL_STRING("-webkit-"))) {
@ -10013,7 +10044,365 @@ CSSParserImpl::ParseGradientColorStops(nsCSSValueGradient* aGradient,
return true;
}
int32_t
// Parses the x or y component of a -webkit-gradient() <point> expression.
// See ParseWebkitGradientPoint() documentation for more.
bool
CSSParserImpl::ParseWebkitGradientPointComponent(nsCSSValue& aComponent,
bool aIsHorizontal)
{
if (!GetToken(true)) {
return false;
}
// Keyword tables to use for keyword-matching
// (Keyword order is important; we assume the index can be multiplied by 50%
// to convert to a percent-valued component.)
static const nsCSSKeyword kHorizKeywords[] = {
eCSSKeyword_left, // 0%
eCSSKeyword_center, // 50%
eCSSKeyword_right // 100%
};
static const nsCSSKeyword kVertKeywords[] = {
eCSSKeyword_top, // 0%
eCSSKeyword_center, // 50%
eCSSKeyword_bottom // 100%
};
static const size_t kNumKeywords = MOZ_ARRAY_LENGTH(kHorizKeywords);
static_assert(kNumKeywords == MOZ_ARRAY_LENGTH(kVertKeywords),
"Horizontal & vertical keyword tables must have same count");
// Try to parse the component as a number, or a percent, or a
// keyword-converted-to-percent.
if (mToken.mType == eCSSToken_Number) {
aComponent.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel);
} else if (mToken.mType == eCSSToken_Percentage) {
aComponent.SetPercentValue(mToken.mNumber);
} else if (mToken.mType == eCSSToken_Ident) {
nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
if (keyword == eCSSKeyword_UNKNOWN) {
return false;
}
// Choose our keyword table:
const nsCSSKeyword* kwTable = aIsHorizontal ? kHorizKeywords : kVertKeywords;
// Convert keyword to percent value (0%, 50%, or 100%)
bool didAcceptKeyword = false;
for (size_t i = 0; i < kNumKeywords; i++) {
if (keyword == kwTable[i]) {
// 0%, 50%, or 100%:
aComponent.SetPercentValue(i * 0.5);
didAcceptKeyword = true;
break;
}
}
if (!didAcceptKeyword) {
return false;
}
} else {
// Unrecognized token type. Put it back. (It might be a closing-paren of an
// invalid -webkit-gradient(...) expression, and we need to be sure caller
// can see it & stops parsing at that point.)
UngetToken();
return false;
}
MOZ_ASSERT(aComponent.GetUnit() == eCSSUnit_Pixel ||
aComponent.GetUnit() == eCSSUnit_Percent,
"If we get here, we should've successfully parsed a number (as a "
"pixel length), a percent, or a keyword (converted to percent)");
return true;
}
// This function parses a "<point>" expression for -webkit-gradient(...)
// Quoting https://www.webkit.org/blog/175/introducing-css-gradients/ :
// "A point is a pair of space-separated values.
// The syntax supports numbers, percentages or
// the keywords top, bottom, left and right
// for point values."
//
// Two additional notes:
// - WebKit also accepts the "center" keyword (not listed in the text above).
// - WebKit only accepts horizontal-flavored keywords (left/center/right) in
// the first ("x") component, and vertical-flavored keywords
// (top/center/bottom) in the second ("y") component. (This is different
// from the standard gradient syntax, which accepts both orderings, e.g.
// "top left" as well as "left top".)
bool
CSSParserImpl::ParseWebkitGradientPoint(nsCSSValuePair& aPoint)
{
return ParseWebkitGradientPointComponent(aPoint.mXValue, true) &&
ParseWebkitGradientPointComponent(aPoint.mYValue, false);
}
// Parse the next token as a <number> (for a <radius> in a -webkit-gradient
// expresison). Returns true on success; returns false & puts back
// whatever it parsed on failure.
bool
CSSParserImpl::ParseWebkitGradientRadius(float& aRadius)
{
if (!GetToken(true)) {
return false;
}
if (mToken.mType != eCSSToken_Number) {
UngetToken();
return false;
}
aRadius = mToken.mNumber;
return true;
}
// Parse one of:
// color-stop(number|percent, color)
// from(color)
// to(color)
//
// Quoting https://www.webkit.org/blog/175/introducing-css-gradients/ :
// A stop is a function, color-stop, that takes two arguments, the stop value
// (either a percentage or a number between 0 and 1.0), and a color (any
// valid CSS color). In addition the shorthand functions from and to are
// supported. These functions only require a color argument and are
// equivalent to color-stop(0, ...) and color-stop(1.0, …) respectively.
bool
CSSParserImpl::ParseWebkitGradientColorStop(nsCSSValueGradient* aGradient)
{
MOZ_ASSERT(aGradient, "null gradient");
if (!GetToken(true)) {
return false;
}
// We're expecting color-stop(...), from(...), or to(...) which are all
// functions. Bail if we got anything else.
if (mToken.mType != eCSSToken_Function) {
UngetToken();
return false;
}
nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
// Parse color-stop location (or infer it, for shorthands "from"/"to"):
if (mToken.mIdent.LowerCaseEqualsLiteral("color-stop")) {
// Parse stop location, followed by comma.
if (!ParseSingleTokenVariant(stop->mLocation,
VARIANT_NUMBER | VARIANT_PERCENT,
nullptr) ||
!ExpectSymbol(',', true)) {
SkipUntil(')'); // Skip to end of color-stop(...) expression.
return false;
}
// If we got a <number>, convert it to percentage for consistency:
if (stop->mLocation.GetUnit() == eCSSUnit_Number) {
stop->mLocation.SetPercentValue(stop->mLocation.GetFloatValue());
}
} else if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
// Shorthand for color-stop(0%, ...)
stop->mLocation.SetPercentValue(0.0f);
} else if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
// Shorthand for color-stop(100%, ...)
stop->mLocation.SetPercentValue(1.0f);
} else {
// Unrecognized function name (invalid for a -webkit-gradient color stop).
UngetToken();
return false;
}
CSSParseResult result = ParseVariant(stop->mColor, VARIANT_COLOR, nullptr);
if (result != CSSParseResult::Ok ||
(stop->mColor.GetUnit() == eCSSUnit_EnumColor &&
stop->mColor.GetIntValue() == NS_COLOR_CURRENTCOLOR)) {
// Parse failure, or parsed "currentColor" which is forbidden in
// -webkit-gradient for some reason.
SkipUntil(')');
return false;
}
// Parse color-stop function close-paren
if (!ExpectSymbol(')', true)) {
SkipUntil(')');
return false;
}
MOZ_ASSERT(stop->mLocation.GetUnit() == eCSSUnit_Percent,
"Should produce only percent-valued stop-locations. "
"(Caller depends on this when sorting color stops.)");
return true;
}
// Comparatison function to use for sorting -webkit-gradient() stops by
// location. This function assumes stops have percent-valued locations (and
// CSSParserImpl::ParseWebkitGradientColorStop should enforce this).
static bool
IsColorStopPctLocationLessThan(const nsCSSValueGradientStop& aStop1,
const nsCSSValueGradientStop& aStop2) {
return (aStop1.mLocation.GetPercentValue() <
aStop2.mLocation.GetPercentValue());
}
// This function parses a list of comma-separated color-stops for a
// -webkit-gradient(...) expression, and then pads & sorts the list as-needed.
bool
CSSParserImpl::ParseWebkitGradientColorStops(nsCSSValueGradient* aGradient)
{
MOZ_ASSERT(aGradient, "null gradient");
// Parse any number of ", <color-stop>" expressions. (0 or more)
// Note: This is different from unprefixed gradient syntax, which
// requires at least 2 stops.
while (ExpectSymbol(',', true)) {
if (!ParseWebkitGradientColorStop(aGradient)) {
return false;
}
}
// Pad up to 2 stops as-needed:
// (Modern gradient expressions are required to have at least 2 stops, so we
// depend on this internally -- e.g. we have an assertion about this in
// nsCSSRendering.cpp. -webkit-gradient syntax allows 0 stops or 1 stop,
// though, so we just pad up to 2 stops in this case).
// If we have no stops, pad with transparent-black:
if (aGradient->mStops.IsEmpty()) {
nsCSSValueGradientStop* stop1 = aGradient->mStops.AppendElement();
stop1->mColor.SetIntegerColorValue(NS_RGBA(0, 0, 0, 0),
eCSSUnit_RGBAColor);
nsCSSValueGradientStop* stop2 = aGradient->mStops.AppendElement();
stop2->mColor.SetIntegerColorValue(NS_RGBA(0, 0, 0, 0),
eCSSUnit_RGBAColor);
} else if (aGradient->mStops.Length() == 1) {
// Copy whatever the author provided in the first stop:
nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
*stop = aGradient->mStops[0];
} else {
// We have >2 stops. Sort them in order of increasing location.
std::stable_sort(aGradient->mStops.begin(),
aGradient->mStops.end(),
IsColorStopPctLocationLessThan);
}
return true;
}
// Finalize our internal representation of a -webkit-gradient(linear, ...)
// expression, given the parsed points. (The parsed color stops
// should already be hanging off of the passed-in nsCSSValueGradient.)
void
CSSParserImpl::FinalizeLinearWebkitGradient(nsCSSValueGradient* aGradient,
const nsCSSValuePair& aStartPoint,
const nsCSSValuePair& aEndPoint)
{
MOZ_ASSERT(!aGradient->mIsRadial, "passed-in gradient must be linear");
aGradient->mIsLegacySyntax = true; // (Needed in order to use non-% points)
aGradient->mBgPos = aStartPoint;
// XXXdholbert Do further positioning/angling here.
}
// Finalize our internal representation of a -webkit-gradient(radial, ...)
// expression, given the parsed points & radii. (The parsed color-stops
// should already be hanging off of the passed-in nsCSSValueGradient).
void
CSSParserImpl::FinalizeRadialWebkitGradient(nsCSSValueGradient* aGradient,
const nsCSSValuePair& aFirstCenter,
const nsCSSValuePair& aSecondCenter,
const float aFirstRadius,
const float aSecondRadius)
{
MOZ_ASSERT(aGradient->mIsRadial, "passed-in gradient must be radial");
aGradient->mBgPos = aFirstCenter;
// XXXdholbert Do further positioning/sizing here.
}
bool
CSSParserImpl::ParseWebkitGradient(nsCSSValue& aValue)
{
// Parse type of gradient
if (!GetToken(true)) {
return false;
}
if (mToken.mType != eCSSToken_Ident) {
UngetToken(); // Important; the token might be ")", which we're about to
// seek to.
SkipUntil(')');
return false;
}
bool isRadial;
if (mToken.mIdent.LowerCaseEqualsLiteral("radial")) {
isRadial = true;
} else if (mToken.mIdent.LowerCaseEqualsLiteral("linear")) {
isRadial = false;
} else {
// Unrecognized gradient type.
SkipUntil(')');
return false;
}
// Parse a comma + first point:
nsCSSValuePair firstPoint;
if (!ExpectSymbol(',', true) ||
!ParseWebkitGradientPoint(firstPoint)) {
SkipUntil(')');
return false;
}
// If radial, parse comma + first radius:
float firstRadius;
if (isRadial) {
if (!ExpectSymbol(',', true) ||
!ParseWebkitGradientRadius(firstRadius)) {
SkipUntil(')');
return false;
}
}
// Parse a comma + second point:
nsCSSValuePair secondPoint;
if (!ExpectSymbol(',', true) ||
!ParseWebkitGradientPoint(secondPoint)) {
SkipUntil(')');
return false;
}
// If radial, parse comma + second radius:
float secondRadius;
if (isRadial) {
if (!ExpectSymbol(',', true) ||
!ParseWebkitGradientRadius(secondRadius)) {
SkipUntil(')');
return false;
}
}
// Construct a nsCSSValueGradient object, and parse color stops into it:
RefPtr<nsCSSValueGradient> cssGradient =
new nsCSSValueGradient(isRadial, false /* aIsRepeating */);
if (!ParseWebkitGradientColorStops(cssGradient) ||
!ExpectSymbol(')', true)) {
// Failed to parse color-stops, or found trailing junk between them & ')'.
SkipUntil(')');
return false;
}
// Finish building cssGradient, based on our parsed positioning/sizing info:
if (isRadial) {
FinalizeRadialWebkitGradient(cssGradient, firstPoint, secondPoint,
firstRadius, secondRadius);
} else {
FinalizeLinearWebkitGradient(cssGradient, firstPoint, secondPoint);
}
aValue.SetGradientValue(cssGradient);
return true;
}
int32_t
CSSParserImpl::ParseChoice(nsCSSValue aValues[],
const nsCSSProperty aPropIDs[], int32_t aNumIDs)
{

Просмотреть файл

@ -627,6 +627,74 @@ var unbalancedGradientAndElementValues = [
if (IsCSSPropertyPrefEnabled("layout.css.prefixes.webkit")) {
// Extend gradient lists with valid/invalid webkit-prefixed expressions:
validGradientAndElementValues.push(
// 2008 GRADIENTS: -webkit-gradient()
// ----------------------------------
// linear w/ no color stops (valid) and a variety of position values:
"-webkit-gradient(linear, 1 2, 3 4)",
"-webkit-gradient(linear,1 2,3 4)", // (no extra space)
"-webkit-gradient(linear , 1 2 , 3 4 )", // (lots of extra space)
"-webkit-gradient(linear, 1 10% , 0% 4)", // percentages
"-webkit-gradient(linear, +1.0 -2%, +5.3% -0)", // (+/- & decimals are valid)
"-webkit-gradient(linear, left top, right bottom)", // keywords
"-webkit-gradient(linear, right center, center top)",
"-webkit-gradient(linear, center center, center center)",
"-webkit-gradient(linear, center 5%, 30 top)", // keywords mixed w/ nums
// linear w/ just 1 color stop:
"-webkit-gradient(linear, 1 2, 3 4, from(lime))",
"-webkit-gradient(linear, 1 2, 3 4, to(lime))",
// * testing the various allowable stop values (<number> & <percent>):
"-webkit-gradient(linear, 1 2, 3 4, color-stop(0, lime))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(-0, lime))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(-30, lime))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(+9999, lime))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(-.1, lime))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(0%, lime))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(100%, lime))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(9999%, lime))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(-.5%, lime))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(+0%, lime))",
// * testing the various color values:
"-webkit-gradient(linear, 1 2, 3 4, color-stop(0, transparent))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(0, rgb(1,2,3)))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(0, #00ff00))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(0, #00f))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(0, hsla(240, 30%, 50%, 0.9)))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(0, rgba(255, 230, 10, 0.5)))",
// linear w/ multiple color stops:
// * using from()/to() -- note that out-of-order is OK:
"-webkit-gradient(linear, 1 2, 3 4, from(lime), from(blue))",
"-webkit-gradient(linear, 1 2, 3 4, to(lime), to(blue))",
"-webkit-gradient(linear, 1 2, 3 4, from(lime), to(blue))",
"-webkit-gradient(linear, 1 2, 3 4, to(lime), from(blue))",
"-webkit-gradient(linear, 1 2, 3 4, from(lime), to(blue), from(purple))",
// * using color-stop():
"-webkit-gradient(linear, 1 2, 3 4, color-stop(0, lime), color-stop(30%, blue))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(0, lime), color-stop(30%, blue), color-stop(100%, purple))",
// * using color-stop() intermixed with from()/to() functions:
"-webkit-gradient(linear, 1 2, 3 4, from(lime), color-stop(30%, blue))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(30%, blue), to(lime))",
// * overshooting endpoints (0 & 1.0)
"-webkit-gradient(linear, 1 2, 3 4, color-stop(-30%, lime), color-stop(.4, blue), color-stop(1.5, purple))",
// * repeating a stop position (valid)
"-webkit-gradient(linear, 1 2, 3 4, color-stop(30%, lime), color-stop(30%, blue))",
// * stops out of order (valid)
"-webkit-gradient(linear, 1 2, 3 4, color-stop(70%, lime), color-stop(20%, blue), color-stop(40%, purple))",
// radial w/ no color stops (valid) and a several different radius values:
"-webkit-gradient(radial, 1 2, 8, 3 4, 9)",
"-webkit-gradient(radial, 1 2, -1.5, center center, +99999.9999)",
// radial w/ color stops
// (mostly leaning on more-robust 'linear' tests above; just testing a few
// examples w/ radial as a sanity-check):
"-webkit-gradient(radial, 1 2, 8, 3 4, 9, from(lime))",
"-webkit-gradient(radial, 1 2, 8, 3 4, 9, to(blue))",
"-webkit-gradient(radial, 1 2, 8, 3 4, 9, color-stop(0.5, #00f), color-stop(0.8, rgba(100, 200, 0, 0.5)))",
// 2011 GRADIENTS: -webkit-linear-gradient(), -webkit-radial -gradient()
// ---------------------------------------------------------------------
// Basic linear-gradient syntax (valid when prefixed or unprefixed):
"-webkit-linear-gradient(red, green, blue)",
@ -677,6 +745,99 @@ if (IsCSSPropertyPrefEnabled("layout.css.prefixes.webkit")) {
);
invalidGradientAndElementValues.push(
// 2008 GRADIENTS: -webkit-gradient()
// https://www.webkit.org/blog/175/introducing-css-gradients/
// ----------------------------------
// Mostly-empty expressions (missing most required pieces):
"-webkit-gradient()",
"-webkit-gradient( )",
"-webkit-gradient(,)",
"-webkit-gradient(bogus)",
"-webkit-gradient(linear)",
"-webkit-gradient(linear,)",
"-webkit-gradient(,linear)",
"-webkit-gradient(radial)",
"-webkit-gradient(radial,)",
// linear w/ partial/missing <point> expression(s)
"-webkit-gradient(linear, 1)", // Incomplete <point>
"-webkit-gradient(linear, left)", // Incomplete <point>
"-webkit-gradient(linear, center)", // Incomplete <point>
"-webkit-gradient(linear, top)", // Incomplete <point>
"-webkit-gradient(linear, 5%)", // Incomplete <point>
"-webkit-gradient(linear, 1 2)", // Missing 2nd <point>
"-webkit-gradient(linear, 1, 3)", // 2 incomplete <point>s
"-webkit-gradient(linear, 1, 3 4)", // Incomplete 1st <point>
"-webkit-gradient(linear, 1 2, 3)", // Incomplete 2nd <point>
"-webkit-gradient(linear, 1 2, 3, 4)", // Comma inside <point>
"-webkit-gradient(linear, 1, 2, 3 4)", // Comma inside <point>
"-webkit-gradient(linear, 1, 2, 3, 4)", // Comma inside <point>
// linear w/ invalid units in <point> expression
"-webkit-gradient(linear, 1px 2, 3 4)",
"-webkit-gradient(linear, 1 2, 3 4px)",
"-webkit-gradient(linear, 1px 2px, 3px 4px)",
"-webkit-gradient(linear, calc(1) 2, 3 4)",
"-webkit-gradient(linear, 1 2em, 3 4)",
// linear w/ <radius> (only valid for radial)
"-webkit-gradient(linear, 1 2, 8, 3 4, 9)",
// linear w/ out-of-order position keywords in <point> expression
// (horizontal keyword is supposed to come first, for "x" coord)
"-webkit-gradient(linear, 0 0, top right)",
"-webkit-gradient(linear, bottom center, 0 0)",
"-webkit-gradient(linear, top bottom, 0 0)",
"-webkit-gradient(linear, bottom top, 0 0)",
"-webkit-gradient(linear, bottom top, 0 0)",
// linear w/ trailing comma (which implies missing color-stops):
"-webkit-gradient(linear, 1 2, 3 4,)",
// linear w/ invalid color values:
"-webkit-gradient(linear, 1 2, 3 4, from(invalidcolorname))",
"-webkit-gradient(linear, 1 2, 3 4, from(inherit))",
"-webkit-gradient(linear, 1 2, 3 4, from(initial))",
"-webkit-gradient(linear, 1 2, 3 4, from(currentColor))",
"-webkit-gradient(linear, 1 2, 3 4, from(00ff00))",
"-webkit-gradient(linear, 1 2, 3 4, from(##00ff00))",
"-webkit-gradient(linear, 1 2, 3 4, from(#00fff))", // wrong num hex digits
"-webkit-gradient(linear, 1 2, 3 4, from(xyz(0,0,0)))", // bogus color func
"-webkit-gradient(linear, 1 2, 3 4, from(rgb(100, 100.5, 30)))", // fraction
// linear w/ color stops that have comma issues
"-webkit-gradient(linear, 1 2, 3 4 from(lime))",
"-webkit-gradient(linear, 1 2, 3 4, from(lime,))",
"-webkit-gradient(linear, 1 2, 3 4, from(lime),)",
"-webkit-gradient(linear, 1 2, 3 4, from(lime) to(blue))",
"-webkit-gradient(linear, 1 2, 3 4, from(lime),, to(blue))",
"-webkit-gradient(linear, 1 2, 3 4, from(rbg(0, 0, 0,)))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(0 lime))",
"-webkit-gradient(linear, 1 2, 3 4, color-stop(0,, lime))",
// radial w/ broken <point>/radius expression(s)
"-webkit-gradient(radial, 1)", // Incomplete <point>
"-webkit-gradient(radial, 1 2)", // Missing radius + 2nd <point>
"-webkit-gradient(radial, 1 2, 8)", // Missing 2nd <point>
"-webkit-gradient(radial, 1 2, 8, 3)", // Incomplete 2nd <point>
"-webkit-gradient(radial, 1 2, 8, 3 4)", // Missing 2nd radius
"-webkit-gradient(radial, 1 2, 3 4, 9)", // Missing 1st radius
// radial w/ incorrect units on radius (invalid; expecting <number>)
"-webkit-gradient(radial, 1 2, 8%, 3 4, 9)",
"-webkit-gradient(radial, 1 2, 8px, 3 4, 9)",
"-webkit-gradient(radial, 1 2, calc(8), 3 4, 9)",
"-webkit-gradient(radial, 1 2, 8em, 3 4, 9)",
"-webkit-gradient(radial, 1 2, top, 3 4, 9)",
// radial w/ trailing comma (which implies missing color-stops):
"-webkit-gradient(linear, 1 2, 8, 3 4, 9,)",
// radial w/ invalid color value (mostly leaning on 'linear' test above):
"-webkit-gradient(radial, 1 2, 8, 3 4, 9, from(invalidcolorname))",
// 2011 GRADIENTS: -webkit-linear-gradient(), -webkit-radial -gradient()
// ---------------------------------------------------------------------
// Syntax that's invalid for all types of gradients:
// * empty gradient expressions:
"-webkit-linear-gradient()",