-
-
Notifications
You must be signed in to change notification settings - Fork 931
Make frontend CSS color validation Chromium-only (remove DOM style fallback) #2946
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
3e82a19
c46da47
7ed95d9
9c46dcb
d941910
e3a3c73
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; | ||
|
|
||
| import { validateCssColor } from "./color-validator"; | ||
|
|
||
| describe("validateCssColor", () => { | ||
| beforeEach(() => { | ||
| vi.stubGlobal("CSS", { | ||
| supports: (_property: string, value: string) => { | ||
| return [ | ||
| "red", | ||
| "#aabbcc", | ||
| "#aabbccdd", | ||
| "rgb(255, 0, 0)", | ||
| "rgba(255, 0, 0, 0.5)", | ||
| "hsl(120 100% 50%)", | ||
| "transparent", | ||
| "currentColor", | ||
| ].includes(value); | ||
| }, | ||
| }); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| vi.unstubAllGlobals(); | ||
| }); | ||
|
|
||
| it("returns type for supported CSS color formats", () => { | ||
| expect(validateCssColor("red")).toBe("keyword"); | ||
| expect(validateCssColor("#aabbcc")).toBe("hex"); | ||
| expect(validateCssColor("#aabbccdd")).toBe("hex8"); | ||
| expect(validateCssColor("rgb(255, 0, 0)")).toBe("rgb"); | ||
| expect(validateCssColor("rgba(255, 0, 0, 0.5)")).toBe("rgba"); | ||
| expect(validateCssColor("hsl(120 100% 50%)")).toBe("hsl"); | ||
| expect(validateCssColor("transparent")).toBe("transparent"); | ||
| expect(validateCssColor("currentColor")).toBe("currentcolor"); | ||
| }); | ||
|
|
||
| it("throws for invalid CSS colors", () => { | ||
| expect(() => validateCssColor(":not-a-color:")).toThrow("Invalid CSS color"); | ||
| expect(() => validateCssColor("#12")).toThrow("Invalid CSS color"); | ||
| expect(() => validateCssColor("rgb(255, 0)")).toThrow("Invalid CSS color"); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,57 @@ | ||||||
| const HexColorRegex = /^#([\da-f]{3}|[\da-f]{4}|[\da-f]{6}|[\da-f]{8})$/i; | ||||||
| const FunctionalColorRegex = /^([a-z-]+)\(/i; | ||||||
| const NamedColorRegex = /^[a-z]+$/i; | ||||||
|
|
||||||
| function isValidCssColor(color: string): boolean { | ||||||
| if (typeof CSS != "undefined" && typeof CSS.supports == "function") { | ||||||
| return CSS.supports("color", color); | ||||||
| } | ||||||
| if (typeof document == "undefined") { | ||||||
| return false; | ||||||
| } | ||||||
| const temp = document.createElement("div"); | ||||||
| temp.style.color = ""; | ||||||
| temp.style.color = color; | ||||||
| return temp.style.color != ""; | ||||||
| } | ||||||
|
|
||||||
| function getCssColorType(color: string): string { | ||||||
| const normalizedColor = color.toLowerCase(); | ||||||
| if (HexColorRegex.test(normalizedColor)) { | ||||||
| if (normalizedColor.length === 4) { | ||||||
| return "hex3"; | ||||||
| } | ||||||
| if (normalizedColor.length === 5) { | ||||||
| return "hex4"; | ||||||
| } | ||||||
| if (normalizedColor.length === 9) { | ||||||
| return "hex8"; | ||||||
| } | ||||||
| return "hex"; | ||||||
| } | ||||||
| if (normalizedColor === "transparent") { | ||||||
| return "transparent"; | ||||||
| } | ||||||
| if (normalizedColor === "currentcolor") { | ||||||
| return "currentcolor"; | ||||||
| } | ||||||
| const functionMatch = normalizedColor.match(FunctionalColorRegex); | ||||||
| if (functionMatch) { | ||||||
| return functionMatch[1]; | ||||||
| } | ||||||
| if (NamedColorRegex.test(normalizedColor)) { | ||||||
| return "keyword"; | ||||||
| } | ||||||
| return "color"; | ||||||
| } | ||||||
|
|
||||||
| export function validateCssColor(color: string): string { | ||||||
| if (typeof color != "string") { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Use strict equality operator Use
Suggested change
|
||||||
| throw new Error(`Invalid CSS color: ${String(color)}`); | ||||||
| } | ||||||
| const normalizedColor = color.trim(); | ||||||
| if (normalizedColor == "" || !isValidCssColor(normalizedColor)) { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Use strict equality operator Should use
Suggested change
|
||||||
| throw new Error(`Invalid CSS color: ${color}`); | ||||||
| } | ||||||
| return getCssColorType(normalizedColor); | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WARNING: Use strict equality operator
Should use
!==instead of!=when comparing to empty string. The project uses!=only for null/undefined checks, not for string comparisons.