From bb6cd56fae4118f44ae47fd6978710a22f9e1510 Mon Sep 17 00:00:00 2001 From: Tim Yung Date: Fri, 2 Apr 2021 15:06:30 -0700 Subject: [PATCH] RN: Fallback for Invalid Colors in `processColorArray` Summary: If an invalid color is supplied to a native component that expects `Array`, it is currently possible to produce an array that contains null or undefined elements. This is problematic because the native component may not know what to do with the null or undefined value. This changes `processColorArray` to always return an array with valid color values. Any invalid color values will fallback to being transparent black, `0x00000000`. Changelog: [General][Fixed] - For native components that accept color arrays, invalid elements will now fallback to transparent with a console error. Reviewed By: JoshuaGross Differential Revision: D27542291 fbshipit-source-id: efa5d130644b3aee68d2b9fad6fdb61af11a2966 --- .../__tests__/processColorArray-test.js | 26 ++++++++++++++++++- Libraries/StyleSheet/processColorArray.js | 22 +++++++++++----- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/Libraries/StyleSheet/__tests__/processColorArray-test.js b/Libraries/StyleSheet/__tests__/processColorArray-test.js index 1be389cdb92421..3ccc582a11663b 100644 --- a/Libraries/StyleSheet/__tests__/processColorArray-test.js +++ b/Libraries/StyleSheet/__tests__/processColorArray-test.js @@ -22,7 +22,7 @@ const PlatformColorAndroid = require('../PlatformColorValueTypes.android') const platformSpecific = OS === 'android' - ? unsigned => unsigned | 0 //eslint-disable-line no-bitwise + ? unsigned => unsigned | 0 // eslint-disable-line no-bitwise : x => x; describe('processColorArray', () => { @@ -63,6 +63,30 @@ describe('processColorArray', () => { const colorFromNoArray = processColorArray(null); expect(colorFromNoArray).toEqual(null); }); + + it('converts invalid colors to transparent', () => { + const spy = jest.spyOn(console, 'error').mockReturnValue(undefined); + + const colors = ['red', '???', null, undefined, false]; + const colorFromStringArray = processColorArray(colors); + const expectedIntArray = [ + 0xffff0000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + ].map(platformSpecific); + expect(colorFromStringArray).toEqual(expectedIntArray); + + for (const color of colors.slice(1)) { + expect(spy).toHaveBeenCalledWith( + 'Invalid value in color array:', + color, + ); + } + + spy.mockRestore(); + }); }); describe('iOS', () => { diff --git a/Libraries/StyleSheet/processColorArray.js b/Libraries/StyleSheet/processColorArray.js index 0cbbdcd745b6b4..1d13d876c11df1 100644 --- a/Libraries/StyleSheet/processColorArray.js +++ b/Libraries/StyleSheet/processColorArray.js @@ -10,15 +10,25 @@ 'use strict'; -const processColor = require('./processColor'); - import type {ColorValue} from './StyleSheet'; -import type {ProcessedColorValue} from './processColor'; +import processColor, {type ProcessedColorValue} from './processColor'; + +const TRANSPARENT = 0; // rgba(0, 0, 0, 0) function processColorArray( - colors: ?Array, -): ?Array { - return colors == null ? null : colors.map(processColor); + colors: ?$ReadOnlyArray, +): ?$ReadOnlyArray { + return colors == null ? null : colors.map(processColorElement); +} + +function processColorElement(color: ColorValue): ProcessedColorValue { + const value = processColor(color); + // For invalid colors, fallback to transparent. + if (value == null) { + console.error('Invalid value in color array:', color); + return TRANSPARENT; + } + return value; } module.exports = processColorArray;