diff --git a/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp b/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp index 123b1410b4bc85..01c76f6385e17f 100644 --- a/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp +++ b/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp @@ -15,134 +15,196 @@ namespace facebook { namespace react { +bool AccessibilityProps::enablePropIteratorSetter = false; + AccessibilityProps::AccessibilityProps( const PropsParserContext &context, AccessibilityProps const &sourceProps, RawProps const &rawProps) - : accessible(convertRawProp( - context, - rawProps, - "accessible", - sourceProps.accessible, - false)), - accessibilityState(convertRawProp( - context, - rawProps, - "accessibilityState", - sourceProps.accessibilityState, - {})), - accessibilityLabel(convertRawProp( - context, - rawProps, - "accessibilityLabel", - sourceProps.accessibilityLabel, - "")), - accessibilityLabelledBy(convertRawProp( - context, - rawProps, - "accessibilityLabelledBy", - sourceProps.accessibilityLabelledBy, - {})), - accessibilityLiveRegion(convertRawProp( - context, - rawProps, - "accessibilityLiveRegion", - sourceProps.accessibilityLiveRegion, - AccessibilityLiveRegion::None)), - accessibilityHint(convertRawProp( - context, - rawProps, - "accessibilityHint", - sourceProps.accessibilityHint, - "")), - accessibilityLanguage(convertRawProp( - context, - rawProps, - "accessibilityLanguage", - sourceProps.accessibilityLanguage, - "")), - accessibilityValue(convertRawProp( - context, - rawProps, - "accessibilityValue", - sourceProps.accessibilityValue, - {})), - accessibilityActions(convertRawProp( - context, - rawProps, - "accessibilityActions", - sourceProps.accessibilityActions, - {})), - accessibilityViewIsModal(convertRawProp( - context, - rawProps, - "accessibilityViewIsModal", - sourceProps.accessibilityViewIsModal, - false)), - accessibilityElementsHidden(convertRawProp( - context, - rawProps, - "accessibilityElementsHidden", - sourceProps.accessibilityElementsHidden, - false)), - accessibilityIgnoresInvertColors(convertRawProp( - context, - rawProps, - "accessibilityIgnoresInvertColors", - sourceProps.accessibilityIgnoresInvertColors, - false)), - onAccessibilityTap(convertRawProp( - context, - rawProps, - "onAccessibilityTap", - sourceProps.onAccessibilityTap, - {})), - onAccessibilityMagicTap(convertRawProp( - context, - rawProps, - "onAccessibilityMagicTap", - sourceProps.onAccessibilityMagicTap, - {})), - onAccessibilityEscape(convertRawProp( - context, - rawProps, - "onAccessibilityEscape", - sourceProps.onAccessibilityEscape, - {})), - onAccessibilityAction(convertRawProp( - context, - rawProps, - "onAccessibilityAction", - sourceProps.onAccessibilityAction, - {})), - importantForAccessibility(convertRawProp( - context, - rawProps, - "importantForAccessibility", - sourceProps.importantForAccessibility, - ImportantForAccessibility::Auto)), + : accessible( + enablePropIteratorSetter ? sourceProps.accessible + : convertRawProp( + context, + rawProps, + "accessible", + sourceProps.accessible, + false)), + accessibilityState( + enablePropIteratorSetter ? sourceProps.accessibilityState + : convertRawProp( + context, + rawProps, + "accessibilityState", + sourceProps.accessibilityState, + {})), + accessibilityLabel( + enablePropIteratorSetter ? sourceProps.accessibilityLabel + : convertRawProp( + context, + rawProps, + "accessibilityLabel", + sourceProps.accessibilityLabel, + "")), + accessibilityLabelledBy( + enablePropIteratorSetter ? sourceProps.accessibilityLabelledBy + : convertRawProp( + context, + rawProps, + "accessibilityLabelledBy", + sourceProps.accessibilityLabelledBy, + {})), + accessibilityLiveRegion( + enablePropIteratorSetter ? sourceProps.accessibilityLiveRegion + : convertRawProp( + context, + rawProps, + "accessibilityLiveRegion", + sourceProps.accessibilityLiveRegion, + AccessibilityLiveRegion::None)), + accessibilityHint( + enablePropIteratorSetter ? sourceProps.accessibilityHint + : convertRawProp( + context, + rawProps, + "accessibilityHint", + sourceProps.accessibilityHint, + "")), + accessibilityLanguage( + enablePropIteratorSetter ? sourceProps.accessibilityLanguage + : convertRawProp( + context, + rawProps, + "accessibilityLanguage", + sourceProps.accessibilityLanguage, + "")), + accessibilityValue( + enablePropIteratorSetter ? sourceProps.accessibilityValue + : convertRawProp( + context, + rawProps, + "accessibilityValue", + sourceProps.accessibilityValue, + {})), + accessibilityActions( + enablePropIteratorSetter ? sourceProps.accessibilityActions + : convertRawProp( + context, + rawProps, + "accessibilityActions", + sourceProps.accessibilityActions, + {})), + accessibilityViewIsModal( + enablePropIteratorSetter ? sourceProps.accessibilityViewIsModal + : convertRawProp( + context, + rawProps, + "accessibilityViewIsModal", + sourceProps.accessibilityViewIsModal, + false)), + accessibilityElementsHidden( + enablePropIteratorSetter + ? sourceProps.accessibilityElementsHidden + : convertRawProp( + context, + rawProps, + "accessibilityElementsHidden", + sourceProps.accessibilityElementsHidden, + false)), + accessibilityIgnoresInvertColors( + enablePropIteratorSetter + ? sourceProps.accessibilityIgnoresInvertColors + : convertRawProp( + context, + rawProps, + "accessibilityIgnoresInvertColors", + sourceProps.accessibilityIgnoresInvertColors, + false)), + onAccessibilityTap( + enablePropIteratorSetter ? sourceProps.onAccessibilityTap + : convertRawProp( + context, + rawProps, + "onAccessibilityTap", + sourceProps.onAccessibilityTap, + {})), + onAccessibilityMagicTap( + enablePropIteratorSetter ? sourceProps.onAccessibilityMagicTap + : convertRawProp( + context, + rawProps, + "onAccessibilityMagicTap", + sourceProps.onAccessibilityMagicTap, + {})), + onAccessibilityEscape( + enablePropIteratorSetter ? sourceProps.onAccessibilityEscape + : convertRawProp( + context, + rawProps, + "onAccessibilityEscape", + sourceProps.onAccessibilityEscape, + {})), + onAccessibilityAction( + enablePropIteratorSetter ? sourceProps.onAccessibilityAction + : convertRawProp( + context, + rawProps, + "onAccessibilityAction", + sourceProps.onAccessibilityAction, + {})), + importantForAccessibility( + enablePropIteratorSetter ? sourceProps.importantForAccessibility + : convertRawProp( + context, + rawProps, + "importantForAccessibility", + sourceProps.importantForAccessibility, + ImportantForAccessibility::Auto)), testId( - convertRawProp(context, rawProps, "testID", sourceProps.testId, "")) { - // It is a (severe!) perf deoptimization to request props out-of-order. - // Thus, since we need to request the same prop twice here - // (accessibilityRole) we "must" do them subsequently here to prevent - // a regression. It is reasonable to ask if the `at` function can be improved; - // it probably can, but this is a fairly rare edge-case that (1) is easy-ish - // to work around here, and (2) would require very careful work to address - // this case and not regress the more common cases. - const auto *rawPropValue = rawProps.at("accessibilityRole", nullptr, nullptr); - AccessibilityTraits traits; - std::string roleString; - if (rawPropValue == nullptr || !rawPropValue->hasValue()) { - traits = AccessibilityTraits::None; - roleString = ""; - } else { - fromRawValue(context, *rawPropValue, traits); - fromRawValue(context, *rawPropValue, roleString); - } + enablePropIteratorSetter ? sourceProps.testId + : convertRawProp( + context, + rawProps, + "testID", + sourceProps.testId, + "")) {} - accessibilityTraits = traits; - accessibilityRole = roleString; +void AccessibilityProps::setProp( + const PropsParserContext &context, + RawPropsPropNameHash hash, + const char *propName, + RawValue const &value) { + switch (hash) { + RAW_SET_PROP_SWITCH_CASE_BASIC(accessible, false); + RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityState, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLabel, std::string{""}); + RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLabelledBy, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityHint, std::string{""}); + RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLanguage, std::string{""}); + RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityValue, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityActions, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityViewIsModal, false); + RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityElementsHidden, false); + RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityIgnoresInvertColors, false); + RAW_SET_PROP_SWITCH_CASE_BASIC(onAccessibilityTap, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(onAccessibilityMagicTap, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(onAccessibilityEscape, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(onAccessibilityAction, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC( + importantForAccessibility, ImportantForAccessibility::Auto); + RAW_SET_PROP_SWITCH_CASE(testId, "testID", std::string{""}); + case CONSTEXPR_RAW_PROPS_KEY_HASH("accessibilityRole"): { + AccessibilityTraits traits = AccessibilityTraits::None; + std::string roleString = ""; + if (value.hasValue()) { + fromRawValue(context, value, traits); + fromRawValue(context, value, roleString); + } + + accessibilityTraits = traits; + accessibilityRole = roleString; + return; + } + } } #pragma mark - DebugStringConvertible diff --git a/ReactCommon/react/renderer/components/view/AccessibilityProps.h b/ReactCommon/react/renderer/components/view/AccessibilityProps.h index 050b0f1161cde5..e9e6aa7b3f09ec 100644 --- a/ReactCommon/react/renderer/components/view/AccessibilityProps.h +++ b/ReactCommon/react/renderer/components/view/AccessibilityProps.h @@ -24,6 +24,14 @@ class AccessibilityProps { AccessibilityProps const &sourceProps, RawProps const &rawProps); + void setProp( + const PropsParserContext &context, + RawPropsPropNameHash hash, + const char *propName, + RawValue const &value); + + static bool enablePropIteratorSetter; + #pragma mark - Props bool accessible{false}; diff --git a/ReactCommon/react/renderer/components/view/ViewProps.cpp b/ReactCommon/react/renderer/components/view/ViewProps.cpp index e09c9a0abcf34d..f95fa3f0003084 100644 --- a/ReactCommon/react/renderer/components/view/ViewProps.cpp +++ b/ReactCommon/react/renderer/components/view/ViewProps.cpp @@ -25,167 +25,331 @@ ViewProps::ViewProps( bool shouldSetRawProps) : YogaStylableProps(context, sourceProps, rawProps, shouldSetRawProps), AccessibilityProps(context, sourceProps, rawProps), - opacity(convertRawProp( - context, - rawProps, - "opacity", - sourceProps.opacity, - (Float)1.0)), - foregroundColor(convertRawProp( - context, - rawProps, - "foregroundColor", - sourceProps.foregroundColor, - {})), - backgroundColor(convertRawProp( - context, - rawProps, - "backgroundColor", - sourceProps.backgroundColor, - {})), - borderRadii(convertRawProp( - context, - rawProps, - "border", - "Radius", - sourceProps.borderRadii, - {})), - borderColors(convertRawProp( - context, - rawProps, - "border", - "Color", - sourceProps.borderColors, - {})), - borderStyles(convertRawProp( - context, - rawProps, - "border", - "Style", - sourceProps.borderStyles, - {})), - shadowColor(convertRawProp( - context, - rawProps, - "shadowColor", - sourceProps.shadowColor, - {})), - shadowOffset(convertRawProp( - context, - rawProps, - "shadowOffset", - sourceProps.shadowOffset, - {})), - shadowOpacity(convertRawProp( - context, - rawProps, - "shadowOpacity", - sourceProps.shadowOpacity, - {})), - shadowRadius(convertRawProp( - context, - rawProps, - "shadowRadius", - sourceProps.shadowRadius, - {})), - transform(convertRawProp( - context, - rawProps, - "transform", - sourceProps.transform, - {})), - backfaceVisibility(convertRawProp( - context, - rawProps, - "backfaceVisibility", - sourceProps.backfaceVisibility, - {})), - shouldRasterize(convertRawProp( - context, - rawProps, - "shouldRasterize", - sourceProps.shouldRasterize, - {})), + opacity( + Props::enablePropIteratorSetter ? sourceProps.opacity + : convertRawProp( + context, + rawProps, + "opacity", + sourceProps.opacity, + (Float)1.0)), + foregroundColor( + Props::enablePropIteratorSetter ? sourceProps.foregroundColor + : convertRawProp( + context, + rawProps, + "foregroundColor", + sourceProps.foregroundColor, + {})), + backgroundColor( + Props::enablePropIteratorSetter ? sourceProps.backgroundColor + : convertRawProp( + context, + rawProps, + "backgroundColor", + sourceProps.backgroundColor, + {})), + borderRadii( + Props::enablePropIteratorSetter ? sourceProps.borderRadii + : convertRawProp( + context, + rawProps, + "border", + "Radius", + sourceProps.borderRadii, + {})), + borderColors( + Props::enablePropIteratorSetter ? sourceProps.borderColors + : convertRawProp( + context, + rawProps, + "border", + "Color", + sourceProps.borderColors, + {})), + borderStyles( + Props::enablePropIteratorSetter ? sourceProps.borderStyles + : convertRawProp( + context, + rawProps, + "border", + "Style", + sourceProps.borderStyles, + {})), + shadowColor( + Props::enablePropIteratorSetter ? sourceProps.shadowColor + : convertRawProp( + context, + rawProps, + "shadowColor", + sourceProps.shadowColor, + {})), + shadowOffset( + Props::enablePropIteratorSetter ? sourceProps.shadowOffset + : convertRawProp( + context, + rawProps, + "shadowOffset", + sourceProps.shadowOffset, + {})), + shadowOpacity( + Props::enablePropIteratorSetter ? sourceProps.shadowOpacity + : convertRawProp( + context, + rawProps, + "shadowOpacity", + sourceProps.shadowOpacity, + {})), + shadowRadius( + Props::enablePropIteratorSetter ? sourceProps.shadowRadius + : convertRawProp( + context, + rawProps, + "shadowRadius", + sourceProps.shadowRadius, + {})), + transform( + Props::enablePropIteratorSetter ? sourceProps.transform + : convertRawProp( + context, + rawProps, + "transform", + sourceProps.transform, + {})), + backfaceVisibility( + Props::enablePropIteratorSetter ? sourceProps.backfaceVisibility + : convertRawProp( + context, + rawProps, + "backfaceVisibility", + sourceProps.backfaceVisibility, + {})), + shouldRasterize( + Props::enablePropIteratorSetter ? sourceProps.shouldRasterize + : convertRawProp( + context, + rawProps, + "shouldRasterize", + sourceProps.shouldRasterize, + {})), zIndex( - convertRawProp(context, rawProps, "zIndex", sourceProps.zIndex, {})), - pointerEvents(convertRawProp( - context, - rawProps, - "pointerEvents", - sourceProps.pointerEvents, - {})), - hitSlop(convertRawProp( - context, - rawProps, - "hitSlop", - sourceProps.hitSlop, - {})), - onLayout(convertRawProp( - context, - rawProps, - "onLayout", - sourceProps.onLayout, - {})), - events(convertRawProp(context, rawProps, sourceProps.events, {})), - collapsable(convertRawProp( - context, - rawProps, - "collapsable", - sourceProps.collapsable, - true)), - removeClippedSubviews(convertRawProp( - context, - rawProps, - "removeClippedSubviews", - sourceProps.removeClippedSubviews, - false)) + Props::enablePropIteratorSetter ? sourceProps.zIndex + : convertRawProp( + context, + rawProps, + "zIndex", + sourceProps.zIndex, + {})), + pointerEvents( + Props::enablePropIteratorSetter ? sourceProps.pointerEvents + : convertRawProp( + context, + rawProps, + "pointerEvents", + sourceProps.pointerEvents, + {})), + hitSlop( + Props::enablePropIteratorSetter ? sourceProps.hitSlop + : convertRawProp( + context, + rawProps, + "hitSlop", + sourceProps.hitSlop, + {})), + onLayout( + Props::enablePropIteratorSetter ? sourceProps.onLayout + : convertRawProp( + context, + rawProps, + "onLayout", + sourceProps.onLayout, + {})), + events( + Props::enablePropIteratorSetter + ? sourceProps.events + : convertRawProp(context, rawProps, sourceProps.events, {})), + collapsable( + Props::enablePropIteratorSetter ? sourceProps.collapsable + : convertRawProp( + context, + rawProps, + "collapsable", + sourceProps.collapsable, + true)), + removeClippedSubviews( + Props::enablePropIteratorSetter + ? sourceProps.removeClippedSubviews + : convertRawProp( + context, + rawProps, + "removeClippedSubviews", + sourceProps.removeClippedSubviews, + false)) #ifdef ANDROID , - elevation(convertRawProp( - context, - rawProps, - "elevation", - sourceProps.elevation, - {})), - nativeBackground(convertRawProp( - context, - rawProps, - "nativeBackgroundAndroid", - sourceProps.nativeBackground, - {})), - nativeForeground(convertRawProp( - context, - rawProps, - "nativeForegroundAndroid", - sourceProps.nativeForeground, - {})), - focusable(convertRawProp( - context, - rawProps, - "focusable", - sourceProps.focusable, - {})), - hasTVPreferredFocus(convertRawProp( - context, - rawProps, - "hasTVPreferredFocus", - sourceProps.hasTVPreferredFocus, - {})), - needsOffscreenAlphaCompositing(convertRawProp( - context, - rawProps, - "needsOffscreenAlphaCompositing", - sourceProps.needsOffscreenAlphaCompositing, - {})), - renderToHardwareTextureAndroid(convertRawProp( - context, - rawProps, - "renderToHardwareTextureAndroid", - sourceProps.renderToHardwareTextureAndroid, - {})) + elevation( + Props::enablePropIteratorSetter ? sourceProps.elevation + : convertRawProp( + context, + rawProps, + "elevation", + sourceProps.elevation, + {})), + nativeBackground( + Props::enablePropIteratorSetter ? sourceProps.nativeBackground + : convertRawProp( + context, + rawProps, + "nativeBackgroundAndroid", + sourceProps.nativeBackground, + {})), + nativeForeground( + Props::enablePropIteratorSetter ? sourceProps.nativeForeground + : convertRawProp( + context, + rawProps, + "nativeForegroundAndroid", + sourceProps.nativeForeground, + {})), + focusable( + Props::enablePropIteratorSetter ? sourceProps.focusable + : convertRawProp( + context, + rawProps, + "focusable", + sourceProps.focusable, + {})), + hasTVPreferredFocus( + Props::enablePropIteratorSetter ? sourceProps.hasTVPreferredFocus + : convertRawProp( + context, + rawProps, + "hasTVPreferredFocus", + sourceProps.hasTVPreferredFocus, + {})), + needsOffscreenAlphaCompositing( + Props::enablePropIteratorSetter + ? sourceProps.needsOffscreenAlphaCompositing + : convertRawProp( + context, + rawProps, + "needsOffscreenAlphaCompositing", + sourceProps.needsOffscreenAlphaCompositing, + {})), + renderToHardwareTextureAndroid( + Props::enablePropIteratorSetter + ? sourceProps.renderToHardwareTextureAndroid + : convertRawProp( + context, + rawProps, + "renderToHardwareTextureAndroid", + sourceProps.renderToHardwareTextureAndroid, + {})) + #endif {}; +#define VIEW_EVENT_CASE(eventType, eventString) \ + case CONSTEXPR_RAW_PROPS_KEY_HASH(eventString): { \ + ViewEvents defaultViewEvents{}; \ + events[eventType] = ({ \ + bool res = defaultViewEvents[eventType]; \ + if (value.hasValue()) { \ + fromRawValue(context, value, res); \ + } \ + res; \ + }); \ + return; \ + } + +void ViewProps::setProp( + const PropsParserContext &context, + RawPropsPropNameHash hash, + const char *propName, + RawValue const &value) { + // All Props structs setProp methods must always, unconditionally, + // call all super::setProp methods, since multiple structs may + // reuse the same values. + YogaStylableProps::setProp(context, hash, propName, value); + AccessibilityProps::setProp(context, hash, propName, value); + + switch (hash) { + RAW_SET_PROP_SWITCH_CASE_BASIC(opacity, (Float)1.0); + RAW_SET_PROP_SWITCH_CASE_BASIC(foregroundColor, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(backgroundColor, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(shadowColor, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(shadowOffset, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(shadowOpacity, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(shadowRadius, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(transform, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(backfaceVisibility, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(shouldRasterize, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(zIndex, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(pointerEvents, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(hitSlop, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(onLayout, {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(collapsable, true); + RAW_SET_PROP_SWITCH_CASE_BASIC(removeClippedSubviews, false); + // events field + VIEW_EVENT_CASE(ViewEvents::Offset::PointerEnter, "onPointerEnter"); + VIEW_EVENT_CASE(ViewEvents::Offset::PointerMove, "onPointerMove"); + VIEW_EVENT_CASE(ViewEvents::Offset::PointerLeave, "onPointerLeave"); + VIEW_EVENT_CASE(ViewEvents::Offset::PointerEnter2, "onPointerEnter2"); + VIEW_EVENT_CASE( + ViewEvents::Offset::PointerEnter2Capture, "onPointerEnter2Capture"); + VIEW_EVENT_CASE(ViewEvents::Offset::PointerMove2, "onPointerMove2"); + VIEW_EVENT_CASE( + ViewEvents::Offset::PointerMove2Capture, "onPointerMove2Capture"); + VIEW_EVENT_CASE(ViewEvents::Offset::PointerLeave2, "onPointerLeave2"); + VIEW_EVENT_CASE( + ViewEvents::Offset::PointerLeave2Capture, "onPointerLeave2Capture"); + VIEW_EVENT_CASE(ViewEvents::Offset::PointerOver, "onPointerOver"); + VIEW_EVENT_CASE(ViewEvents::Offset::PointerOut, "onPointerOut"); + VIEW_EVENT_CASE( + ViewEvents::Offset::MoveShouldSetResponder, "onMoveShouldSetResponder"); + VIEW_EVENT_CASE( + ViewEvents::Offset::MoveShouldSetResponderCapture, + "onMoveShouldSetResponderCapture"); + VIEW_EVENT_CASE( + ViewEvents::Offset::StartShouldSetResponder, + "onStartShouldSetResponder"); + VIEW_EVENT_CASE( + ViewEvents::Offset::StartShouldSetResponderCapture, + "onStartShouldSetResponderCapture"); + VIEW_EVENT_CASE(ViewEvents::Offset::ResponderGrant, "onResponderGrant"); + VIEW_EVENT_CASE(ViewEvents::Offset::ResponderReject, "onResponderReject"); + VIEW_EVENT_CASE(ViewEvents::Offset::ResponderStart, "onResponderStart"); + VIEW_EVENT_CASE(ViewEvents::Offset::ResponderEnd, "onResponderEnd"); + VIEW_EVENT_CASE(ViewEvents::Offset::ResponderRelease, "onResponderRelease"); + VIEW_EVENT_CASE(ViewEvents::Offset::ResponderMove, "ResponderMove"); + VIEW_EVENT_CASE( + ViewEvents::Offset::ResponderTerminate, "onResponderTerminate"); + VIEW_EVENT_CASE( + ViewEvents::Offset::ResponderTerminationRequest, + "onResponderTerminationRequest"); + VIEW_EVENT_CASE( + ViewEvents::Offset::ShouldBlockNativeResponder, + "onShouldBlockNativeResponder"); + VIEW_EVENT_CASE(ViewEvents::Offset::TouchStart, "onTouchStart"); + VIEW_EVENT_CASE(ViewEvents::Offset::TouchMove, "onTouchMove"); + VIEW_EVENT_CASE(ViewEvents::Offset::TouchEnd, "onTouchEnd"); + VIEW_EVENT_CASE(ViewEvents::Offset::TouchCancel, "onTouchCancel"); +#ifdef ANDROID + RAW_SET_PROP_SWITCH_CASE_BASIC(elevation, {}); + RAW_SET_PROP_SWITCH_CASE(nativeBackground, "nativeBackgroundAndroid", {}); + RAW_SET_PROP_SWITCH_CASE(nativeForeground, "nativeForegroundAndroid", {}); + RAW_SET_PROP_SWITCH_CASE_BASIC(focusable, false); + RAW_SET_PROP_SWITCH_CASE_BASIC(hasTVPreferredFocus, false); + RAW_SET_PROP_SWITCH_CASE_BASIC(needsOffscreenAlphaCompositing, false); + RAW_SET_PROP_SWITCH_CASE_BASIC(renderToHardwareTextureAndroid, false); +#endif + // BorderRadii + SET_CASCADED_RECTANGLE_CORNERS(borderRadii, "border", "Radius", value); + SET_CASCADED_RECTANGLE_EDGES(borderColors, "border", "Color", value); + SET_CASCADED_RECTANGLE_EDGES(borderStyles, "border", "Style", value); + } +} + #pragma mark - Convenience Methods static BorderRadii ensureNoOverlap(BorderRadii const &radii, Size const &size) { diff --git a/ReactCommon/react/renderer/components/view/ViewProps.h b/ReactCommon/react/renderer/components/view/ViewProps.h index be31a70f787cfd..21a425dc73fe7e 100644 --- a/ReactCommon/react/renderer/components/view/ViewProps.h +++ b/ReactCommon/react/renderer/components/view/ViewProps.h @@ -35,6 +35,12 @@ class ViewProps : public YogaStylableProps, public AccessibilityProps { RawProps const &rawProps, bool shouldSetRawProps = true); + void setProp( + const PropsParserContext &context, + RawPropsPropNameHash hash, + const char *propName, + RawValue const &value); + #pragma mark - Props // Color diff --git a/ReactCommon/react/renderer/components/view/propsConversions.h b/ReactCommon/react/renderer/components/view/propsConversions.h index 413c29936ba1d8..eb56b9b9701cf6 100644 --- a/ReactCommon/react/renderer/components/view/propsConversions.h +++ b/ReactCommon/react/renderer/components/view/propsConversions.h @@ -301,6 +301,7 @@ static inline YGStyle convertRawProp( return yogaStyle; } +// This can be deleted when non-iterator ViewProp parsing is deleted template static inline CascadedRectangleCorners convertRawProp( const PropsParserContext &context, @@ -465,6 +466,7 @@ static inline CascadedRectangleEdges convertRawProp( return result; } +// This can be deleted when non-iterator ViewProp parsing is deleted static inline ViewEvents convertRawProp( const PropsParserContext &context, RawProps const &rawProps, diff --git a/ReactCommon/react/renderer/core/ConcreteComponentDescriptor.h b/ReactCommon/react/renderer/core/ConcreteComponentDescriptor.h index e2d515543d1dd6..b49db6571e414c 100644 --- a/ReactCommon/react/renderer/core/ConcreteComponentDescriptor.h +++ b/ReactCommon/react/renderer/core/ConcreteComponentDescriptor.h @@ -109,7 +109,21 @@ class ConcreteComponentDescriptor : public ComponentDescriptor { rawProps.parse(rawPropsParser_, context); - return ShadowNodeT::Props(context, rawProps, props); + // Call old-style constructor + auto shadowNodeProps = ShadowNodeT::Props(context, rawProps, props); + + // Use the new-style iterator + // Note that we just check if `Props` has this flag set, no matter + // the type of ShadowNode; it acts as the single global flag. + if (Props::enablePropIteratorSetter) { + rawProps.iterateOverValues([&](RawPropsPropNameHash hash, + const char *propName, + RawValue const &fn) { + shadowNodeProps.get()->setProp(context, hash, propName, fn); + }); + } + + return shadowNodeProps; }; SharedProps interpolateProps( diff --git a/ReactCommon/react/renderer/core/ConcreteShadowNode.h b/ReactCommon/react/renderer/core/ConcreteShadowNode.h index 934de8d84bade8..bb5b022b8cbab5 100644 --- a/ReactCommon/react/renderer/core/ConcreteShadowNode.h +++ b/ReactCommon/react/renderer/core/ConcreteShadowNode.h @@ -47,6 +47,7 @@ class ConcreteShadowNode : public BaseShadowNodeT { using ConcreteProps = PropsT; using SharedConcreteProps = std::shared_ptr; + using UnsharedConcreteProps = std::shared_ptr; using ConcreteEventEmitter = EventEmitterT; using SharedConcreteEventEmitter = std::shared_ptr; using SharedConcreteShadowNode = std::shared_ptr; @@ -69,11 +70,11 @@ class ConcreteShadowNode : public BaseShadowNodeT { return BaseShadowNodeT::BaseTraits(); } - static SharedConcreteProps Props( + static UnsharedConcreteProps Props( const PropsParserContext &context, RawProps const &rawProps, SharedProps const &baseProps = nullptr) { - return std::make_shared( + return std::make_shared( context, baseProps ? static_cast(*baseProps) : PropsT(), rawProps); diff --git a/ReactCommon/react/renderer/core/Props.cpp b/ReactCommon/react/renderer/core/Props.cpp index d1b5ff7e151c8c..ad1c01c5c2855f 100644 --- a/ReactCommon/react/renderer/core/Props.cpp +++ b/ReactCommon/react/renderer/core/Props.cpp @@ -13,17 +13,21 @@ namespace facebook { namespace react { +bool Props::enablePropIteratorSetter = false; + Props::Props( const PropsParserContext &context, const Props &sourceProps, const RawProps &rawProps, const bool shouldSetRawProps) - : nativeId(convertRawProp( - context, - rawProps, - "nativeID", - sourceProps.nativeId, - {})), + : nativeId( + enablePropIteratorSetter ? sourceProps.nativeId + : convertRawProp( + context, + rawProps, + "nativeID", + sourceProps.nativeId, + {})), revision(sourceProps.revision + 1) #ifdef ANDROID , @@ -31,7 +35,20 @@ Props::Props( shouldSetRawProps ? (folly::dynamic)rawProps : /* null */ folly::dynamic()) #endif - {}; +{ +} + +void Props::setProp( + const PropsParserContext &context, + RawPropsPropNameHash hash, + const char *propName, + RawValue const &value) { + switch (hash) { + case CONSTEXPR_RAW_PROPS_KEY_HASH("nativeID"): + fromRawValue(context, value, nativeId, {}); + return; + } +} } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/core/Props.h b/ReactCommon/react/renderer/core/Props.h index 987c4228a7fe9b..045de81dc1790e 100644 --- a/ReactCommon/react/renderer/core/Props.h +++ b/ReactCommon/react/renderer/core/Props.h @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -37,6 +38,24 @@ class Props : public virtual Sealable, public virtual DebugStringConvertible { bool shouldSetRawProps = true); virtual ~Props() = default; + static bool enablePropIteratorSetter; + + /** + * Set a prop value via iteration (see enableIterator above). + * If setProp is defined for a particular props struct, it /must/ + * be called every time setProp is called on the hierarchy. + * For example, ViewProps overrides setProp and so ViewProps must + * explicitly call Props::setProp every time ViewProps::setProp is + * called. This is because a single prop from JS can be reused + * multiple times for different values in the hierarchy. For example, if + * ViewProps uses "propX", Props may also use "propX". + */ + void setProp( + const PropsParserContext &context, + RawPropsPropNameHash hash, + const char *propName, + RawValue const &value); + std::string nativeId; /* diff --git a/ReactCommon/react/renderer/core/PropsMacros.h b/ReactCommon/react/renderer/core/PropsMacros.h new file mode 100644 index 00000000000000..63625c66b17ce9 --- /dev/null +++ b/ReactCommon/react/renderer/core/PropsMacros.h @@ -0,0 +1,90 @@ +/* + * 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 + +// Get hash at compile-time. sizeof(str) - 1 == strlen +// Auto-formatting makes this more ugly than it has to be, but it works. +// The pragma is to ignore warnings about variable shadowing of `len` and/or +// `hash`, which are technically shadowing but it doesn't matter since they're +// constexpr'd. +#define CONSTEXPR_RAW_PROPS_KEY_HASH(s) \ + ({ \ + _Pragma("clang diagnostic push") _Pragma( \ + "clang diagnostic ignored \"-Wshadow\"") constexpr RawPropsPropNameLength \ + len = sizeof(s) - 1; \ + constexpr RawPropsPropNameHash hash = folly::hash::fnv32_buf(s, len); \ + hash; \ + _Pragma("clang diagnostic pop") \ + }) + +#define RAW_PROPS_KEY_HASH(s) folly::hash::fnv32_buf(s, std::strlen(s)) + +// Convenience for building setProps switch statements. +// This injects `fromRawValue` into source; each file that uses +// this macro must import the proper, respective headers required. +#define RAW_SET_PROP_SWITCH_CASE(field, jsPropName, defaultValue) \ + case CONSTEXPR_RAW_PROPS_KEY_HASH(jsPropName): \ + fromRawValue(context, value, field, defaultValue); \ + return; + +// Convenience for building setProps switch statements where the field name is +// the same as the string identifier +#define RAW_SET_PROP_SWITCH_CASE_BASIC(field, defaultValue) \ + RAW_SET_PROP_SWITCH_CASE(field, #field, defaultValue) + +#define CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, field, fieldNameString, value) \ + case CONSTEXPR_RAW_PROPS_KEY_HASH(fieldNameString): { \ + if (!value.hasValue()) { \ + decltype(struct) defaultValues{}; \ + struct.field = defaultValues.field; \ + return; \ + } \ + fromRawValue(context, value, struct.field); \ + return; \ + } + +#define SET_CASCADED_RECTANGLE_CORNERS(struct, prefix, suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, topLeft, prefix "TopLeft" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, topRight, prefix "TopRight" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, bottomLeft, prefix "BottomLeft" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, bottomRight, prefix "BottomRight" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, topStart, prefix "TopStart" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, topEnd, prefix "TopEnd" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, bottomStart, prefix "BottomStart" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, bottomEnd, prefix "BottomEnd" suffix, rawValue) + +#define SET_CASCADED_RECTANGLE_EDGES(struct, prefix, suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, left, prefix "Left" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, right, prefix "Right" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, top, prefix "Top" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, bottom, prefix "Bottom" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, start, prefix "Start" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, end, prefix "End" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, horizontal, prefix "Horizontal" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, vertical, prefix "Vertical" suffix, rawValue) \ + CASE_STATEMENT_SET_FIELD_VALUE_INDEXED( \ + struct, all, prefix "" suffix, rawValue) diff --git a/ReactCommon/react/renderer/core/RawProps.cpp b/ReactCommon/react/renderer/core/RawProps.cpp index 45d63d48e2ab89..454e2bed304262 100644 --- a/ReactCommon/react/renderer/core/RawProps.cpp +++ b/ReactCommon/react/renderer/core/RawProps.cpp @@ -93,5 +93,12 @@ const RawValue *RawProps::at( return parser_->at(*this, RawPropsKey{prefix, name, suffix}); } +void RawProps::iterateOverValues( + std::function< + void(RawPropsPropNameHash, const char *, RawValue const &)> const &fn) + const { + return parser_->iterateOverValues(*this, fn); +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/core/RawProps.h b/ReactCommon/react/renderer/core/RawProps.h index 3b28256f4d4dee..4e86e289cc1c5d 100644 --- a/ReactCommon/react/renderer/core/RawProps.h +++ b/ReactCommon/react/renderer/core/RawProps.h @@ -97,6 +97,15 @@ class RawProps final { const RawValue *at(char const *name, char const *prefix, char const *suffix) const noexcept; + /** + * Iterator functions: for when you want to iterate over values in-order + * instead of using `at` to access values randomly. + */ + void iterateOverValues( + std::function< + void(RawPropsPropNameHash, const char *, RawValue const &)> const &fn) + const; + private: friend class RawPropsParser; diff --git a/ReactCommon/react/renderer/core/RawPropsParser.cpp b/ReactCommon/react/renderer/core/RawPropsParser.cpp index 359b9f2f35ddd6..882a4a935a57e2 100644 --- a/ReactCommon/react/renderer/core/RawPropsParser.cpp +++ b/ReactCommon/react/renderer/core/RawPropsParser.cpp @@ -8,6 +8,7 @@ #include "RawPropsParser.h" #include +#include #include #include @@ -99,6 +100,13 @@ void RawPropsParser::preparse(RawProps const &rawProps) const noexcept { // Resetting the cursor, the next increment will give `0`. rawProps.keyIndexCursor_ = static_cast(keyCount - 1); + // If the Props constructor doesn't use ::at at all, we might be + // able to skip this entirely (in those cases, the Props struct probably + // uses setProp instead). + if (keyCount == 0) { + return; + } + switch (rawProps.mode_) { case RawProps::Mode::Empty: return; @@ -123,6 +131,7 @@ void RawPropsParser::preparse(RawProps const &rawProps) const noexcept { auto keyIndex = nameToIndex_.at( name.data(), static_cast(name.size())); + if (keyIndex == kRawPropsValueIndexEmpty) { continue; } @@ -145,6 +154,7 @@ void RawPropsParser::preparse(RawProps const &rawProps) const noexcept { auto keyIndex = nameToIndex_.at( name.data(), static_cast(name.size())); + if (keyIndex == kRawPropsValueIndexEmpty) { continue; } @@ -158,5 +168,58 @@ void RawPropsParser::preparse(RawProps const &rawProps) const noexcept { } } +/** + * To be used by RawProps only. Value iterator functions. + */ +void RawPropsParser::iterateOverValues( + RawProps const &rawProps, + std::function< + void(RawPropsPropNameHash, const char *, RawValue const &)> const + &visit) const { + switch (rawProps.mode_) { + case RawProps::Mode::Empty: + return; + + case RawProps::Mode::JSI: { + auto &runtime = *rawProps.runtime_; + if (!rawProps.value_.isObject()) { + LOG(ERROR) << "Preparse props: rawProps value is not object"; + } + react_native_assert(rawProps.value_.isObject()); + auto object = rawProps.value_.asObject(runtime); + + auto names = object.getPropertyNames(runtime); + auto count = names.size(runtime); + + for (size_t i = 0; i < count; i++) { + auto nameValue = names.getValueAtIndex(runtime, i).getString(runtime); + auto value = object.getProperty(runtime, nameValue); + + auto name = nameValue.utf8(runtime); + + auto nameHash = RAW_PROPS_KEY_HASH(name.c_str()); + auto rawValue = RawValue(jsi::dynamicFromValue(runtime, value)); + + visit(nameHash, name.c_str(), rawValue); + } + + break; + } + + case RawProps::Mode::Dynamic: { + auto const &dynamic = rawProps.dynamic_; + + for (auto const &pair : dynamic.items()) { + auto name = pair.first.getString(); + + auto nameHash = RAW_PROPS_KEY_HASH(name.c_str()); + auto rawValue = RawValue{pair.second}; + visit(nameHash, name.c_str(), rawValue); + } + break; + } + } +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/core/RawPropsParser.h b/ReactCommon/react/renderer/core/RawPropsParser.h index f64dc9a5af2124..c112f6247cbd57 100644 --- a/ReactCommon/react/renderer/core/RawPropsParser.h +++ b/ReactCommon/react/renderer/core/RawPropsParser.h @@ -76,6 +76,15 @@ class RawPropsParser final { RawValue const *at(RawProps const &rawProps, RawPropsKey const &key) const noexcept; + /** + * To be used by RawProps only. Value iterator functions. + */ + void iterateOverValues( + RawProps const &rawProps, + std::function< + void(RawPropsPropNameHash, const char *, RawValue const &)> const &fn) + const; + mutable butter::small_vector keys_{}; mutable RawPropsKeyMap nameToIndex_{}; diff --git a/ReactCommon/react/renderer/core/RawPropsPrimitives.h b/ReactCommon/react/renderer/core/RawPropsPrimitives.h index bc5f2c9a8fdc1a..355496cc27fd74 100644 --- a/ReactCommon/react/renderer/core/RawPropsPrimitives.h +++ b/ReactCommon/react/renderer/core/RawPropsPrimitives.h @@ -7,6 +7,7 @@ #pragma once +#include #include namespace facebook { @@ -20,6 +21,7 @@ static_assert( sizeof(RawPropsValueIndex) == 1, "RawPropsValueIndex must be one byte size."); using RawPropsPropNameLength = uint8_t; +using RawPropsPropNameHash = uint32_t; /* * The number which is *usually* bigger than a number of explicitly specified diff --git a/ReactCommon/react/renderer/core/RawValue.h b/ReactCommon/react/renderer/core/RawValue.h index b5a3da4831478b..d541f11a16ed9d 100644 --- a/ReactCommon/react/renderer/core/RawValue.h +++ b/ReactCommon/react/renderer/core/RawValue.h @@ -12,6 +12,8 @@ #include #include +#include + #include namespace facebook { diff --git a/ReactCommon/react/renderer/core/propsConversions.h b/ReactCommon/react/renderer/core/propsConversions.h index 0095a3989f4018..adbc07860736a0 100644 --- a/ReactCommon/react/renderer/core/propsConversions.h +++ b/ReactCommon/react/renderer/core/propsConversions.h @@ -20,6 +20,35 @@ namespace facebook { namespace react { +/** + * Use this only when a prop update has definitely been sent from JS; + * essentially, cases where rawValue is virtually guaranteed to not be a + * nullptr. + */ +template +void fromRawValue( + const PropsParserContext &context, + RawValue const &rawValue, + T &result, + T defaultValue) { + if (!rawValue.hasValue()) { + result = std::move(defaultValue); + return; + } + + fromRawValue(context, rawValue, result); +} + +template +void fromRawValue( + const PropsParserContext &context, + RawValue const &rawValue, + std::optional &result) { + T res{}; + fromRawValue(context, rawValue, res); + result = std::optional(res); +} + template void fromRawValue( const PropsParserContext &context,