277 lines
7.7 KiB
C++
277 lines
7.7 KiB
C++
/*
|
|
* 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/CSSConversions.h>
|
|
#include <react/renderer/core/PropsParserContext.h>
|
|
#include <react/renderer/core/RawProps.h>
|
|
#include <react/renderer/css/CSSShadow.h>
|
|
#include <react/renderer/css/CSSValueParser.h>
|
|
#include <react/renderer/graphics/BoxShadow.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 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<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)) {
|
|
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<bool>()) {
|
|
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<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
|