mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Reland 2: Wire up native box shadow parsing (#49503)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/49503 Relanding this change, now checking for the existing of FeatureFlags native module before checking it, with the assumption when the native module is unavailable (legacy arch) we will never do native processing, or even use the SVC. This aligns with the approach taken in `setupTimers` for the event loop, but is probably not the right long term solution for flags API. This also inlines a couple fixes originally up the stack, of missing propagation of inline state, and missing the gating in ReactNativeStyleAttributes. Changelog: [Internal] Reviewed By: joevilches Differential Revision: D69804412 fbshipit-source-id: 1f285994132cff75b6845b64cf26fceae37d4c92
This commit is contained in:
committed by
Facebook GitHub Bot
parent
b53e86b4bd
commit
8a2602fd69
+9
-1
@@ -10,6 +10,8 @@
|
||||
|
||||
import type {AnyAttributeType} from '../../Renderer/shims/ReactNativeTypes';
|
||||
|
||||
import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
|
||||
import NativeReactNativeFeatureFlags from '../../../src/private/featureflags/specs/NativeReactNativeFeatureFlags';
|
||||
import processAspectRatio from '../../StyleSheet/processAspectRatio';
|
||||
import processBackgroundImage from '../../StyleSheet/processBackgroundImage';
|
||||
import processBoxShadow from '../../StyleSheet/processBoxShadow';
|
||||
@@ -136,7 +138,13 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
|
||||
/*
|
||||
* BoxShadow
|
||||
*/
|
||||
boxShadow: {process: processBoxShadow},
|
||||
boxShadow:
|
||||
NativeReactNativeFeatureFlags != null &&
|
||||
ReactNativeFeatureFlags.enableNativeCSSParsing()
|
||||
? true
|
||||
: {
|
||||
process: processBoxShadow,
|
||||
},
|
||||
|
||||
/**
|
||||
* Linear Gradient
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
import type {PartialViewConfigWithoutName} from './PlatformBaseViewConfig';
|
||||
|
||||
import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags';
|
||||
import NativeReactNativeFeatureFlags from '../../src/private/featureflags/specs/NativeReactNativeFeatureFlags';
|
||||
import ReactNativeStyleAttributes from '../Components/View/ReactNativeStyleAttributes';
|
||||
import {DynamicallyInjectedByGestureHandler} from './ViewConfigIgnore';
|
||||
|
||||
@@ -169,9 +171,13 @@ const validAttributesForNonEventProps = {
|
||||
experimental_backgroundImage: {
|
||||
process: require('../StyleSheet/processBackgroundImage').default,
|
||||
},
|
||||
boxShadow: {
|
||||
process: require('../StyleSheet/processBoxShadow').default,
|
||||
},
|
||||
boxShadow:
|
||||
NativeReactNativeFeatureFlags != null &&
|
||||
ReactNativeFeatureFlags.enableNativeCSSParsing()
|
||||
? true
|
||||
: {
|
||||
process: require('../StyleSheet/processBoxShadow').default,
|
||||
},
|
||||
filter: {
|
||||
process: require('../StyleSheet/processFilter').default,
|
||||
},
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
import type {PartialViewConfigWithoutName} from './PlatformBaseViewConfig';
|
||||
|
||||
import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags';
|
||||
import NativeReactNativeFeatureFlags from '../../src/private/featureflags/specs/NativeReactNativeFeatureFlags';
|
||||
import ReactNativeStyleAttributes from '../Components/View/ReactNativeStyleAttributes';
|
||||
import {
|
||||
ConditionallyIgnoredEventHandlers,
|
||||
@@ -228,9 +230,13 @@ const validAttributesForNonEventProps = {
|
||||
filter: {
|
||||
process: require('../StyleSheet/processFilter').default,
|
||||
},
|
||||
boxShadow: {
|
||||
process: require('../StyleSheet/processBoxShadow').default,
|
||||
},
|
||||
boxShadow:
|
||||
NativeReactNativeFeatureFlags != null &&
|
||||
ReactNativeFeatureFlags.enableNativeCSSParsing()
|
||||
? true
|
||||
: {
|
||||
process: 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>
|
||||
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* 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),
|
||||
.inset = cssShadow.inset,
|
||||
};
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user