diff --git a/devtools/client/shared/css-angle.js b/devtools/client/shared/css-angle.js index f3612ed8432b..8dc5e62d9328 100644 --- a/devtools/client/shared/css-angle.js +++ b/devtools/client/shared/css-angle.js @@ -4,8 +4,6 @@ "use strict"; -const {CSS_ANGLEUNIT} = require("devtools/shared/css/properties-db"); - const SPECIALVALUES = new Set([ "initial", "inherit", @@ -41,7 +39,12 @@ module.exports.angleUtils = { classifyAngle: classifyAngle }; -CssAngle.ANGLEUNIT = CSS_ANGLEUNIT; +CssAngle.ANGLEUNIT = { + "deg": "deg", + "rad": "rad", + "grad": "grad", + "turn": "turn" +}; CssAngle.prototype = { _angleUnit: null, diff --git a/devtools/shared/css/color.js b/devtools/shared/css/color.js index 77a0045cc176..1d39e385f82b 100644 --- a/devtools/shared/css/color.js +++ b/devtools/shared/css/color.js @@ -6,9 +6,6 @@ const Services = require("Services"); -const {CSS_ANGLEUNIT} = require("devtools/shared/css/properties-db"); -const {getAngleValueInDegrees} = require("devtools/shared/css/parsing-utils"); - const {getCSSLexer} = require("devtools/shared/css/lexer"); const {cssColors} = require("devtools/shared/css/color-db"); @@ -653,174 +650,15 @@ function clamp(value, min, max) { * null at EOF. */ function getToken(lexer) { - if (lexer._hasPushBackToken) { - lexer._hasPushBackToken = false; - return lexer._currentToken; - } - while (true) { let token = lexer.nextToken(); if (!token || (token.tokenType !== "comment" && token.tokenType !== "whitespace")) { - lexer._currentToken = token; return token; } } } -/** - * A helper function to put a token back to lexer for the next call of - * getToken(). - * - * @param {CSSLexer} lexer The lexer - */ -function unGetToken(lexer) { - if (lexer._hasPushBackToken) { - throw new Error("Double pushback."); - } - lexer._hasPushBackToken = true; -} - -/** - * A helper function that checks if the next token matches symbol. - * If so, reads the token and returns true. If not, pushes the - * token back and returns false. - * - * @param {CSSLexer} lexer The lexer. - * @param {String} symbol The symbol. - * @return {Boolean} The expect symbol is parsed or not. - */ -function expectSymbol(lexer, symbol) { - let token = getToken(lexer); - if (!token) { - return false; - } - - if (token.tokenType !== "symbol" || token.text !== symbol) { - unGetToken(lexer); - return false; - } - - return true; -} - -/** - * Parse a or a 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]. - * - * @param {CSSLexer} lexer The lexer. - * @param {Boolean} isPercentage The color component is or not. - * @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) { - let token = getToken(lexer); - - if (!token) { - return false; - } - - if (isPercentage) { - if (token.tokenType !== "percentage") { - return false; - } - } else if (token.tokenType !== "number") { - return false; - } - - let colorComponent = 0; - if (isPercentage) { - colorComponent = clamp(token.number, 0, 1); - } else { - colorComponent = clamp(token.number, 0, 255); - } - - if (separator !== "" && !expectSymbol(lexer, separator)) { - return false; - } - - colorArray.push(colorComponent); - - return true; -} - -/** - * Parse an optional [ separator ] expression, followed by a - * close-parenthesis, at the end of a css color function (e.g. rgba() or hsla()). - * If this function simply encounters a close-parenthesis (without the - * [ separator ]), it will still succeed. Then put a fully-opaque - * alpha value into the colorArray. The range of output alpha value is [0, 1]. - * - * @param {CSSLexer} lexer The lexer - * @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 parseColorOpacityAndCloseParen(lexer, separator, colorArray) { - // The optional [separator ] was omitted, so set the opacity - // to a fully-opaque value '1.0' and return success. - if (expectSymbol(lexer, ")")) { - colorArray.push(1); - return true; - } - - if (!expectSymbol(lexer, separator)) { - return false; - } - - let token = getToken(lexer); - if (!token) { - return false; - } - - // or - if (token.tokenType !== "number" && token.tokenType !== "percentage") { - return false; - } - - if (!expectSymbol(lexer, ")")) { - return false; - } - - colorArray.push(clamp(token.number, 0, 1)); - - return true; -} - -/** - * Parse a hue value. - * = | - * - * @param {CSSLexer} lexer The lexer - * @param {Array} colorArray [out] The parsed color component will push into this array. - * @return {Boolean} Return false on error. - */ -function parseHue(lexer, colorArray) { - let token = getToken(lexer); - - if (!token) { - return false; - } - - let val = 0; - if (token.tokenType === "number") { - val = token.number; - } else if (token.tokenType === "dimension" && token.text in CSS_ANGLEUNIT) { - val = getAngleValueInDegrees(token.number, token.text); - } else { - return false; - } - - val = val / 360.0; - colorArray.push(val - Math.floor(val)); - - return true; -} - /** * A helper function to examine a token and ensure it is a comma. * Then fetch and return the next token. Returns null if the @@ -839,96 +677,72 @@ function requireComma(lexer, token) { } /** - * A helper function to parse the color components of hsl()/hsla() function. - * hsl() and hsla() are now aliases. + * A helper function to parse the first three arguments to hsl() + * or hsla(). * * @param {CSSLexer} lexer The lexer - * @return {Array} An array of the form [r,g,b,a]; or null on error. + * @return {Array} An array of the form [r,g,b]; or null on error. */ function parseHsl(lexer) { - // comma-less expression: - // hsl() = hsl( [ / ]? ) - // the expression with comma: - // hsl() = hsl( , , , ? ) - // - // = | - // = | + let vals = []; - const commaSeparator = ","; - let hsl = []; - let a = []; - - // Parse hue. - if (!parseHue(lexer, hsl)) { + let token = getToken(lexer); + if (!token || token.tokenType !== "number") { return null; } - // Look for a comma separator after "hue" component to determine if the - // expression is comma-less or not. - let hasComma = expectSymbol(lexer, commaSeparator); + let val = token.number / 360.0; + vals.push(val - Math.floor(val)); - // Parse saturation, lightness and opacity. - // The saturation and lightness are , so reuse the - // 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) && - parseColorOpacityAndCloseParen(lexer, separatorBeforeAlpha, a)) { - return [...hslToRGB(hsl), ...a]; + for (let i = 0; i < 2; ++i) { + token = requireComma(lexer, getToken(lexer)); + if (!token || token.tokenType !== "percentage") { + return null; + } + vals.push(clamp(token.number, 0, 1)); } - return null; + return hslToRGB(vals); } /** - * A helper function to parse the color arguments of rgb()/rgba() function. - * rgb() and rgba() now are aliases. + * A helper function to parse the first three arguments to rgb() + * or rgba(). * - * @param {CSSLexer} lexer The lexer. - * @return {Array} An array of the form [r,g,b,a]; or null on error. + * @param {CSSLexer} lexer The lexer + * @return {Array} An array of the form [r,g,b]; or null on error. */ function parseRgb(lexer) { - // comma-less expression: - // rgb() = rgb( component{3} [ / ]? ) - // the expression with comma: - // rgb() = rgb( component#{3} , ? ) - // - // component = | - // = | - - const commaSeparator = ","; - let rgba = []; - - let token = getToken(lexer); - if (token.tokenType !== "percentage" && token.tokenType !== "number") { - return null; - } - unGetToken(lexer); - let isPercentage = token.tokenType === "percentage"; - - // Parse R. - if (!parseColorComponent(lexer, isPercentage, "", rgba)) { - return null; - } - let hasComma = expectSymbol(lexer, commaSeparator); - - // Parse G, B and A. - // 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) && - parseColorOpacityAndCloseParen(lexer, separatorBeforeAlpha, rgba)) { - if (isPercentage) { - rgba[0] = Math.round(255 * rgba[0]); - rgba[1] = Math.round(255 * rgba[1]); - rgba[2] = Math.round(255 * rgba[2]); + let isPercentage = false; + let vals = []; + for (let i = 0; i < 3; ++i) { + let token = getToken(lexer); + if (i > 0) { + token = requireComma(lexer, token); + } + if (!token) { + return null; } - return rgba; - } - return null; + /* Either all parameters are integers, or all are percentages, so + check the first one to see. */ + if (i === 0 && token.tokenType === "percentage") { + isPercentage = true; + } + + if (isPercentage) { + if (token.tokenType !== "percentage") { + return null; + } + vals.push(Math.round(255 * clamp(token.number, 0, 1))); + } else { + if (token.tokenType !== "number" || !token.isInteger) { + return null; + } + vals.push(clamp(token.number, 0, 255)); + } + } + return vals; } /** @@ -972,11 +786,28 @@ function colorToRGBA(name) { } let hsl = func.text === "hsl" || func.text === "hsla"; + let alpha = func.text === "rgba" || func.text === "hsla"; let vals = hsl ? parseHsl(lexer) : parseRgb(lexer); if (!vals) { return null; } + + if (alpha) { + let token = requireComma(lexer, getToken(lexer)); + if (!token || token.tokenType !== "number") { + return null; + } + vals.push(clamp(token.number, 0, 1)); + } else { + vals.push(1); + } + + let parenToken = getToken(lexer); + if (!parenToken || parenToken.tokenType !== "symbol" || + parenToken.text !== ")") { + return null; + } if (getToken(lexer) !== null) { return null; } diff --git a/devtools/shared/css/parsing-utils.js b/devtools/shared/css/parsing-utils.js index 608cad383538..563aa95b1182 100644 --- a/devtools/shared/css/parsing-utils.js +++ b/devtools/shared/css/parsing-utils.js @@ -14,8 +14,6 @@ "use strict"; -const {CSS_ANGLEUNIT} = require("devtools/shared/css/properties-db"); - const promise = require("promise"); const {getCSSLexer} = require("devtools/shared/css/lexer"); const {Task} = require("devtools/shared/task"); @@ -1135,30 +1133,6 @@ function parseSingleValue(isCssPropertyKnown, value) { }; } -/** - * Convert an angle value to degree. - * - * @param {Number} angleValue The angle value. - * @param {CSS_ANGLEUNIT} angleUnit The angleValue's angle unit. - * @return {Number} An angle value in degree. - */ -function getAngleValueInDegrees(angleValue, angleUnit) { - switch (angleUnit) { - case CSS_ANGLEUNIT.deg: - return angleValue; - case CSS_ANGLEUNIT.grad: - return angleValue * 0.9; - case CSS_ANGLEUNIT.rad: - return angleValue * 180 / Math.PI; - case CSS_ANGLEUNIT.turn: - return angleValue * 360; - default: - throw new Error("No matched angle unit."); - } - - return 0; -} - exports.cssTokenizer = cssTokenizer; exports.cssTokenizerWithLineColumn = cssTokenizerWithLineColumn; exports.escapeCSSComment = escapeCSSComment; @@ -1170,4 +1144,3 @@ exports._parseCommentDeclarations = parseCommentDeclarations; exports.RuleRewriter = RuleRewriter; exports.parsePseudoClassesAndAttributes = parsePseudoClassesAndAttributes; exports.parseSingleValue = parseSingleValue; -exports.getAngleValueInDegrees = getAngleValueInDegrees; diff --git a/devtools/shared/css/properties-db.js b/devtools/shared/css/properties-db.js index 32454482a9ff..057a56cbfa0c 100644 --- a/devtools/shared/css/properties-db.js +++ b/devtools/shared/css/properties-db.js @@ -42,16 +42,6 @@ exports.CSS_TYPES = { "URL": 11, }; -/** - * All CSS types that properties can support. This list can be manually edited. - */ -exports.CSS_ANGLEUNIT = { - "deg": "deg", - "rad": "rad", - "grad": "grad", - "turn": "turn" -}; - /** * All cubic-bezier CSS timing-function names. This list can be manually edited. */