Bug 1310681 - pass css-color-4 color function supporting info to devtool css OutputParser and SwatchColorPickerTooltip. r?tromey draft
authorJerryShih <hshih@mozilla.com>
Fri, 21 Oct 2016 14:59:07 -0400
changeset 453221 15741fa813f7
parent 453220 6ffecc937a89
child 453222 4e2b3d62895f
push id39601
push userbmo:hshih@mozilla.com
push dateFri, 23 Dec 2016 01:20:43 +0000
reviewerstromey
bugs1310681
milestone53.0a1
Bug 1310681 - pass css-color-4 color function supporting info to devtool css OutputParser and SwatchColorPickerTooltip. r?tromey Pass css-color-4 supporting status from css-property db to OutputParser and SwatchColorPickerTooltip. MozReview-Commit-ID: N1ffWOlf9f
devtools/client/inspector/shared/tooltips-overlay.js
devtools/client/shared/output-parser.js
devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
devtools/shared/css/color.js
--- a/devtools/client/inspector/shared/tooltips-overlay.js
+++ b/devtools/client/inspector/shared/tooltips-overlay.js
@@ -83,17 +83,19 @@ TooltipsOverlay.prototype = {
     this.previewTooltip.startTogglingOnHover(this.view.element,
       this._onPreviewTooltipTargetHover.bind(this));
 
     // MDN CSS help tooltip
     this.cssDocs = new CssDocsTooltip(toolbox.doc);
 
     if (this.isRuleView) {
       // Color picker tooltip
-      this.colorPicker = new SwatchColorPickerTooltip(toolbox.doc, this.view.inspector);
+      this.colorPicker = new SwatchColorPickerTooltip(toolbox.doc,
+                                                      this.view.inspector,
+                                                      this._cssProperties);
       // Cubic bezier tooltip
       this.cubicBezier = new SwatchCubicBezierTooltip(toolbox.doc);
       // Filter editor tooltip
       this.filterEditor = new SwatchFilterTooltip(toolbox.doc,
         this._cssProperties.getValidityChecker(this.view.inspector.panelDoc));
     }
 
     this._isStarted = true;
--- a/devtools/client/shared/output-parser.js
+++ b/devtools/client/shared/output-parser.js
@@ -35,26 +35,31 @@ const CSS_GRID_ENABLED_PREF = "layout.cs
  *
  * @param {Document} document Used to create DOM nodes.
  * @param {Function} supportsTypes - A function that returns a boolean when asked if a css
  *                   property name supports a given css type.
  *                   The function is executed like supportsType("color", CSS_TYPES.COLOR)
  *                   where CSS_TYPES is defined in devtools/shared/css/properties-db.js
  * @param {Function} isValidOnClient - A function that checks if a css property
  *                   name/value combo is valid.
+ * @param {Function} supportsCssColor4ColorFunction - A function for checking
+ *                   the supporting of css-color-4 color function.
  */
-function OutputParser(document, {supportsType, isValidOnClient}) {
+function OutputParser(document,
+                      {supportsType, isValidOnClient, supportsCssColor4ColorFunction}) {
   this.parsed = [];
   this.doc = document;
   this.supportsType = supportsType;
   this.isValidOnClient = isValidOnClient;
   this.colorSwatches = new WeakMap();
   this.angleSwatches = new WeakMap();
   this._onColorSwatchMouseDown = this._onColorSwatchMouseDown.bind(this);
   this._onAngleSwatchMouseDown = this._onAngleSwatchMouseDown.bind(this);
+
+  this.cssColor4 = supportsCssColor4ColorFunction();
 }
 
 OutputParser.prototype = {
   /**
    * Parse a CSS property value given a property name.
    *
    * @param  {String} name
    *         CSS Property Name
@@ -181,47 +186,49 @@ OutputParser.prototype = {
             }
             ++parenDepth;
           } else {
             let functionText = this._collectFunctionText(token, text,
                                                          tokenStream);
 
             if (options.expectCubicBezier && token.text === "cubic-bezier") {
               this._appendCubicBezier(functionText, options);
-            } else if (colorOK() && colorUtils.isValidCSSColor(functionText)) {
+            } else if (colorOK() &&
+                       colorUtils.isValidCSSColor(functionText, this.cssColor4)) {
               this._appendColor(functionText, options);
             } else {
               this._appendTextNode(functionText);
             }
           }
           break;
         }
 
         case "ident":
           if (options.expectCubicBezier &&
               BEZIER_KEYWORDS.indexOf(token.text) >= 0) {
             this._appendCubicBezier(token.text, options);
           } else if (Services.prefs.getBoolPref(CSS_GRID_ENABLED_PREF) &&
                      options.expectDisplay && token.text === "grid" &&
                      text === token.text) {
             this._appendGrid(token.text, options);
-          } else if (colorOK() && colorUtils.isValidCSSColor(token.text)) {
+          } else if (colorOK() &&
+                     colorUtils.isValidCSSColor(token.text, this.cssColor4)) {
             this._appendColor(token.text, options);
           } else if (angleOK(token.text)) {
             this._appendAngle(token.text, options);
           } else {
             this._appendTextNode(text.substring(token.startOffset,
                                                 token.endOffset));
           }
           break;
 
         case "id":
         case "hash": {
           let original = text.substring(token.startOffset, token.endOffset);
-          if (colorOK() && colorUtils.isValidCSSColor(original)) {
+          if (colorOK() && colorUtils.isValidCSSColor(original, this.cssColor4)) {
             this._appendColor(original, options);
           } else {
             this._appendTextNode(original);
           }
           break;
         }
         case "dimension":
           let value = text.substring(token.startOffset, token.endOffset);
@@ -387,17 +394,17 @@ OutputParser.prototype = {
    *
    * @param  {String} color
    *         Color to append
    * @param  {Object} [options]
    *         Options object. For valid options and default values see
    *         _mergeOptions().
    */
   _appendColor: function (color, options = {}) {
-    let colorObj = new colorUtils.CssColor(color);
+    let colorObj = new colorUtils.CssColor(color, this.cssColor4);
 
     if (this._isValidColor(colorObj)) {
       let container = this._createNode("span", {
         "data-color": color
       });
 
       if (options.colorSwatchClass) {
         let swatch = this._createNode("span", {
--- a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
+++ b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
@@ -23,28 +23,33 @@ const XHTML_NS = "http://www.w3.org/1999
  * color picker.
  *
  * @param {Document} document
  *        The document to attach the SwatchColorPickerTooltip. This is either the toolbox
  *        document if the tooltip is a popup tooltip or the panel's document if it is an
  *        inline editor.
  * @param {InspectorPanel} inspector
  *        The inspector panel, needed for the eyedropper.
+ * @param {Function} supportsCssColor4ColorFunction
+ *        A function for checking the supporting of css-color-4 color function.
  */
-function SwatchColorPickerTooltip(document, inspector) {
+function SwatchColorPickerTooltip(document,
+                                  inspector,
+                                  {supportsCssColor4ColorFunction}) {
   let stylesheet = "chrome://devtools/content/shared/widgets/spectrum.css";
   SwatchBasedEditorTooltip.call(this, document, stylesheet);
 
   this.inspector = inspector;
 
   // Creating a spectrum instance. this.spectrum will always be a promise that
   // resolves to the spectrum instance
   this.spectrum = this.setColorPickerContent([0, 0, 0, 1]);
   this._onSpectrumColorChange = this._onSpectrumColorChange.bind(this);
   this._openEyeDropper = this._openEyeDropper.bind(this);
+  this.cssColor4 = supportsCssColor4ColorFunction();
 }
 
 SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.prototype, {
   /**
    * Fill the tooltip with a new instance of the spectrum color picker widget
    * initialized with the given color, and return the instance of spectrum
    */
   setColorPickerContent: function (color) {
@@ -152,24 +157,24 @@ SwatchColorPickerTooltip.prototype = Her
   },
 
   _onEyeDropperDone: function () {
     this.eyedropperOpen = false;
     this.activeSwatch = null;
   },
 
   _colorToRgba: function (color) {
-    color = new colorUtils.CssColor(color);
+    color = new colorUtils.CssColor(color, this.cssColor4);
     let rgba = color._getRGBATuple();
     return [rgba.r, rgba.g, rgba.b, rgba.a];
   },
 
   _toDefaultType: function (color) {
     let colorObj = new colorUtils.CssColor(color);
-    colorObj.setAuthoredUnitFromColor(this._originalColor);
+    colorObj.setAuthoredUnitFromColor(this._originalColor, this.cssColor4);
     return colorObj.toString();
   },
 
   destroy: function () {
     SwatchBasedEditorTooltip.prototype.destroy.call(this);
     this.inspector = null;
     this.currentSwatchColor = null;
     this.spectrum.off("changed", this._onSpectrumColorChange);
--- a/devtools/shared/css/color.js
+++ b/devtools/shared/css/color.js
@@ -23,16 +23,20 @@ const SPECIALVALUES = new Set([
 ]);
 
 /**
  * This module is used to convert between various color types.
  *
  * Usage:
  *   let {colorUtils} = require("devtools/shared/css/color");
  *   let color = new colorUtils.CssColor("red");
+ *   // In order to support css-color-4 color function, pass true to the
+ *   // second argument.
+ *   // e.g.
+ *   //   let color = new colorUtils.CssColor("red", true);
  *
  *   color.authored === "red"
  *   color.hasAlpha === false
  *   color.valid === true
  *   color.transparent === false // transparent has a special status.
  *   color.name === "red"        // returns hex when no name available.
  *   color.hex === "#f00"        // returns shortHex when available else returns
  *                                  longHex. If alpha channel is present then we
@@ -53,18 +57,19 @@ const SPECIALVALUES = new Set([
  *   color.toString() === "#f00"; // Outputs the color type determined in the
  *                                   COLOR_UNIT_PREF constant (above).
  *   // Color objects can be reused
  *   color.newColor("green") === "#0f0"; // true
  *
  *   Valid values for COLOR_UNIT_PREF are contained in CssColor.COLORUNIT.
  */
 
-function CssColor(colorValue) {
+function CssColor(colorValue, supportsCssColor4ColorFunction = false) {
   this.newColor(colorValue);
+  this.cssColor4 = supportsCssColor4ColorFunction;
 }
 
 module.exports.colorUtils = {
   CssColor: CssColor,
   rgbToHsl: rgbToHsl,
   setAlpha: setAlpha,
   classifyColor: classifyColor,
   rgbToColorName: rgbToColorName,
@@ -87,16 +92,19 @@ CssColor.prototype = {
   _colorUnit: null,
   _colorUnitUppercase: false,
 
   // The value as-authored.
   authored: null,
   // A lower-cased copy of |authored|.
   lowerCased: null,
 
+  // Whether the value should be parsed using css-color-4 rules.
+  cssColor4: false,
+
   _setColorUnitUppercase: function (color) {
     // Specifically exclude the case where the color is
     // case-insensitive.  This makes it so that "#000" isn't
     // considered "upper case" for the purposes of color cycling.
     this._colorUnitUppercase = (color === color.toUpperCase()) &&
       (color !== color.toLowerCase());
   },
 
@@ -131,17 +139,17 @@ CssColor.prototype = {
   get hasAlpha() {
     if (!this.valid) {
       return false;
     }
     return this._getRGBATuple().a !== 1;
   },
 
   get valid() {
-    return isValidCSSColor(this.authored);
+    return isValidCSSColor(this.authored, this.cssColor4);
   },
 
   /**
    * Return true for all transparent values e.g. rgba(0, 0, 0, 0).
    */
   get transparent() {
     try {
       let tuple = this._getRGBATuple();
@@ -388,17 +396,17 @@ CssColor.prototype = {
     return color;
   },
 
   /**
    * Returns a RGBA 4-Tuple representation of a color or transparent as
    * appropriate.
    */
   _getRGBATuple: function () {
-    let tuple = colorToRGBA(this.authored);
+    let tuple = colorToRGBA(this.authored, this.cssColor4);
 
     tuple.a = parseFloat(tuple.a.toFixed(1));
 
     return tuple;
   },
 
   _hsl: function (maybeAlpha) {
     if (this.lowerCased.startsWith("hsl(") && maybeAlpha === undefined) {
@@ -476,21 +484,23 @@ function roundTo(number, digits) {
  * Takes a color value of any type (hex, hsl, hsla, rgb, rgba)
  * and an alpha value to generate an rgba string with the correct
  * alpha value.
  *
  * @param  {String} colorValue
  *         Color in the form of hex, hsl, hsla, rgb, rgba.
  * @param  {Number} alpha
  *         Alpha value for the color, between 0 and 1.
+ * @param  {Boolean} useCssColor4ColorFunction
+ *         use css-color-4 color function or not.
  * @return {String}
  *         Converted color with `alpha` value in rgba form.
  */
-function setAlpha(colorValue, alpha) {
-  let color = new CssColor(colorValue);
+function setAlpha(colorValue, alpha, useCssColor4ColorFunction = false) {
+  let color = new CssColor(colorValue, useCssColor4ColorFunction);
 
   // Throw if the color supplied is not valid.
   if (!color.valid) {
     throw new Error("Invalid color.");
   }
 
   // If an invalid alpha valid, just set to 1.
   if (!(alpha >= 0 && alpha <= 1)) {
@@ -1044,22 +1054,21 @@ function parseOldStyleRgb(lexer, hasAlph
   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
+ * @param {Boolean} useCssColor4ColorFunction use css-color-4 color function or not.
  * @return {Object} an object of the form {r, g, b, a}; or null if the
  *         name was not a valid color
  */
-function colorToRGBA(name, oldColorFunctionSyntax = true) {
+function colorToRGBA(name, useCssColor4ColorFunction = false) {
   name = name.trim().toLowerCase();
 
   if (name in cssColors) {
     let result = cssColors[name];
     return {r: result[0], g: result[1], b: result[2], a: result[3]};
   } else if (name === "transparent") {
     return {r: 0, g: 0, b: 0, a: 0};
   } else if (name === "currentcolor") {
@@ -1084,17 +1093,17 @@ function colorToRGBA(name, oldColorFunct
   if (!func || func.tokenType !== "function" ||
       !expectedFunctions.includes(func.text)) {
     return null;
   }
 
   let hsl = func.text === "hsl" || func.text === "hsla";
 
   let vals;
-  if (oldColorFunctionSyntax) {
+  if (!useCssColor4ColorFunction) {
     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;
@@ -1105,13 +1114,14 @@ function colorToRGBA(name, oldColorFunct
 
   return {r: vals[0], g: vals[1], b: vals[2], a: vals[3]};
 }
 
 /**
  * Check whether a string names a valid CSS color.
  *
  * @param {String} name The string to check
+ * @param {Boolean} useCssColor4ColorFunction use css-color-4 color function or not.
  * @return {Boolean} True if the string is a CSS color name.
  */
-function isValidCSSColor(name) {
-  return colorToRGBA(name) !== null;
+function isValidCSSColor(name, useCssColor4ColorFunction = false) {
+  return colorToRGBA(name, useCssColor4ColorFunction) !== null;
 }