/* * 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 #include #include #include #include #include #include #include #include #include #include #include namespace facebook::react { inline void parseProcessedBoxShadow( const PropsParserContext& context, const RawValue& value, std::vector& result) { react_native_expect(value.hasType>()); if (!value.hasType>()) { result = {}; return; } std::vector boxShadows{}; auto rawBoxShadows = static_cast>(value); for (const auto& rawBoxShadow : rawBoxShadows) { bool isMap = rawBoxShadow.hasType>(); 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>(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()); if (!offsetX->second.hasType()) { 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()); if (!offsetY->second.hasType()) { result = {}; return; } boxShadow.offsetY = (Float)offsetY->second; auto blurRadius = rawBoxShadowMap.find("blurRadius"); if (blurRadius != rawBoxShadowMap.end()) { react_native_expect(blurRadius->second.hasType()); if (!blurRadius->second.hasType()) { result = {}; return; } boxShadow.blurRadius = (Float)blurRadius->second; } auto spreadDistance = rawBoxShadowMap.find("spreadDistance"); if (spreadDistance != rawBoxShadowMap.end()) { react_native_expect(spreadDistance->second.hasType()); if (!spreadDistance->second.hasType()) { result = {}; return; } boxShadow.spreadDistance = (Float)spreadDistance->second; } auto inset = rawBoxShadowMap.find("inset"); if (inset != rawBoxShadowMap.end()) { react_native_expect(inset->second.hasType()); if (!inset->second.hasType()) { 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 std::optional 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& result) { auto boxShadowList = parseCSSProperty((std::string)value); if (!std::holds_alternative(boxShadowList)) { result = {}; return; } for (const auto& cssShadow : std::get(boxShadowList)) { if (auto boxShadow = fromCSSShadow(cssShadow)) { result.push_back(*boxShadow); } else { result = {}; return; } } } inline std::optional parseBoxShadowRawValue( const PropsParserContext& context, const RawValue& value) { if (!value.hasType>()) { return {}; } auto boxShadow = std::unordered_map(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)) { if (*blurRadiusValue < 0) { return {}; } 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()) { inset = (bool)rawInset->second; } else { return {}; } } SharedColor color; auto rawColor = boxShadow.find("color"); if (rawColor != boxShadow.end()) { color = coerceColor(rawColor->second, context); 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&& value, std::vector& 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& result) { if (value.hasType()) { parseUnprocessedBoxShadowString((std::string)value, result); } else if (value.hasType>()) { parseUnprocessedBoxShadowList( context, (std::vector)value, result); } else { result = {}; } } inline void fromRawValue( const PropsParserContext& context, const RawValue& value, std::vector& result) { if (ReactNativeFeatureFlags::enableNativeCSSParsing()) { parseUnprocessedBoxShadow(context, value, result); } else { parseProcessedBoxShadow(context, value, result); } } } // namespace facebook::react