Diff
Modified: trunk/LayoutTests/ChangeLog (222101 => 222102)
--- trunk/LayoutTests/ChangeLog 2017-09-15 19:30:52 UTC (rev 222101)
+++ trunk/LayoutTests/ChangeLog 2017-09-15 19:50:40 UTC (rev 222102)
@@ -1,3 +1,16 @@
+2017-09-15 Matt Baker <[email protected]>
+
+ Web Inspector: Canvas: recording parameters that include colors should show an InlineSwatch (2D canvas)
+ https://bugs.webkit.org/show_bug.cgi?id=176822
+ <rdar://problem/34402170>
+
+ Reviewed by Devin Rousso.
+
+ Add tests for color space conversions.
+
+ * inspector/model/color-expected.txt:
+ * inspector/model/color.html:
+
2017-09-15 Brent Fulgham <[email protected]>
Unreviewed test maintenance.
Modified: trunk/LayoutTests/inspector/model/color-expected.txt (222101 => 222102)
--- trunk/LayoutTests/inspector/model/color-expected.txt 2017-09-15 19:30:52 UTC (rev 222101)
+++ trunk/LayoutTests/inspector/model/color-expected.txt 2017-09-15 19:50:40 UTC (rev 222102)
@@ -153,3 +153,29 @@
PASS: Color as 'HSL' should be 'hsla(201, 100%, 70%, 0.5)'
PASS: Color as 'HSLA' should be 'hsla(201, 100%, 70%, 0.5)'
+-- Running test case: WI.Color.rgb2hsv
+PASS: Should convert [0,0,0] to [0,0,0].
+PASS: Should convert [255,255,255] to [0,0,1].
+PASS: Should convert [255,0,0] to [0,1,1].
+PASS: Should convert [0,255,0] to [120,1,1].
+PASS: Should convert [0,0,255] to [240,1,1].
+PASS: Should convert [-1,-1,-1] to [0,0,0].
+PASS: Should convert [256,256,256] to [0,0,1].
+PASS: Should convert [254.9,0,0] to [0,1,1].
+
+-- Running test case: WI.Color.cmyk2rgb
+PASS: Should convert [0,0,0,1] to [0,0,0].
+PASS: Should convert [1,0,0,0] to [0,255,255].
+PASS: Should convert [0,1,0,0] to [255,0,255].
+PASS: Should convert [0,0,1,0] to [255,255,0].
+PASS: Should convert [0,0,0,0] to [255,255,255].
+PASS: Should convert [2,0,0,0] to [0,255,255].
+PASS: Should convert [-1,0,0,0] to [255,255,255].
+
+-- Running test case: WI.Color.normalized2rgb
+PASS: Should convert [0,0,0] to [0,0,0].
+PASS: Should convert [1,1,1] to [255,255,255].
+PASS: Should convert [0.24,0.25,0.26] to [61,64,66].
+PASS: Should convert [2,0,0] to [255,0,0].
+PASS: Should convert [-1,0,0] to [0,0,0].
+
Modified: trunk/LayoutTests/inspector/model/color.html (222101 => 222102)
--- trunk/LayoutTests/inspector/model/color.html 2017-09-15 19:30:52 UTC (rev 222101)
+++ trunk/LayoutTests/inspector/model/color.html 2017-09-15 19:50:40 UTC (rev 222102)
@@ -298,6 +298,64 @@
}
});
+ function testColorConversion(func, value, expected) {
+ let actual = func(...value);
+ InspectorTest.expectShallowEqual(actual, expected, `Should convert ${JSON.stringify(value)} to ${JSON.stringify(expected)}.`);
+ }
+
+ suite.addTestCase({
+ name: "WI.Color.rgb2hsv",
+ description: "Test conversion from RGB to HSV.",
+ test(resolve, reject) {
+ testColorConversion(WI.Color.rgb2hsv, [0, 0, 0], [0, 0, 0]);
+ testColorConversion(WI.Color.rgb2hsv, [255, 255, 255], [0, 0, 1]);
+ testColorConversion(WI.Color.rgb2hsv, [255, 0, 0], [0, 1, 1]);
+ testColorConversion(WI.Color.rgb2hsv, [0, 255, 0], [120, 1, 1]);
+ testColorConversion(WI.Color.rgb2hsv, [0, 0, 255], [240, 1, 1]);
+
+ // Out-of-bounds and floating point inputs.
+ testColorConversion(WI.Color.rgb2hsv, [-1, -1, -1], [0, 0, 0]);
+ testColorConversion(WI.Color.rgb2hsv, [256, 256, 256], [0, 0, 1]);
+ testColorConversion(WI.Color.rgb2hsv, [254.9, 0, 0], [0, 1, 1]);
+
+ resolve();
+ }
+ });
+
+ suite.addTestCase({
+ name: "WI.Color.cmyk2rgb",
+ description: "Test conversion from CMYK to RGB.",
+ test(resolve, reject) {
+ testColorConversion(WI.Color.cmyk2rgb, [0, 0, 0, 1], [0, 0, 0]);
+ testColorConversion(WI.Color.cmyk2rgb, [1, 0, 0, 0], [0, 255, 255]);
+ testColorConversion(WI.Color.cmyk2rgb, [0, 1, 0, 0], [255, 0, 255]);
+ testColorConversion(WI.Color.cmyk2rgb, [0, 0, 1, 0], [255, 255, 0]);
+ testColorConversion(WI.Color.cmyk2rgb, [0, 0, 0, 0], [255, 255, 255]);
+
+ // Out-of-bounds inputs.
+ testColorConversion(WI.Color.cmyk2rgb, [2, 0, 0, 0], [0, 255, 255]);
+ testColorConversion(WI.Color.cmyk2rgb, [-1, 0, 0, 0], [255, 255, 255]);
+
+ resolve();
+ }
+ });
+
+ suite.addTestCase({
+ name: "WI.Color.normalized2rgb",
+ description: "Test conversion from normalized RGB to RGB.",
+ test(resolve, reject) {
+ testColorConversion(WI.Color.normalized2rgb, [0, 0, 0], [0, 0, 0]);
+ testColorConversion(WI.Color.normalized2rgb, [1, 1, 1], [255, 255, 255]);
+ testColorConversion(WI.Color.normalized2rgb, [0.24, 0.25, 0.26], [61, 64, 66]); // Values chosen to test round up/down behavior.
+
+ // Out-of-bounds inputs.
+ testColorConversion(WI.Color.normalized2rgb, [2, 0, 0], [255, 0, 0]);
+ testColorConversion(WI.Color.normalized2rgb, [-1, 0, 0], [0, 0, 0]);
+
+ resolve();
+ }
+ });
+
suite.runTestCasesAndFinish();
}
</script>
Modified: trunk/Source/WebInspectorUI/ChangeLog (222101 => 222102)
--- trunk/Source/WebInspectorUI/ChangeLog 2017-09-15 19:30:52 UTC (rev 222101)
+++ trunk/Source/WebInspectorUI/ChangeLog 2017-09-15 19:50:40 UTC (rev 222102)
@@ -1,3 +1,60 @@
+2017-09-15 Matt Baker <[email protected]>
+
+ Web Inspector: Canvas: recording parameters that include colors should show an InlineSwatch (2D canvas)
+ https://bugs.webkit.org/show_bug.cgi?id=176822
+ <rdar://problem/34402170>
+
+ Reviewed by Devin Rousso.
+
+ Show inline swatches in the canvas recording action and state sidebars.
+
+ * UserInterface/Models/Color.js:
+ Added helpers for dealing with CMYK and normalized RGB. The latter is
+ for RGB components scaled to the range [0, 1]. Also improved handling
+ for 8-bit channel values.
+ (WI.Color.rgb2hsv):
+ (WI.Color.cmyk2rgb):
+ (WI.Color.normalized2rgb):
+ (WI.Color._eightBitChannel):
+ (WI.Color.prototype._toRGBString):
+ (WI.Color.prototype._toRGBAString):
+ (WI.Color.prototype._componentToHexValue):
+ (WI.Color.prototype._rgbToHSL):
+ (WI.Color.prototype._componentToNumber): Deleted.
+ Replaced by _eightBitChannel.
+
+ * UserInterface/Models/RecordingAction.js:
+ (WI.RecordingAction):
+ (WI.RecordingAction.prototype.getColorParameters):
+ Get a subset of parameters that describe a color. This can be an array
+ containing one value (e.g. fillStyle), or multiple values, as is the
+ case with non-standard API functions that describe color using multiple
+ parameters (e.g. setFillColor).
+
+ * UserInterface/Views/InlineSwatch.css:
+ (.inline-swatch:not(.read-only):hover > span):
+ (.inline-swatch:hover > span): Deleted.
+ * UserInterface/Views/InlineSwatch.js:
+ (WI.InlineSwatch):
+ Read-only colors shouldn't show a context menu or hover effects.
+
+ * UserInterface/Views/RecordingActionTreeElement.css:
+ (.tree-outline:matches(:focus, .force-focus) .item.action > .titles .parameters > .inline-swatch):
+
+ * UserInterface/Views/RecordingActionTreeElement.js:
+ (WI.RecordingActionTreeElement._generateDOM.createParameterElement):
+ (WI.RecordingActionTreeElement._generateDOM):
+ (WI.RecordingActionTreeElement._createSwatchForColorParameters):
+
+ * UserInterface/Views/RecordingStateDetailsSidebarPanel.css:
+ (.sidebar > .panel.details.recording-state > .content > .data-grid .inline-swatch):
+
+ * UserInterface/Views/RecordingStateDetailsSidebarPanel.js:
+ (WI.RecordingStateDetailsSidebarPanel.prototype._generateDetailsCanvas2D.isColorProperty):
+ (WI.RecordingStateDetailsSidebarPanel.prototype._generateDetailsCanvas2D.createInlineSwatch):
+ (WI.RecordingStateDetailsSidebarPanel.prototype._generateDetailsCanvas2D):
+ (WI.RecordingStateDetailsSidebarPanel):
+
2017-09-14 Joseph Pecoraro <[email protected]>
Unreviewed rollout r222036.
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/Color.js (222101 => 222102)
--- trunk/Source/WebInspectorUI/UserInterface/Models/Color.js 2017-09-15 19:30:52 UTC (rev 222101)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/Color.js 2017-09-15 19:50:40 UTC (rev 222102)
@@ -157,9 +157,9 @@
static rgb2hsv(r, g, b)
{
- r /= 255;
- g /= 255;
- b /= 255;
+ r = WI.Color._eightBitChannel(r) / 255;
+ g = WI.Color._eightBitChannel(g) / 255;
+ b = WI.Color._eightBitChannel(b) / 255;
let min = Math.min(Math.min(r, g), b);
let max = Math.max(Math.max(r, g), b);
@@ -228,7 +228,33 @@
return rgb;
}
+ static cmyk2rgb(c, m, y, k)
+ {
+ c = Number.constrain(c, 0, 1);
+ m = Number.constrain(m, 0, 1);
+ y = Number.constrain(y, 0, 1);
+ k = Number.constrain(k, 0, 1);
+ let r = 255 - ((Math.min(1, c * (1 - k) + k)) * 255);
+ let g = 255 - ((Math.min(1, m * (1 - k) + k)) * 255);
+ let b = 255 - ((Math.min(1, y * (1 - k) + k)) * 255);
+ return [r, g, b];
+ }
+
+ static normalized2rgb(r, g, b)
+ {
+ return [
+ WI.Color._eightBitChannel(r * 255),
+ WI.Color._eightBitChannel(g * 255),
+ WI.Color._eightBitChannel(b * 255)
+ ];
+ }
+
+ static _eightBitChannel(value)
+ {
+ return Number.constrain(Math.round(value), 0, 255);
+ }
+
// Public
nextFormat(format)
@@ -484,16 +510,18 @@
if (!this.simple)
return this._toRGBAString();
- let rgba = this.rgba.slice(0, -1);
- rgba = rgba.map((value) => value.maxDecimals(2));
- return "rgb(" + rgba.join(", ") + ")";
+ let r = WI.Color._eightBitChannel(this.rgb[0]);
+ let g = WI.Color._eightBitChannel(this.rgb[1]);
+ let b = WI.Color._eightBitChannel(this.rgb[2]);
+ return `rgb(${r}, ${g}, ${b})`;
}
_toRGBAString()
{
- let rgba = this.rgba;
- rgba = rgba.map((value) => value.maxDecimals(2));
- return "rgba(" + rgba.join(", ") + ")";
+ let r = WI.Color._eightBitChannel(this.rgb[0]);
+ let g = WI.Color._eightBitChannel(this.rgb[1]);
+ let b = WI.Color._eightBitChannel(this.rgb[2]);
+ return `rgba(${r}, ${g}, ${b}, ${this.alpha})`;
}
_toHSLString()
@@ -513,14 +541,9 @@
return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " + hsla[3] + ")";
}
- _componentToNumber(value)
- {
- return Number.constrain(value, 0, 255);
- }
-
_componentToHexValue(value)
{
- let hex = this._componentToNumber(value).toString(16);
+ let hex = WI.Color._eightBitChannel(value).toString(16);
if (hex.length === 1)
hex = "0" + hex;
return hex;
@@ -528,9 +551,9 @@
_rgbToHSL(rgb)
{
- let r = this._componentToNumber(rgb[0]) / 255;
- let g = this._componentToNumber(rgb[1]) / 255;
- let b = this._componentToNumber(rgb[2]) / 255;
+ let r = WI.Color._eightBitChannel(rgb[0]) / 255;
+ let g = WI.Color._eightBitChannel(rgb[1]) / 255;
+ let b = WI.Color._eightBitChannel(rgb[2]) / 255;
let max = Math.max(r, g, b);
let min = Math.min(r, g, b);
let diff = max - min;
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/RecordingAction.js (222101 => 222102)
--- trunk/Source/WebInspectorUI/UserInterface/Models/RecordingAction.js 2017-09-15 19:30:52 UTC (rev 222101)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/RecordingAction.js 2017-09-15 19:50:40 UTC (rev 222102)
@@ -167,6 +167,29 @@
this._stateModifiers.add(item);
}
}
+
+ getColorParameters()
+ {
+ switch (this._name) {
+ // 2D
+ case "fillStyle":
+ case "strokeStyle":
+ case "shadowColor":
+ // 2D (non-standard, legacy)
+ case "setFillColor":
+ case "setStrokeColor":
+ // WebGL
+ case "blendColor":
+ case "clearColor":
+ return this._parameters;
+
+ // 2D (non-standard, legacy)
+ case "setShadow":
+ return this._parameters.slice(3);
+ }
+
+ return [];
+ }
};
WI.RecordingAction._functionNames = {
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.css (222101 => 222102)
--- trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.css 2017-09-15 19:30:52 UTC (rev 222101)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.css 2017-09-15 19:50:40 UTC (rev 222102)
@@ -84,7 +84,7 @@
pointer-events: none;
}
-.inline-swatch:hover > span {
+.inline-swatch:not(.read-only):hover > span {
border-color: hsla(0, 0%, 25%, 0.8);
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js (222101 => 222102)
--- trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js 2017-09-15 19:30:52 UTC (rev 222101)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js 2017-09-15 19:50:40 UTC (rev 222102)
@@ -35,29 +35,32 @@
this._swatchElement = document.createElement("span");
this._swatchElement.classList.add("inline-swatch", this._type.split("-").lastValue);
- switch (this._type) {
- case WI.InlineSwatch.Type.Color:
- this._swatchElement.title = WI.UIString("Click to select a color. Shift-click to switch color formats.");
- break;
- case WI.InlineSwatch.Type.Gradient:
- this._swatchElement.title = WI.UIString("Edit custom gradient");
- break;
- case WI.InlineSwatch.Type.Bezier:
- this._swatchElement.title = WI.UIString("Edit “cubic-bezier“ function");
- break;
- case WI.InlineSwatch.Type.Spring:
- this._swatchElement.title = WI.UIString("Edit “spring“ function");
- break;
- case WI.InlineSwatch.Type.Variable:
- this._swatchElement.title = WI.UIString("View variable value");
- break;
- default:
- WI.reportInternalError(`Unknown InlineSwatch type "${type}"`);
- break;
- }
+ this._boundSwatchElementClicked = null;
- this._boundSwatchElementClicked = null;
- if (!readOnly) {
+ if (readOnly)
+ this._swatchElement.classList.add("read-only");
+ else {
+ switch (this._type) {
+ case WI.InlineSwatch.Type.Color:
+ this._swatchElement.title = WI.UIString("Click to select a color. Shift-click to switch color formats.");
+ break;
+ case WI.InlineSwatch.Type.Gradient:
+ this._swatchElement.title = WI.UIString("Edit custom gradient");
+ break;
+ case WI.InlineSwatch.Type.Bezier:
+ this._swatchElement.title = WI.UIString("Edit “cubic-bezier“ function");
+ break;
+ case WI.InlineSwatch.Type.Spring:
+ this._swatchElement.title = WI.UIString("Edit “spring“ function");
+ break;
+ case WI.InlineSwatch.Type.Variable:
+ this._swatchElement.title = WI.UIString("View variable value");
+ break;
+ default:
+ WI.reportInternalError(`Unknown InlineSwatch type "${type}"`);
+ break;
+ }
+
this._boundSwatchElementClicked = this._swatchElementClicked.bind(this);
this._swatchElement.addEventListener("click", this._boundSwatchElementClicked);
if (this._type === WI.InlineSwatch.Type.Color)
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/RecordingActionTreeElement.css (222101 => 222102)
--- trunk/Source/WebInspectorUI/UserInterface/Views/RecordingActionTreeElement.css 2017-09-15 19:30:52 UTC (rev 222101)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/RecordingActionTreeElement.css 2017-09-15 19:50:40 UTC (rev 222102)
@@ -61,11 +61,19 @@
opacity: 1;
}
+body:not(.window-inactive, .window-docked-inactive) .item.action > .titles .parameter.swizzled {
+ color: var(--text-color-gray-medium);
+}
+
body:not(.window-inactive, .window-docked-inactive) :matches(:focus, .force-focus) .item.action.selected > .titles .parameter.swizzled,
body:not(.window-inactive, .window-docked-inactive) :matches(:focus, .force-focus) .item.action.selected::before {
- color: var(--selected-secondary-text-color);
+ color: var(--console-secondary-text-color);
}
+.tree-outline:matches(:focus, .force-focus) .item.action > .titles .parameters > .inline-swatch {
+ vertical-align: -1px;
+}
+
.tree-outline[data-indent="1"] .item.action::before,
.tree-outline[data-indent="2"] .item.action::before {
min-width: var(--data-indent);
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/RecordingActionTreeElement.js (222101 => 222102)
--- trunk/Source/WebInspectorUI/UserInterface/Views/RecordingActionTreeElement.js 2017-09-15 19:30:52 UTC (rev 222101)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/RecordingActionTreeElement.js 2017-09-15 19:50:40 UTC (rev 222102)
@@ -125,9 +125,60 @@
if (recordingAction.isFunction)
copyText += ")";
+ let colorParameters = recordingAction.getColorParameters();
+ if (colorParameters.length) {
+ let swatch = WI.RecordingActionTreeElement._createSwatchForColorParameters(colorParameters);
+ let insertionIndex = recordingAction.parameters.indexOf(colorParameters[0]);
+ let parameterElement = parametersContainer.children[insertionIndex];
+ parametersContainer.insertBefore(swatch.element, parameterElement);
+
+ if (recordingAction.swizzleTypes[insertionIndex] === WI.Recording.Swizzle.String) {
+ parameterElement.textContent = swatch.value.toString();
+ parameterElement.classList.add("color");
+ }
+ }
+
return {titleFragment, copyText};
}
+ static _createSwatchForColorParameters(parameters)
+ {
+ let rgb = [];
+ let color = null;
+
+ switch (parameters.length) {
+ case 1:
+ case 2:
+ if (typeof parameters[0] === "string")
+ color = WI.Color.fromString(parameters[0]);
+ else
+ rgb = WI.Color.normalized2rgb(parameters[0], parameters[0], parameters[0]);
+ break;
+
+ case 4:
+ rgb = WI.Color.normalized2rgb(parameters[0], parameters[1], parameters[2]);
+ break;
+
+ case 5:
+ rgb = WI.Color.cmyk2rgb(...parameters);
+ break;
+
+ default:
+ console.error("Unexpected number of color parameters.");
+ return null;
+ }
+
+ if (!color) {
+ let alpha = parameters.length === 1 ? 1 : parameters.lastValue;
+ color = new WI.Color(WI.Color.Format.RGBA, [...rgb, alpha]);
+ if (!color)
+ return null;
+ }
+
+ const readOnly = true;
+ return new WI.InlineSwatch(WI.InlineSwatch.Type.Color, color, readOnly);
+ }
+
static _getClassNames(recordingAction)
{
let classNames = ["action"];
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.css (222101 => 222102)
--- trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.css 2017-09-15 19:30:52 UTC (rev 222101)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.css 2017-09-15 19:50:40 UTC (rev 222102)
@@ -38,3 +38,7 @@
.sidebar > .panel.details.recording-state > .content > .data-grid tr:not(.selected) .unavailable {
color: grey;
}
+
+.sidebar > .panel.details.recording-state > .content > .data-grid .inline-swatch {
+ vertical-align: -1px;
+}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.js (222101 => 222102)
--- trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.js 2017-09-15 19:30:52 UTC (rev 222101)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/RecordingStateDetailsSidebarPanel.js 2017-09-15 19:50:40 UTC (rev 222102)
@@ -135,6 +135,19 @@
state.webkitLineDash = context.webkitLineDash;
state.webkitLineDashOffset = context.webkitLineDashOffset;
+ function isColorProperty(name) {
+ return name === "fillStyle" || name === "strokeStyle" || name === "shadowColor";
+ }
+
+ function createInlineSwatch(value) {
+ let color = WI.Color.fromString(value);
+ if (!color)
+ return null;
+
+ const readOnly = true;
+ return new WI.InlineSwatch(WI.InlineSwatch.Type.Color, color, readOnly);
+ }
+
let action = ""
for (let name in state) {
let value = state[name];
@@ -154,6 +167,11 @@
value = JSON.stringify(value);
}
+ } else if (isColorProperty(name)) {
+ let swatch = createInlineSwatch(value);
+ let label = swatch.value.toString();
+ value = document.createElement("span");
+ value.append(swatch.element, label);
}
let classNames = [];