Back out "Back out "[RN][CSS] Wire up native box shadow parsing"" (#49376)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49376

We can reland this now that D69520832 update feature flags infra.

Changelog: [Internal]

Reviewed By: mdvacca

Differential Revision: D69548370

fbshipit-source-id: ec1187dffdca4d9d4064235e9ef6336e66521d26
This commit is contained in:
Nick Gerleman
2025-02-12 16:59:44 -08:00
committed by Facebook GitHub Bot
parent 1d8021d821
commit 1663dc1978
5 changed files with 319 additions and 99 deletions
@@ -10,6 +10,7 @@
import type {PartialViewConfigWithoutName} from './PlatformBaseViewConfig';
import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags';
import ReactNativeStyleAttributes from '../Components/View/ReactNativeStyleAttributes';
import {DynamicallyInjectedByGestureHandler} from './ViewConfigIgnore';
@@ -170,7 +171,9 @@ const validAttributesForNonEventProps = {
process: require('../StyleSheet/processBackgroundImage').default,
},
boxShadow: {
process: require('../StyleSheet/processBoxShadow').default,
process: ReactNativeFeatureFlags.enableNativeCSSParsing()
? undefined
: require('../StyleSheet/processBoxShadow').default,
},
filter: {
process: require('../StyleSheet/processFilter').default,
@@ -10,6 +10,7 @@
import type {PartialViewConfigWithoutName} from './PlatformBaseViewConfig';
import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags';
import ReactNativeStyleAttributes from '../Components/View/ReactNativeStyleAttributes';
import {
ConditionallyIgnoredEventHandlers,
@@ -229,7 +230,9 @@ const validAttributesForNonEventProps = {
process: require('../StyleSheet/processFilter').default,
},
boxShadow: {
process: require('../StyleSheet/processBoxShadow').default,
process: ReactNativeFeatureFlags.enableNativeCSSParsing()
? undefined
: require('../StyleSheet/processBoxShadow').default,
},
mixBlendMode: true,
isolation: true,
@@ -10,6 +10,7 @@
#include <algorithm>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/components/view/BoxShadowPropsConversions.h>
#include <react/renderer/components/view/conversions.h>
#include <react/renderer/components/view/primitives.h>
#include <react/renderer/components/view/propsConversions.h>
@@ -0,0 +1,310 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <glog/logging.h>
#include <react/debug/react_native_expect.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/components/view/primitives.h>
#include <react/renderer/core/PropsParserContext.h>
#include <react/renderer/core/RawProps.h>
#include <react/renderer/core/graphicsConversions.h>
#include <react/renderer/css/CSSShadow.h>
#include <react/renderer/css/CSSValueParser.h>
#include <react/renderer/graphics/BoxShadow.h>
#include <react/renderer/graphics/PlatformColorParser.h>
#include <optional>
#include <string>
#include <unordered_map>
namespace facebook::react {
inline void parseProcessedBoxShadow(
const PropsParserContext& context,
const RawValue& value,
std::vector<BoxShadow>& result) {
react_native_expect(value.hasType<std::vector<RawValue>>());
if (!value.hasType<std::vector<RawValue>>()) {
result = {};
return;
}
std::vector<BoxShadow> boxShadows{};
auto rawBoxShadows = static_cast<std::vector<RawValue>>(value);
for (const auto& rawBoxShadow : rawBoxShadows) {
bool isMap =
rawBoxShadow.hasType<std::unordered_map<std::string, RawValue>>();
react_native_expect(isMap);
if (!isMap) {
// If any box shadow is malformed then we should not apply any of them
// which is the web behavior.
result = {};
return;
}
auto rawBoxShadowMap =
static_cast<std::unordered_map<std::string, RawValue>>(rawBoxShadow);
BoxShadow boxShadow{};
auto offsetX = rawBoxShadowMap.find("offsetX");
react_native_expect(offsetX != rawBoxShadowMap.end());
if (offsetX == rawBoxShadowMap.end()) {
result = {};
return;
}
react_native_expect(offsetX->second.hasType<Float>());
if (!offsetX->second.hasType<Float>()) {
result = {};
return;
}
boxShadow.offsetX = (Float)offsetX->second;
auto offsetY = rawBoxShadowMap.find("offsetY");
react_native_expect(offsetY != rawBoxShadowMap.end());
if (offsetY == rawBoxShadowMap.end()) {
result = {};
return;
}
react_native_expect(offsetY->second.hasType<Float>());
if (!offsetY->second.hasType<Float>()) {
result = {};
return;
}
boxShadow.offsetY = (Float)offsetY->second;
auto blurRadius = rawBoxShadowMap.find("blurRadius");
if (blurRadius != rawBoxShadowMap.end()) {
react_native_expect(blurRadius->second.hasType<Float>());
if (!blurRadius->second.hasType<Float>()) {
result = {};
return;
}
boxShadow.blurRadius = (Float)blurRadius->second;
}
auto spreadDistance = rawBoxShadowMap.find("spreadDistance");
if (spreadDistance != rawBoxShadowMap.end()) {
react_native_expect(spreadDistance->second.hasType<Float>());
if (!spreadDistance->second.hasType<Float>()) {
result = {};
return;
}
boxShadow.spreadDistance = (Float)spreadDistance->second;
}
auto inset = rawBoxShadowMap.find("inset");
if (inset != rawBoxShadowMap.end()) {
react_native_expect(inset->second.hasType<bool>());
if (!inset->second.hasType<bool>()) {
result = {};
return;
}
boxShadow.inset = (bool)inset->second;
}
auto color = rawBoxShadowMap.find("color");
if (color != rawBoxShadowMap.end()) {
fromRawValue(
context.contextContainer,
context.surfaceId,
color->second,
boxShadow.color);
}
boxShadows.push_back(boxShadow);
}
result = boxShadows;
}
inline SharedColor fromCSSColor(const CSSColor& cssColor) {
return hostPlatformColorFromRGBA(
cssColor.r, cssColor.g, cssColor.b, cssColor.a);
}
inline std::optional<BoxShadow> fromCSSShadow(const CSSShadow& cssShadow) {
// TODO: handle non-px values
if (cssShadow.offsetX.unit != CSSLengthUnit::Px ||
cssShadow.offsetY.unit != CSSLengthUnit::Px ||
cssShadow.blurRadius.unit != CSSLengthUnit::Px ||
cssShadow.spreadDistance.unit != CSSLengthUnit::Px) {
return {};
}
return BoxShadow{
.offsetX = cssShadow.offsetX.value,
.offsetY = cssShadow.offsetY.value,
.blurRadius = cssShadow.blurRadius.value,
.spreadDistance = cssShadow.spreadDistance.value,
.color = fromCSSColor(cssShadow.color),
};
}
inline void parseUnprocessedBoxShadowString(
std::string&& value,
std::vector<BoxShadow>& result) {
auto boxShadowList = parseCSSProperty<CSSShadowList>((std::string)value);
if (!std::holds_alternative<CSSShadowList>(boxShadowList)) {
result = {};
return;
}
for (const auto& cssShadow : std::get<CSSShadowList>(boxShadowList)) {
if (auto boxShadow = fromCSSShadow(cssShadow)) {
result.push_back(*boxShadow);
} else {
result = {};
return;
}
}
}
inline std::optional<Float> coerceLength(const RawValue& value) {
if (value.hasType<Float>()) {
return (Float)value;
}
if (value.hasType<std::string>()) {
auto len = parseCSSProperty<CSSLength>((std::string)value);
if (!std::holds_alternative<CSSLength>(len)) {
return {};
}
auto cssLen = std::get<CSSLength>(len);
if (cssLen.unit != CSSLengthUnit::Px) {
return {};
}
return cssLen.value;
}
return {};
}
inline std::optional<BoxShadow> parseBoxShadowRawValue(
const PropsParserContext& context,
const RawValue& value) {
if (!value.hasType<std::unordered_map<std::string, RawValue>>()) {
return {};
}
auto boxShadow = std::unordered_map<std::string, RawValue>(value);
auto rawOffsetX = boxShadow.find("offsetX");
if (rawOffsetX == boxShadow.end()) {
return {};
}
auto offsetX = coerceLength(rawOffsetX->second);
if (!offsetX.has_value()) {
return {};
}
auto rawOffsetY = boxShadow.find("offsetY");
if (rawOffsetY == boxShadow.end()) {
return {};
}
auto offsetY = coerceLength(rawOffsetY->second);
if (!offsetY.has_value()) {
return {};
}
Float blurRadius = 0;
auto rawBlurRadius = boxShadow.find("blurRadius");
if (rawBlurRadius != boxShadow.end()) {
if (auto blurRadiusValue = coerceLength(rawBlurRadius->second)) {
blurRadius = *blurRadiusValue;
} else {
return {};
}
}
Float spreadDistance = 0;
auto rawSpreadDistance = boxShadow.find("spreadDistance");
if (rawSpreadDistance != boxShadow.end()) {
if (auto spreadDistanceValue = coerceLength(rawSpreadDistance->second)) {
spreadDistance = *spreadDistanceValue;
} else {
return {};
}
}
bool inset = false;
auto rawInset = boxShadow.find("inset");
if (rawInset != boxShadow.end()) {
if (rawInset->second.hasType<bool>()) {
inset = (bool)rawInset->second;
} else {
return {};
}
}
SharedColor color;
auto rawColor = boxShadow.find("color");
if (rawColor != boxShadow.end()) {
const auto& rawColorValue = rawColor->second;
if (rawColorValue.hasType<std::string>()) {
auto cssColor = parseCSSProperty<CSSColor>((std::string)rawColorValue);
if (!std::holds_alternative<CSSColor>(cssColor)) {
return {};
}
color = fromCSSColor(std::get<CSSColor>(cssColor));
} else {
fromRawValue(
context.contextContainer, context.surfaceId, rawColor->second, color);
if (!color) {
return {};
}
}
}
return BoxShadow{
.offsetX = *offsetX,
.offsetY = *offsetY,
.blurRadius = blurRadius,
.spreadDistance = spreadDistance,
.color = color,
.inset = inset};
}
inline void parseUnprocessedBoxShadowList(
const PropsParserContext& context,
std::vector<RawValue>&& value,
std::vector<BoxShadow>& result) {
for (const auto& rawValue : value) {
if (auto boxShadow = parseBoxShadowRawValue(context, rawValue)) {
result.push_back(*boxShadow);
} else {
result = {};
return;
}
}
}
inline void parseUnprocessedBoxShadow(
const PropsParserContext& context,
const RawValue& value,
std::vector<BoxShadow>& result) {
if (value.hasType<std::string>()) {
parseUnprocessedBoxShadowString((std::string)value, result);
} else if (value.hasType<std::vector<RawValue>>()) {
parseUnprocessedBoxShadowList(
context, (std::vector<RawValue>)value, result);
} else {
result = {};
}
}
inline void fromRawValue(
const PropsParserContext& context,
const RawValue& value,
std::vector<BoxShadow>& result) {
if (ReactNativeFeatureFlags::enableNativeCSSParsing()) {
parseUnprocessedBoxShadow(context, value, result);
} else {
parseProcessedBoxShadow(context, value, result);
}
}
} // namespace facebook::react
@@ -16,7 +16,6 @@
#include <react/renderer/core/graphicsConversions.h>
#include <react/renderer/graphics/BackgroundImage.h>
#include <react/renderer/graphics/BlendMode.h>
#include <react/renderer/graphics/BoxShadow.h>
#include <react/renderer/graphics/Filter.h>
#include <react/renderer/graphics/Isolation.h>
#include <react/renderer/graphics/LinearGradient.h>
@@ -1053,102 +1052,6 @@ inline void fromRawValue(
}
}
inline void fromRawValue(
const PropsParserContext& context,
const RawValue& value,
std::vector<BoxShadow>& result) {
react_native_expect(value.hasType<std::vector<RawValue>>());
if (!value.hasType<std::vector<RawValue>>()) {
result = {};
return;
}
std::vector<BoxShadow> boxShadows{};
auto rawBoxShadows = static_cast<std::vector<RawValue>>(value);
for (const auto& rawBoxShadow : rawBoxShadows) {
bool isMap =
rawBoxShadow.hasType<std::unordered_map<std::string, RawValue>>();
react_native_expect(isMap);
if (!isMap) {
// If any box shadow is malformed then we should not apply any of them
// which is the web behavior.
result = {};
return;
}
auto rawBoxShadowMap =
static_cast<std::unordered_map<std::string, RawValue>>(rawBoxShadow);
BoxShadow boxShadow{};
auto offsetX = rawBoxShadowMap.find("offsetX");
react_native_expect(offsetX != rawBoxShadowMap.end());
if (offsetX == rawBoxShadowMap.end()) {
result = {};
return;
}
react_native_expect(offsetX->second.hasType<Float>());
if (!offsetX->second.hasType<Float>()) {
result = {};
return;
}
boxShadow.offsetX = (Float)offsetX->second;
auto offsetY = rawBoxShadowMap.find("offsetY");
react_native_expect(offsetY != rawBoxShadowMap.end());
if (offsetY == rawBoxShadowMap.end()) {
result = {};
return;
}
react_native_expect(offsetY->second.hasType<Float>());
if (!offsetY->second.hasType<Float>()) {
result = {};
return;
}
boxShadow.offsetY = (Float)offsetY->second;
auto blurRadius = rawBoxShadowMap.find("blurRadius");
if (blurRadius != rawBoxShadowMap.end()) {
react_native_expect(blurRadius->second.hasType<Float>());
if (!blurRadius->second.hasType<Float>()) {
result = {};
return;
}
boxShadow.blurRadius = (Float)blurRadius->second;
}
auto spreadDistance = rawBoxShadowMap.find("spreadDistance");
if (spreadDistance != rawBoxShadowMap.end()) {
react_native_expect(spreadDistance->second.hasType<Float>());
if (!spreadDistance->second.hasType<Float>()) {
result = {};
return;
}
boxShadow.spreadDistance = (Float)spreadDistance->second;
}
auto inset = rawBoxShadowMap.find("inset");
if (inset != rawBoxShadowMap.end()) {
react_native_expect(inset->second.hasType<bool>());
if (!inset->second.hasType<bool>()) {
result = {};
return;
}
boxShadow.inset = (bool)inset->second;
}
auto color = rawBoxShadowMap.find("color");
if (color != rawBoxShadowMap.end()) {
fromRawValue(
context.contextContainer,
context.surfaceId,
color->second,
boxShadow.color);
}
boxShadows.push_back(boxShadow);
}
result = boxShadows;
}
inline void fromRawValue(
const PropsParserContext& context,
const RawValue& value,