author | Nicolas Chevobbe <chevobbe.nicolas@gmail.com> |
Tue, 10 May 2016 13:54:08 +0200 | |
changeset 366198 | 6d24c9e1ef5118c30c8fc6dbe8e5d27da8635b6c |
parent 365987 | 45daaf6edeae80ec8c67da50fa1d31f4a1b1a454 |
child 520718 | 8cc0fff80f6e677bab910fb5cf61d6d7ac352ae8 |
push id | 17920 |
push user | chevobbe.nicolas@gmail.com |
push date | Thu, 12 May 2016 05:31:50 +0000 |
reviewers | miker |
bugs | 1271191 |
milestone | 49.0a1 |
--- a/devtools/client/inspector/rules/test/browser_rules_colorpicker-swatch-displayed.js +++ b/devtools/client/inspector/rules/test/browser_rules_colorpicker-swatch-displayed.js @@ -11,16 +11,34 @@ const TEST_URI = ` body { color: red; background-color: #ededed; background-image: url(chrome://global/skin/icons/warning-64.png); border: 2em solid rgba(120, 120, 120, .5); } * { color: blue; + background: linear-gradient( + to right, + #f00, + #f008, + #00ff00, + #00ff0080, + rgb(31,170,217), + rgba(31,170,217,.5), + hsl(5, 5%, 5%), + hsla(5, 5%, 5%, 0.25), + #F00, + #F008, + #00FF00, + #00FF0080, + RGB(31,170,217), + RGBA(31,170,217,.5), + HSL(5, 5%, 5%), + HSLA(5, 5%, 5%, 0.25)); box-shadow: inset 0 0 2px 20px red, inset 0 0 2px 40px blue; } </style> Testing the color picker tooltip! `; // Tests that properties in the rule-view contain color swatches. // Each entry in the test array should contain: @@ -29,16 +47,17 @@ const TEST_URI = ` // propertyName: the property to test // nb: the number of color swatches this property should have // } const TESTS = [ {selector: "body", propertyName: "color", nb: 1}, {selector: "body", propertyName: "background-color", nb: 1}, {selector: "body", propertyName: "border", nb: 1}, {selector: "*", propertyName: "color", nb: 1}, + {selector: "*", propertyName: "background", nb: 16}, {selector: "*", propertyName: "box-shadow", nb: 2}, ]; add_task(function* () { yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); let {view} = yield openRuleView(); for (let {selector, propertyName, nb} of TESTS) {
--- a/devtools/client/inspector/rules/test/browser_rules_cycle-color.js +++ b/devtools/client/inspector/rules/test/browser_rules_cycle-color.js @@ -8,26 +8,28 @@ const TEST_URI = ` <style type="text/css"> body { color: #f00; } span { color: blue; + border-color: #ff000080; } </style> <body><span>Test</span> cycling color types in the rule view!</body> `; add_task(function* () { yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); let {inspector, view} = yield openRuleView(); let container = getRuleViewProperty(view, "body", "color").valueSpan; yield checkColorCycling(container, view); + yield checkAlphaColorCycling(inspector, view); yield checkColorCyclingPersist(inspector, view); }); function* checkColorCycling(container, view) { let valueNode = container.querySelector(".ruleview-color"); let win = view.styleWindow; // Hex @@ -50,16 +52,41 @@ function* checkColorCycling(container, v comment: "Color displayed as an HSL value again." }]; for (let test of tests) { yield checkSwatchShiftClick(container, win, test.value, test.comment); } } +function* checkAlphaColorCycling(inspector, view) { + yield selectNode("span", inspector); + let container = getRuleViewProperty(view, "span", "border-color").valueSpan; + let valueNode = container.querySelector(".ruleview-color"); + let win = view.styleWindow; + + is(valueNode.textContent, "#ff000080", + "Color displayed as an alpha hex value."); + + let tests = [{ + value: "hsla(0, 100%, 50%, 0.5)", + comment: "Color displayed as an HSLa value." + }, { + value: "rgba(255, 0, 0, 0.5)", + comment: "Color displayed as an RGBa value." + }, { + value: "#ff000080", + comment: "Color displayed as an alpha hex value again." + }]; + + for (let test of tests) { + yield checkSwatchShiftClick(container, win, test.value, test.comment); + } +} + function* checkColorCyclingPersist(inspector, view) { yield selectNode("span", inspector); let container = getRuleViewProperty(view, "span", "color").valueSpan; let valueNode = container.querySelector(".ruleview-color"); let win = view.styleWindow; is(valueNode.textContent, "blue", "Color displayed as a color name.");
--- a/devtools/client/inspector/rules/test/browser_rules_edit-property-increments.js +++ b/devtools/client/inspector/rules/test/browser_rules_edit-property-increments.js @@ -26,16 +26,17 @@ add_task(function* () { yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); let {inspector, view} = yield openRuleView(); yield selectNode("#test", inspector); yield testMarginIncrements(view); yield testVariousUnitIncrements(view); yield testHexIncrements(view); + yield testAlphaHexIncrements(view); yield testRgbIncrements(view); yield testShorthandIncrements(view); yield testOddCases(view); yield testZeroValueIncrements(view); }); function* testMarginIncrements(view) { info("Testing keyboard increments on the margin property"); @@ -91,16 +92,33 @@ function* testHexIncrements(view) { 3: {start: "#CCCCCC", end: "#CDCCCC", selection: [1, 3]}, 4: {shift: true, start: "#CCCCCC", end: "#DCCCCC", selection: [1, 3]}, 5: {start: "#FFFFFF", end: "#FFFFFF", selectAll: true}, 6: {down: true, shift: true, start: "#000000", end: "#000000", selectAll: true} }); } +function* testAlphaHexIncrements(view) { + info("Testing keyboard increments with alpha hex colors"); + + let idRuleEditor = getRuleViewRuleEditor(view, 1); + let hexColorPropEditor = idRuleEditor.rule.textProps[2].editor; + + yield runIncrementTest(hexColorPropEditor, view, { + 1: {start: "#CCCCCCAA", end: "#CDCDCDAB", selectAll: true}, + 2: {shift: true, start: "#CCCCCCAA", end: "#DCDCDCBA", selectAll: true}, + 3: {start: "#CCCCCCAA", end: "#CDCCCCAA", selection: [1, 3]}, + 4: {shift: true, start: "#CCCCCCAA", end: "#DCCCCCAA", selection: [1, 3]}, + 5: {start: "#FFFFFFFF", end: "#FFFFFFFF", selectAll: true}, + 6: {down: true, shift: true, start: "#00000000", end: "#00000000", + selectAll: true} + }); +} + function* testRgbIncrements(view) { info("Testing keyboard increments with rgb colors"); let idRuleEditor = getRuleViewRuleEditor(view, 1); let rgbColorPropEditor = idRuleEditor.rule.textProps[3].editor; yield runIncrementTest(rgbColorPropEditor, view, { 1: {start: "rgb(0,0,0)", end: "rgb(0,1,0)", selection: [6, 7]},
--- a/devtools/client/shared/css-color.js +++ b/devtools/client/shared/css-color.js @@ -26,22 +26,26 @@ const SPECIALVALUES = new Set([ * let {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); * let {colorUtils} = require("devtools/shared/css-color"); * let color = new colorUtils.CssColor("red"); * * color.authored === "red" * color.hasAlpha === false * color.valid === true * color.transparent === false // transparent has a special status. - * color.name === "red" // returns hex or rgba when no name available. + * 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 - * return this.rgba. + * return this.alphaHex if available, + * or this.longAlphaHex if not. + * color.alphaHex === "#f00f" // returns short alpha hex when available + * else returns longAlphaHex. * color.longHex === "#ff0000" // If alpha channel is present then we return - * this.rgba. + * this.longAlphaHex. + * color.longAlphaHex === "#ff0000ff" * color.rgb === "rgb(255, 0, 0)" // If alpha channel is present * // then we return this.rgba. * color.rgba === "rgba(255, 0, 0, 1)" * color.hsl === "hsl(0, 100%, 50%)" * color.hsla === "hsla(0, 100%, 50%, 1)" // If alpha channel is present * then we return this.rgba. * * color.toString() === "#f00"; // Outputs the color type determined in the @@ -147,57 +151,86 @@ CssColor.prototype = { if (invalidOrSpecialValue !== false) { return invalidOrSpecialValue; } try { let tuple = this._getRGBATuple(); if (tuple.a !== 1) { - return this.rgb; + return this.hex; } let {r, g, b} = tuple; return rgbToColorName(r, g, b); } catch (e) { return this.hex; } }, get hex() { let invalidOrSpecialValue = this._getInvalidOrSpecialValue(); if (invalidOrSpecialValue !== false) { return invalidOrSpecialValue; } if (this.hasAlpha) { - return this.rgba; + return this.alphaHex; } let hex = this.longHex; if (hex.charAt(1) == hex.charAt(2) && hex.charAt(3) == hex.charAt(4) && hex.charAt(5) == hex.charAt(6)) { hex = "#" + hex.charAt(1) + hex.charAt(3) + hex.charAt(5); } return hex; }, + get alphaHex() { + let invalidOrSpecialValue = this._getInvalidOrSpecialValue(); + if (invalidOrSpecialValue !== false) { + return invalidOrSpecialValue; + } + + let alphaHex = this.longAlphaHex; + if (alphaHex.charAt(1) == alphaHex.charAt(2) && + alphaHex.charAt(3) == alphaHex.charAt(4) && + alphaHex.charAt(5) == alphaHex.charAt(6) && + alphaHex.charAt(7) == alphaHex.charAt(8)) { + alphaHex = "#" + alphaHex.charAt(1) + alphaHex.charAt(3) + + alphaHex.charAt(5) + alphaHex.charAt(7); + } + return alphaHex; + }, + get longHex() { let invalidOrSpecialValue = this._getInvalidOrSpecialValue(); if (invalidOrSpecialValue !== false) { return invalidOrSpecialValue; } if (this.hasAlpha) { - return this.rgba; + return this.longAlphaHex; } let tuple = this._getRGBATuple(); return "#" + ((1 << 24) + (tuple.r << 16) + (tuple.g << 8) + (tuple.b << 0)).toString(16).substr(-6); }, + get longAlphaHex() { + let invalidOrSpecialValue = this._getInvalidOrSpecialValue(); + if (invalidOrSpecialValue !== false) { + return invalidOrSpecialValue; + } + + let tuple = this._getRGBATuple(); + return "#" + ((1 << 24) + (tuple.r << 16) + (tuple.g << 8) + + (tuple.b << 0)).toString(16).substr(-6) + + Math.round(tuple.a * 255).toString(16).padEnd(2, "0"); + }, + get rgb() { let invalidOrSpecialValue = this._getInvalidOrSpecialValue(); if (invalidOrSpecialValue !== false) { return invalidOrSpecialValue; } if (!this.hasAlpha) { if (this.lowerCased.startsWith("rgb(")) { // The color is valid and begins with rgb(. @@ -535,44 +568,52 @@ function hslToRGB([h, s, l]) { m1 = l * 2 - m2; r = Math.floor(255 * _hslValue(m1, m2, h + 1.0 / 3.0)); g = Math.floor(255 * _hslValue(m1, m2, h)); b = Math.floor(255 * _hslValue(m1, m2, h - 1.0 / 3.0)); return [r, g, b]; } /** - * A helper function to convert a hex string like "F0C" to a color. + * A helper function to convert a hex string like "F0C" or "F0C8" to a color. * * @param {String} name the color string * @return {Object} an object of the form {r, g, b, a}; or null if the * name was not a valid color */ function hexToRGBA(name) { - let r, g, b; + let r, g, b, a = 1; if (name.length === 3) { - let val = parseInt(name, 16); - b = ((val & 15) << 4) + (val & 15); - val >>= 4; - g = ((val & 15) << 4) + (val & 15); - val >>= 4; - r = ((val & 15) << 4) + (val & 15); + // short hex string (e.g. F0C) + r = parseInt(name.charAt(0) + name.charAt(0), 16); + g = parseInt(name.charAt(1) + name.charAt(1), 16); + b = parseInt(name.charAt(2) + name.charAt(2), 16); + } else if (name.length === 4) { + // short alpha hex string (e.g. F0CA) + r = parseInt(name.charAt(0) + name.charAt(0), 16); + g = parseInt(name.charAt(1) + name.charAt(1), 16); + b = parseInt(name.charAt(2) + name.charAt(2), 16); + a = parseInt(name.charAt(3) + name.charAt(3), 16) / 255; } else if (name.length === 6) { - let val = parseInt(name, 16); - b = val & 255; - val >>= 8; - g = val & 255; - val >>= 8; - r = val & 255; + // hex string (e.g. FD01CD) + r = parseInt(name.charAt(0) + name.charAt(1), 16); + g = parseInt(name.charAt(2) + name.charAt(3), 16); + b = parseInt(name.charAt(4) + name.charAt(5), 16); + } else if (name.length === 8) { + // alpha hex string (e.g. FD01CDAB) + r = parseInt(name.charAt(0) + name.charAt(1), 16); + g = parseInt(name.charAt(2) + name.charAt(3), 16); + b = parseInt(name.charAt(4) + name.charAt(5), 16); + a = parseInt(name.charAt(6) + name.charAt(7), 16) / 255; } else { return null; } - - return {r, g, b, a: 1}; + a = Math.round(a * 10) / 10; + return {r, g, b, a}; } /** * A helper function to clamp a value. * * @param {Number} value The value to clamp * @param {Number} min The minimum value * @param {Number} max The maximum value
--- a/devtools/client/shared/inplace-editor.js +++ b/devtools/client/shared/inplace-editor.js @@ -812,17 +812,27 @@ InplaceEditor.prototype = { if (rawValue.length === 3) { rawValue = rawValue.charAt(0) + rawValue.charAt(0) + rawValue.charAt(1) + rawValue.charAt(1) + rawValue.charAt(2) + rawValue.charAt(2); offset *= 2; offsetEnd *= 2; } - if (rawValue.length !== 6) { + // Normalize #ABCD -> #AABBCCDD. + if (rawValue.length === 4) { + rawValue = rawValue.charAt(0) + rawValue.charAt(0) + + rawValue.charAt(1) + rawValue.charAt(1) + + rawValue.charAt(2) + rawValue.charAt(2) + + rawValue.charAt(3) + rawValue.charAt(3); + offset *= 2; + offsetEnd *= 2; + } + + if (rawValue.length !== 6 && rawValue.length !== 8) { return null; } // If no selection, increment an adjacent color, preferably one to the left. if (offset === offsetEnd) { if (offset === 0) { offsetEnd = 1; } else {
--- a/devtools/client/shared/test/browser_css_color.js +++ b/devtools/client/shared/test/browser_css_color.js @@ -107,17 +107,19 @@ function testColorMatch(name, hex, hsl, function testSetAlpha() { let values = [ ["longhex", "#ff0000", 0.5, "rgba(255, 0, 0, 0.5)"], ["hex", "#f0f", 0.2, "rgba(255, 0, 255, 0.2)"], ["rgba", "rgba(120, 34, 23, 1)", 0.25, "rgba(120, 34, 23, 0.25)"], ["rgb", "rgb(120, 34, 23)", 0.25, "rgba(120, 34, 23, 0.25)"], ["hsl", "hsl(208, 100%, 97%)", 0.75, "rgba(239, 247, 255, 0.75)"], - ["hsla", "hsla(208, 100%, 97%, 1)", 0.75, "rgba(239, 247, 255, 0.75)"] + ["hsla", "hsla(208, 100%, 97%, 1)", 0.75, "rgba(239, 247, 255, 0.75)"], + ["alphahex", "#f08f", 0.6, "rgba(255, 0, 136, 0.6)"], + ["longalphahex", "#00ff80ff", 0.2, "rgba(0, 255, 128, 0.2)"] ]; values.forEach(([type, value, alpha, expected]) => { is(colorUtils.setAlpha(value, alpha), expected, "correctly sets alpha value for " + type); }); try { colorUtils.setAlpha("rgb(24, 25, 45, 1)", 1); ok(false, "Should fail when passing in an invalid color."); @@ -272,23 +274,25 @@ function getTestData() { {authored: "tomato", name: "tomato", hex: "#ff6347", hsl: "hsl(9, 100%, 64%)", rgb: "rgb(255, 99, 71)"}, {authored: "turquoise", name: "turquoise", hex: "#40e0d0", hsl: "hsl(174, 72%, 56%)", rgb: "rgb(64, 224, 208)"}, {authored: "violet", name: "violet", hex: "#ee82ee", hsl: "hsl(300, 76%, 72%)", rgb: "rgb(238, 130, 238)"}, {authored: "wheat", name: "wheat", hex: "#f5deb3", hsl: "hsl(39, 77%, 83%)", rgb: "rgb(245, 222, 179)"}, {authored: "white", name: "white", hex: "#fff", hsl: "hsl(0, 0%, 100%)", rgb: "rgb(255, 255, 255)"}, {authored: "whitesmoke", name: "whitesmoke", hex: "#f5f5f5", hsl: "hsl(0, 0%, 96%)", rgb: "rgb(245, 245, 245)"}, {authored: "yellow", name: "yellow", hex: "#ff0", hsl: "hsl(60, 100%, 50%)", rgb: "rgb(255, 255, 0)"}, {authored: "yellowgreen", name: "yellowgreen", hex: "#9acd32", hsl: "hsl(80, 61%, 50%)", rgb: "rgb(154, 205, 50)"}, - {authored: "rgba(0, 0, 0, 0)", name: "rgba(0, 0, 0, 0)", hex: "rgba(0, 0, 0, 0)", hsl: "hsla(0, 0%, 0%, 0)", rgb: "rgba(0, 0, 0, 0)"}, - {authored: "hsla(0, 0%, 0%, 0)", name: "rgba(0, 0, 0, 0)", hex: "rgba(0, 0, 0, 0)", hsl: "hsla(0, 0%, 0%, 0)", rgb: "rgba(0, 0, 0, 0)"}, - {authored: "rgba(50, 60, 70, 0.5)", name: "rgba(50, 60, 70, 0.5)", hex: "rgba(50, 60, 70, 0.5)", hsl: "hsla(210, 17%, 24%, 0.5)", rgb: "rgba(50, 60, 70, 0.5)"}, - {authored: "rgba(0, 0, 0, 0.3)", name: "rgba(0, 0, 0, 0.3)", hex: "rgba(0, 0, 0, 0.3)", hsl: "hsla(0, 0%, 0%, 0.3)", rgb: "rgba(0, 0, 0, 0.3)"}, - {authored: "rgba(255, 255, 255, 0.6)", name: "rgba(255, 255, 255, 0.6)", hex: "rgba(255, 255, 255, 0.6)", hsl: "hsla(0, 0%, 100%, 0.6)", rgb: "rgba(255, 255, 255, 0.6)"}, + {authored: "rgba(0, 0, 0, 0)", name: "#0000", hex: "#0000", hsl: "hsla(0, 0%, 0%, 0)", rgb: "rgba(0, 0, 0, 0)"}, + {authored: "hsla(0, 0%, 0%, 0)", name: "#0000", hex: "#0000", hsl: "hsla(0, 0%, 0%, 0)", rgb: "rgba(0, 0, 0, 0)"}, + {authored: "rgba(50, 60, 70, 0.5)", name: "#323c4680", hex: "#323c4680", hsl: "hsla(210, 17%, 24%, 0.5)", rgb: "rgba(50, 60, 70, 0.5)"}, + {authored: "rgba(0, 0, 0, 0.3)", name: "#0000004d", hex: "#0000004d", hsl: "hsla(0, 0%, 0%, 0.3)", rgb: "rgba(0, 0, 0, 0.3)"}, + {authored: "rgba(255, 255, 255, 0.6)", name: "#fff9", hex: "#fff9", hsl: "hsla(0, 0%, 100%, 0.6)", rgb: "rgba(255, 255, 255, 0.6)"}, {authored: "rgba(127, 89, 45, 1)", name: "#7f592d", hex: "#7f592d", hsl: "hsl(32, 48%, 34%)", rgb: "rgb(127, 89, 45)"}, {authored: "hsla(19.304, 56%, 40%, 1)", name: "#9f512c", hex: "#9f512c", hsl: "hsl(19, 57%, 40%)", rgb: "rgb(159, 81, 44)"}, + {authored: "#f089", name: "#f089", hex: "#f089", hsl: "hsla(328, 100%, 50%, 0.6)", rgb: "rgba(255, 0, 136, 0.6)"}, + {authored: "#00ff8080", name: "#00ff8080", hex: "#00ff8080", hsl: "hsla(150, 100%, 50%, 0.5)", rgb: "rgba(0, 255, 128, 0.5)"}, {authored: "currentcolor", name: "currentcolor", hex: "currentcolor", hsl: "currentcolor", rgb: "currentcolor"}, {authored: "inherit", name: "inherit", hex: "inherit", hsl: "inherit", rgb: "inherit"}, {authored: "initial", name: "initial", hex: "initial", hsl: "initial", rgb: "initial"}, {authored: "invalidColor", name: "", hex: "", hsl: "", rgb: ""}, {authored: "transparent", name: "transparent", hex: "transparent", hsl: "transparent", rgb: "transparent"}, {authored: "unset", name: "unset", hex: "unset", hsl: "unset", rgb: "unset"} ]; }
--- a/devtools/client/shared/test/unit/test_cssColor.js +++ b/devtools/client/shared/test/unit/test_cssColor.js @@ -20,18 +20,21 @@ const CLASSIFY_TESTS = [ { input: "rgb(255,0,192)", output: "rgb" }, { input: "RGB(255,0,192)", output: "rgb" }, { input: "RGB(100%,0%,83%)", output: "rgb" }, { input: "rgba(255,0,192, 0.25)", output: "rgb" }, { input: "hsl(5, 5%, 5%)", output: "hsl" }, { input: "hsla(5, 5%, 5%, 0.25)", output: "hsl" }, { input: "hSlA(5, 5%, 5%, 0.25)", output: "hsl" }, { input: "#f0c", output: "hex" }, + { input: "#f0c0", output: "hex" }, { input: "#fe01cb", output: "hex" }, + { input: "#fe01cb80", output: "hex" }, { input: "#FE01CB", output: "hex" }, + { input: "#FE01CB80", output: "hex" }, { input: "blue", output: "name" }, { input: "orange", output: "name" } ]; function compareWithDomutils(input, isColor) { let ours = colorUtils.colorToRGBA(input); let platform = DOMUtils.colorToRGBA(input); deepEqual(ours, platform, "color " + input + " matches DOMUtils");