Skip to content

Commit

Permalink
TINY-10916: Transparent color is converted into #00000 (#9649) (#9670)
Browse files Browse the repository at this point in the history
* TINY-10916: Convert transparent to fully transparent black in Styles.ts

* TINY-10916: Add changelog

* TINY-10916: Prevent transparent from been converted

* TINY-10916: Add and refactor tests

* Update .changes/unreleased/tinymce-TINY-10916-2024-05-15.yaml



* TINY-10916: Add more test

* TINY-10916: Add more tests

* TINY-10916: Update rgba regex to do exact match.

* TINY-10916: Remove transparent string check (WIP)

* TINY-10916: Rever rgba regex to rgb regex

* TINY-10916: Lint fix

* TINY-10916: Remove global match in rgb regex

* TINY-10916: Refactor code, add support and test for new format

* Update tinymce-TINY-10916-2024-05-15.yaml



* TINY-10916: Update rgba regex

* TINY-10916: Add RgbaColour test

* TINY-10916: Update rgba regex and test

---------

Co-authored-by: Andrew Herron <thespyder@programmer.net>
Co-authored-by: spocke <spocke@moxiecode.com>
  • Loading branch information
3 people committed May 30, 2024
1 parent 2881dc6 commit 29292cc
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 7 deletions.
6 changes: 6 additions & 0 deletions .changes/unreleased/tinymce-TINY-10916-2024-05-15.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
project: tinymce
kind: Fixed
body: CSS color values set to `transparent` were incorrectly converted to '#000000`.
time: 2024-05-15T17:05:18.42363+10:00
custom:
Issue: TINY-10916
21 changes: 16 additions & 5 deletions modules/acid/src/main/ts/ephox/acid/api/colour/RgbaColour.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-console */
import { Optional } from '@ephox/katamari';

import { Hex, Hsv, Rgba } from './ColourTypes';
Expand All @@ -7,8 +8,10 @@ const min = Math.min;
const max = Math.max;
const round = Math.round;

const rgbRegex = /^\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/i;
const rgbaRegex = /^\s*rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?(?:\.\d+)?)\s*\)\s*$/i;
const rgbRegex = /^\s*rgb\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*\)\s*$/i;

// This regex will match rgba(0, 0, 0, 0.5) or rgba(0, 0, 0, 50%) , or without commas
const rgbaRegex = /^\s*rgba\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*((?:\d?\.\d+|\d+)%?)\s*\)\s*$/i;

