Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -136,7 +138,13 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
/*
* BoxShadow
*/
boxShadow: {process: processBoxShadow},
boxShadow:
NativeReactNativeFeatureFlags != null &&
ReactNativeFeatureFlags.enableNativeCSSParsing()
? true
: {
process: processBoxShadow,
},

/**
* Linear Gradient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading