Bug 1302787 - Add a mode for css-color-4 color parser to accept the old style syntax only. r=tromey

--HG--
extra : rebase_source : 089e0201bfa832848b0230a44b19753a1b2206fd
extra : histedit_source : 84e737082baf5c52e891187d9073202235f35e93
This commit is contained in:
JerryShih 2016-10-16 03:15:37 +08:00
Родитель 995689ff27
Коммит b1c8f17089
1 изменённых файлов: 162 добавлений и 23 удалений

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

@ -704,36 +704,54 @@ function expectSymbol(lexer, symbol) {
return true;
}
const COLOR_COMPONENT_TYPE = {
"integer": "integer",
"number": "number",
"percentage": "percentage",
};
/**
* Parse a <number> or a <percentage> color component. If |separator| is
* provided (not an empty string ""), this function will also attempt to parse that
* character after parsing the color component. The range of output component
* value is [0, 1] if the "isPercentage" is true. Otherwise, the range is
* [0, 255].
* Parse a <integer> or a <number> or a <percentage> color component. If
* |separator| is provided (not an empty string ""), this function will also
* attempt to parse that character after parsing the color component. The range
* of output component value is [0, 1] if the component type is percentage.
* Otherwise, the range is [0, 255].
*
* @param {CSSLexer} lexer The lexer.
* @param {Boolean} isPercentage The color component is <percentage> or not.
* @param {COLOR_COMPONENT_TYPE} type The color component type.
* @param {String} separator The separator.
* @param {Array} colorArray [out] The parsed color component will push into this array.
* @return {Boolean} Return false on error.
*/
function parseColorComponent(lexer, isPercentage, separator, colorArray) {
function parseColorComponent(lexer, type, separator, colorArray) {
let token = getToken(lexer);
if (!token) {
return false;
}
if (isPercentage) {
if (token.tokenType !== "percentage") {
return false;
}
} else if (token.tokenType !== "number") {
return false;
switch (type) {
case COLOR_COMPONENT_TYPE.integer:
if (token.tokenType !== "number" || !token.isInteger) {
return false;
}
break;
case COLOR_COMPONENT_TYPE.number:
if (token.tokenType !== "number") {
return false;
}
break;
case COLOR_COMPONENT_TYPE.percentage:
if (token.tokenType !== "percentage") {
return false;
}
break;
default:
throw new Error("Invalid color component type.");
}
let colorComponent = 0;
if (isPercentage) {
if (type === COLOR_COMPONENT_TYPE.percentage) {
colorComponent = clamp(token.number, 0, 1);
} else {
colorComponent = clamp(token.number, 0, 255);
@ -855,8 +873,9 @@ function parseHsl(lexer) {
// version of parseColorComponent function for them. No need to check the
// separator after 'lightness'. It will be checked in opacity value parsing.
let separatorBeforeAlpha = hasComma ? commaSeparator : "/";
if (parseColorComponent(lexer, true, hasComma ? commaSeparator : "", hsl) &&
parseColorComponent(lexer, true, "", hsl) &&
if (parseColorComponent(lexer, COLOR_COMPONENT_TYPE.percentage,
hasComma ? commaSeparator : "", hsl) &&
parseColorComponent(lexer, COLOR_COMPONENT_TYPE.percentage, "", hsl) &&
parseColorOpacityAndCloseParen(lexer, separatorBeforeAlpha, a)) {
return [...hslToRGB(hsl), ...a];
}
@ -864,6 +883,59 @@ function parseHsl(lexer) {
return null;
}
/**
* A helper function to parse the color arguments of old style hsl()/hsla()
* function.
*
* @param {CSSLexer} lexer The lexer.
* @param {Boolean} hasAlpha The color function has alpha component or not.
* @return {Array} An array of the form [r,g,b,a]; or null on error.
*/
function parseOldStyleHsl(lexer, hasAlpha) {
// hsla() = hsla( <hue>, <saturation>, <lightness>, <alpha-value> )
// hsl() = hsl( <hue>, <saturation>, <lightness> )
//
// <hue> = <number>
// <alpha-value> = <number>
const commaSeparator = ",";
const closeParen = ")";
let hsl = [];
let a = [];
// Parse hue.
let token = getToken(lexer);
if (!token || token.tokenType !== "number") {
return null;
}
if (!expectSymbol(lexer, commaSeparator)) {
return null;
}
let val = token.number / 360.0;
hsl.push(val - Math.floor(val));
// Parse saturation, lightness and opacity.
// The saturation and lightness are <percentage>, so reuse the <percentage>
// version of parseColorComponent function for them. The opacity is <number>
if (hasAlpha) {
if (parseColorComponent(lexer, COLOR_COMPONENT_TYPE.percentage,
commaSeparator, hsl) &&
parseColorComponent(lexer, COLOR_COMPONENT_TYPE.percentage,
commaSeparator, hsl) &&
parseColorComponent(lexer, COLOR_COMPONENT_TYPE.number,
closeParen, a)) {
return [...hslToRGB(hsl), ...a];
}
} else if (parseColorComponent(lexer, COLOR_COMPONENT_TYPE.percentage,
commaSeparator, hsl) &&
parseColorComponent(lexer, COLOR_COMPONENT_TYPE.percentage,
closeParen, hsl)) {
return [...hslToRGB(hsl), 1];
}
return null;
}
/**
* A helper function to parse the color arguments of rgb()/rgba() function.
* rgb() and rgba() now are aliases.
@ -888,10 +960,12 @@ function parseRgb(lexer) {
return null;
}
unGetToken(lexer);
let isPercentage = token.tokenType === "percentage";
let type = (token.tokenType === "percentage") ?
COLOR_COMPONENT_TYPE.percentage :
COLOR_COMPONENT_TYPE.number;
// Parse R.
if (!parseColorComponent(lexer, isPercentage, "", rgba)) {
if (!parseColorComponent(lexer, type, "", rgba)) {
return null;
}
let hasComma = expectSymbol(lexer, commaSeparator);
@ -900,10 +974,10 @@ function parseRgb(lexer) {
// No need to check the separator after 'B'. It will be checked in 'A' values
// parsing.
let separatorBeforeAlpha = hasComma ? commaSeparator : "/";
if (parseColorComponent(lexer, isPercentage, hasComma ? commaSeparator : "", rgba) &&
parseColorComponent(lexer, isPercentage, "", rgba) &&
if (parseColorComponent(lexer, type, hasComma ? commaSeparator : "", rgba) &&
parseColorComponent(lexer, type, "", rgba) &&
parseColorOpacityAndCloseParen(lexer, separatorBeforeAlpha, rgba)) {
if (isPercentage) {
if (type === COLOR_COMPONENT_TYPE.percentage) {
rgba[0] = Math.round(255 * rgba[0]);
rgba[1] = Math.round(255 * rgba[1]);
rgba[2] = Math.round(255 * rgba[2]);
@ -914,15 +988,73 @@ function parseRgb(lexer) {
return null;
}
/**
* A helper function to parse the color arguments of old style rgb()/rgba()
* function.
*
* @param {CSSLexer} lexer The lexer.
* @param {Boolean} hasAlpha The color function has alpha component or not.
* @return {Array} An array of the form [r,g,b,a]; or null on error.
*/
function parseOldStyleRgb(lexer, hasAlpha) {
// rgba() = rgba( component#{3} , <alpha-value> )
// rgb() = rgb( component#{3} )
//
// component = <integer> | <percentage>
// <alpha-value> = <number>
const commaSeparator = ",";
const closeParen = ")";
let rgba = [];
let token = getToken(lexer);
if (token.tokenType !== "percentage" &&
(token.tokenType !== "number" || !token.isInteger)) {
return null;
}
unGetToken(lexer);
let type = (token.tokenType === "percentage") ?
COLOR_COMPONENT_TYPE.percentage :
COLOR_COMPONENT_TYPE.integer;
// Parse R. G, B and A.
if (hasAlpha) {
if (!parseColorComponent(lexer, type, commaSeparator, rgba) ||
!parseColorComponent(lexer, type, commaSeparator, rgba) ||
!parseColorComponent(lexer, type, commaSeparator, rgba) ||
!parseColorComponent(lexer, COLOR_COMPONENT_TYPE.number,
closeParen, rgba)) {
return null;
}
} else if (!parseColorComponent(lexer, type, commaSeparator, rgba) ||
!parseColorComponent(lexer, type, commaSeparator, rgba) ||
!parseColorComponent(lexer, type, closeParen, rgba)) {
return null;
}
if (type === COLOR_COMPONENT_TYPE.percentage) {
rgba[0] = Math.round(255 * rgba[0]);
rgba[1] = Math.round(255 * rgba[1]);
rgba[2] = Math.round(255 * rgba[2]);
}
if (!hasAlpha) {
rgba.push(1);
}
return rgba;
}
/**
* Convert a string representing a color to an object holding the
* color's components. Any valid CSS color form can be passed in.
*
* @param {String} name the color
* @param {Boolean} oldColorFunctionSyntax use old color function syntax or the
* css-color-4 syntax
* @return {Object} an object of the form {r, g, b, a}; or null if the
* name was not a valid color
*/
function colorToRGBA(name) {
function colorToRGBA(name, oldColorFunctionSyntax = true) {
name = name.trim().toLowerCase();
if (name in cssColors) {
@ -956,7 +1088,14 @@ function colorToRGBA(name) {
let hsl = func.text === "hsl" || func.text === "hsla";
let vals = hsl ? parseHsl(lexer) : parseRgb(lexer);
let vals;
if (oldColorFunctionSyntax) {
let hasAlpha = (func.text === "rgba" || func.text === "hsla");
vals = hsl ? parseOldStyleHsl(lexer, hasAlpha) : parseOldStyleRgb(lexer, hasAlpha);
} else {
vals = hsl ? parseHsl(lexer) : parseRgb(lexer);
}
if (!vals) {
return null;
}