const rgbaColour = (red: number, green: number, blue: number, alpha: number): Rgba => ({
red,
Expand Down Expand Up @@ -102,13 +105,20 @@ const fromStringValues = (red: string, green: string, blue: string, alpha: strin
const g = parseInt(green, 10);
const b = parseInt(blue, 10);
const a = parseFloat(alpha);

return rgbaColour(r, g, b, a);
};

const fromString = (rgbaString: string): Optional<Rgba> => {
if (rgbaString === 'transparent') {
return Optional.some(rgbaColour(0, 0, 0, 0));
const getColorFormat = (colorString: string): string => {
if (rgbRegex.test(colorString)) {
return 'rgb';
} else if (rgbaRegex.test(colorString)) {
return 'rgba';
}
return 'other';
};

const fromString = (rgbaString: string): Optional<Rgba> => {
const rgbMatch = rgbRegex.exec(rgbaString);
if (rgbMatch !== null) {
return Optional.some(fromStringValues(rgbMatch[1], rgbMatch[2], rgbMatch[3], '1'));
Expand All @@ -130,6 +140,7 @@ export {
fromHsv,
fromHex,
fromString,
getColorFormat,
toString,
red
};
26 changes: 26 additions & 0 deletions modules/acid/src/test/ts/browser/RgbaColourTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { context, describe, it } from '@ephox/bedrock-client';
import { Arr } from '@ephox/katamari';
import { assert } from 'chai';

import * as RgbaColour from 'ephox/acid/api/colour/RgbaColour';

describe('RgbaColourTest', () => {
context('colour identify', () => {
it('TINY-7480: identify rgb colours', () => {
Arr.each([
{ colour: 'rgb(0, 0, 0)', expected: 'rgb' },
{ colour: 'rgba(0, 0, 0, 0)', expected: 'rgba' },
// rgb with alpha value is invalid
{ colour: 'rgb(0, 0, 0, 0)', expected: 'other' },
{ colour: 'rgb(0 0 0)', expected: 'rgb' },
{ colour: 'rgba(0 0 0 0)', expected: 'rgba' },
// We currently don't support converting this format
{ colour: 'rgb(0 0 0 / 10%)', expected: 'other' },
], (test) => {
const { colour, expected } = test;
const result = RgbaColour.getColorFormat(colour);
assert.equal(result, expected);
});
});
});
});
3 changes: 1 addition & 2 deletions modules/tinymce/src/core/main/ts/api/html/Styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ const Styles = (settings: StylesSettings = {}, schema?: Schema): Styles => {
const urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi;
const styleRegExp = /\s*([^:]+):\s*([^;]+);?/g;
const trimRightRegExp = /\s+$/;
const rgbaRegExp = /rgba *\(/i;
const encodingLookup: Record<string, string> = {};
let validStyles: Record<string, string[]> | undefined;
let invalidStyles: Record<string, SchemaMap> | undefined;
Expand Down Expand Up @@ -266,7 +265,7 @@ const Styles = (settings: StylesSettings = {}, schema?: Schema): Styles => {
}

// Convert RGB colors to HEX
if (!rgbaRegExp.test(value)) {
if (RgbaColour.getColorFormat(value) === 'rgb') {
RgbaColour.fromString(value).each((rgba) => {
value = Transformations.rgbaToHexString(RgbaColour.toString(rgba)).toLowerCase();
});
Expand Down
46 changes: 46 additions & 0 deletions modules/tinymce/src/core/test/ts/browser/html/StylesTest.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { Assertions } from '@ephox/agar';
import { describe, it } from '@ephox/bedrock-client';
import { Arr } from '@ephox/katamari';
import { TinyHooks } from '@ephox/wrap-mcagar';
import { assert } from 'chai';

import Editor from 'tinymce/core/api/Editor';
import Schema from 'tinymce/core/api/html/Schema';
import Styles from 'tinymce/core/api/html/Styles';

describe('browser.tinymce.core.html.StylesTest', () => {

const hook = TinyHooks.bddSetupLight<Editor>({
base_url: '/project/tinymce/js/tinymce'
}, []);

const assertStyles = (styles: Styles, input: string, expected: string) => {
assert.equal(styles.serialize(styles.parse(input)), expected);
};
Expand Down Expand Up @@ -264,4 +272,42 @@ describe('browser.tinymce.core.html.StylesTest', () => {
assertStyles(styles, 'color: rgba(1, 2, 3);', 'color: rgba(1, 2, 3);');
assertStyles(styles, 'color: rgb(1, 2, 3, 0.5);', 'color: rgb(1, 2, 3, 0.5);');
});

it('TINY-10916: transparent should not be converted to other formats', () => {
const styles = Styles();
assertStyles(styles, 'color: transparent;', 'color: transparent;');
assertStyles(styles, 'background-color: transparent;', 'background-color: transparent;');
assertStyles(styles, 'border-color: transparent;', 'border-color: transparent;');
assertStyles(styles, 'border: 1px solid transparent;', 'border: 1px solid transparent;');
assertStyles(styles, 'background: transparent;', 'background: transparent;');
assertStyles(styles, 'outline: 1px solid transparent;', 'outline: 1px solid transparent;');
assertStyles(styles, 'box-shadow: 1px 1px 1px transparent;', 'box-shadow: 1px 1px 1px transparent;');
assertStyles(styles, 'text-shadow: 1px 1px 1px transparent;', 'text-shadow: 1px 1px 1px transparent;');
assertStyles(styles, 'text-decoration-color: transparent;', 'text-decoration-color: transparent;');
});

it('TINY-10916: transparent should not be converted to other format when using set/get Content API', () => {
const editor = hook.editor();
Arr.each([
'<p style="color: transparent;">colour transparent</p>',
'<p style="background-color: transparent;">bg colour transparent</p>',
'<p style="border-color: transparent;">border colour transparent</p>',
'<p style="border: 1px solid transparent;">border transparent</p>',
`<p style="background: transparent;">bg transparent</p>`,
'<p style="outline: 1px solid transparent;">outline transparent</p>',
'<p style="box-shadow: 1px 1px 1px transparent;">box-shadow transparent</p>',
'<p style="text-shadow: 1px 1px 1px transparent;">text-shadow transparent</p>',
'<p style="text-decoration-color: transparent;">text-decoration-color transparent</p>'
], (content) => {
editor.setContent(content);
const result = editor.getContent();
Assertions.assertEq('Should not convert transparent to other format', content, result);
});
});

it('TINY-10916: only non calculative new colour foramt should be handled', () => {
const styles = Styles();
assertStyles(styles, 'color: rgb(0 0 0)', 'color: #000000;');
assertStyles(styles, 'color: rgb(0 0 0 / 0)', 'color: rgb(0 0 0 / 0);');
});
});

0 comments on commit 29292cc

Please sign in to comment.