From f4d58da1fd12b14cbdc4399719579c99368202e7 Mon Sep 17 00:00:00 2001 From: Tomasz Wasilczyk Date: Thu, 6 Aug 2026 14:18:27 -0700 Subject: [PATCH] Better link detection in inline text Summary: Improved link detection to make them appear in Gaze and TalkBack: - Adds support for button RN role in addition to link. These are both triggered by link elements, but the former happens when `` tag is without a `href` - for example ``. - Don't ignore AX role when RN role is set. The latter gets inherited from ancestor, so `axrole=link` under a parent that's `role=header` was not considered a link. Changelog: [Internal] Differential Revision: D115058930 --- .../featureflags/ReactNativeFeatureFlags.kt | 8 +- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 +- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 ++- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../react/views/text/TextLayoutManager.kt | 32 +++--- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 ++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 +- .../TextLayoutManagerRelaxedLinkRoleTest.kt | 103 +++++++++++++++++ .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 +- .../ReactNativeFeatureFlagsAccessor.cpp | 108 ++++++++++-------- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../ReactNativeFeatureFlags.config.js | 11 ++ .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 22 files changed, 301 insertions(+), 79 deletions(-) create mode 100644 packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerRelaxedLinkRoleTest.kt diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index 1596d8eb2a46..7af2b51dd454 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2fc347cdb33327437d29e5fd91e24011>> + * @generated SignedSource<<09455266e2f609cf7874d35018493dbd>> */ /** @@ -288,6 +288,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enablePropsUpdateReconciliationAndroid(): Boolean = accessor.enablePropsUpdateReconciliationAndroid() + /** + * When enabled, inline text with a link-like role produces a ClickableSpan and appears in the TalkBack links rotor. + */ + @JvmStatic + public fun enableRelaxedLinkRole(): Boolean = accessor.enableRelaxedLinkRole() + /** * When enabled, RuntimeScheduler_Modern clears pending tasks and rendering updates before handling an error. */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index 59d53089af06..6d52a74f481e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -63,6 +63,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableNativeCSSParsingCache: Boolean? = null private var enablePreparedTextLayoutCache: Boolean? = null private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null + private var enableRelaxedLinkRoleCache: Boolean? = null private var enableRuntimeSchedulerQueueClearingOnErrorCache: Boolean? = null private var enableSchedulerDelegateInvalidationCache: Boolean? = null private var enableSwiftUIBasedFiltersCache: Boolean? = null @@ -495,6 +496,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableRelaxedLinkRole(): Boolean { + var cached = enableRelaxedLinkRoleCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableRelaxedLinkRole() + enableRelaxedLinkRoleCache = cached + } + return cached + } + override fun enableRuntimeSchedulerQueueClearingOnError(): Boolean { var cached = enableRuntimeSchedulerQueueClearingOnErrorCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index fbbef2ca6587..ab04b288fe12 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<1ef72233f02973021b83bd2e2aa1f69b>> + * @generated SignedSource<<1d327a377b1bd9a139930a2e6e050b22>> */ /** @@ -114,6 +114,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enablePropsUpdateReconciliationAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun enableRelaxedLinkRole(): Boolean + @DoNotStrip @JvmStatic public external fun enableRuntimeSchedulerQueueClearingOnError(): Boolean @DoNotStrip @JvmStatic public external fun enableSchedulerDelegateInvalidation(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index b9cd0522e87f..d6bd4ff0e88a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -109,6 +109,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enablePropsUpdateReconciliationAndroid(): Boolean = false + override fun enableRelaxedLinkRole(): Boolean = false + override fun enableRuntimeSchedulerQueueClearingOnError(): Boolean = false override fun enableSchedulerDelegateInvalidation(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index 4ed61aa19114..05c2b9195902 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<9ae32c46a5a6310ef96eb91c9ea5b12e>> + * @generated SignedSource<<675f3186fc6ae8d242dd6785cbb123cf>> */ /** @@ -67,6 +67,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableNativeCSSParsingCache: Boolean? = null private var enablePreparedTextLayoutCache: Boolean? = null private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null + private var enableRelaxedLinkRoleCache: Boolean? = null private var enableRuntimeSchedulerQueueClearingOnErrorCache: Boolean? = null private var enableSchedulerDelegateInvalidationCache: Boolean? = null private var enableSwiftUIBasedFiltersCache: Boolean? = null @@ -542,6 +543,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableRelaxedLinkRole(): Boolean { + var cached = enableRelaxedLinkRoleCache + if (cached == null) { + cached = currentProvider.enableRelaxedLinkRole() + accessedFeatureFlags.add("enableRelaxedLinkRole") + enableRelaxedLinkRoleCache = cached + } + return cached + } + override fun enableRuntimeSchedulerQueueClearingOnError(): Boolean { var cached = enableRuntimeSchedulerQueueClearingOnErrorCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index 1b52bd34d580..c9d742f27988 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<1a1d47f2d85404c776e55db40f7dbc6e>> + * @generated SignedSource<<277f00b235bc2b974e7cfd7c89b615ff>> */ /** @@ -109,6 +109,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enablePropsUpdateReconciliationAndroid(): Boolean + @DoNotStrip public fun enableRelaxedLinkRole(): Boolean + @DoNotStrip public fun enableRuntimeSchedulerQueueClearingOnError(): Boolean @DoNotStrip public fun enableSchedulerDelegateInvalidation(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt index e64f5de40759..cccd32af2d65 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt @@ -236,6 +236,21 @@ internal object TextLayoutManager { } } + private fun isRoleLinkLike( + role: ReactAccessibilityDelegate.Role?, + accessibilityRole: ReactAccessibilityDelegate.AccessibilityRole?, + ): Boolean { + if (!ReactNativeFeatureFlags.enableRelaxedLinkRole()) { + return if (role != null) (role == ReactAccessibilityDelegate.Role.LINK) + else (accessibilityRole == ReactAccessibilityDelegate.AccessibilityRole.LINK) + } + + return role == ReactAccessibilityDelegate.Role.LINK || + accessibilityRole == ReactAccessibilityDelegate.AccessibilityRole.LINK || + role == ReactAccessibilityDelegate.Role.BUTTON || + accessibilityRole == ReactAccessibilityDelegate.AccessibilityRole.BUTTON + } + @OptIn(UnstableReactNativeAPI::class) private fun buildSpannableFromFragments( assets: AssetManager, @@ -276,13 +291,7 @@ internal object TextLayoutManager { ), ) } else if (end >= start) { - val roleIsLink = - if (textAttributes.role != null) - (textAttributes.role == ReactAccessibilityDelegate.Role.LINK) - else - (textAttributes.accessibilityRole == - ReactAccessibilityDelegate.AccessibilityRole.LINK) - if (roleIsLink) { + if (isRoleLinkLike(textAttributes.role, textAttributes.accessibilityRole)) { if (ReactNativeFeatureFlags.enablePreparedTextLayout()) { ops.add(SetSpanOperation(start, end, ReactLinkSpan(i))) } else { @@ -496,14 +505,7 @@ internal object TextLayoutManager { spanFlags, ) } else { - val roleIsLink = - if (fragment.props.role != null) - (fragment.props.role == ReactAccessibilityDelegate.Role.LINK) - else - (fragment.props.accessibilityRole == - ReactAccessibilityDelegate.AccessibilityRole.LINK) - - if (roleIsLink) { + if (isRoleLinkLike(fragment.props.role, fragment.props.accessibilityRole)) { if (ReactNativeFeatureFlags.enablePreparedTextLayout()) { spannable.setSpan(ReactLinkSpan(i), start, end, spanFlags) } else { diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index bd0bdc7ac32b..779f5af68a69 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<28de1e205f30135e96d5cb94a902faec>> + * @generated SignedSource<<8cea4c9c393c9deee077f778c4c4a152>> */ /** @@ -297,6 +297,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableRelaxedLinkRole() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableRelaxedLinkRole"); + return method(javaProvider_); + } + bool enableRuntimeSchedulerQueueClearingOnError() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableRuntimeSchedulerQueueClearingOnError"); @@ -780,6 +786,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enablePropsUpdateReconciliationAndroid( return ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid(); } +bool JReactNativeFeatureFlagsCxxInterop::enableRelaxedLinkRole( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableRelaxedLinkRole(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableRuntimeSchedulerQueueClearingOnError( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableRuntimeSchedulerQueueClearingOnError(); @@ -1160,6 +1171,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enablePropsUpdateReconciliationAndroid", JReactNativeFeatureFlagsCxxInterop::enablePropsUpdateReconciliationAndroid), + makeNativeMethod( + "enableRelaxedLinkRole", + JReactNativeFeatureFlagsCxxInterop::enableRelaxedLinkRole), makeNativeMethod( "enableRuntimeSchedulerQueueClearingOnError", JReactNativeFeatureFlagsCxxInterop::enableRuntimeSchedulerQueueClearingOnError), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index a787b50e7c9c..338d0955f32f 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<535898dd9498c65f30d56122c06b408b>> + * @generated SignedSource<<5a8a97b8894d6b5aeb1168fe9930c3cc>> */ /** @@ -159,6 +159,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enablePropsUpdateReconciliationAndroid( facebook::jni::alias_ref); + static bool enableRelaxedLinkRole( + facebook::jni::alias_ref); + static bool enableRuntimeSchedulerQueueClearingOnError( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerRelaxedLinkRoleTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerRelaxedLinkRoleTest.kt new file mode 100644 index 000000000000..bf18ed7b62e8 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerRelaxedLinkRoleTest.kt @@ -0,0 +1,103 @@ +/* + * 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. + */ + +package com.facebook.react.views.text + +import android.text.Spannable +import android.text.style.ClickableSpan +import com.facebook.react.common.mapbuffer.WritableMapBuffer +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsDefaults +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests +import com.facebook.react.uimanager.DisplayMetricsHolder +import com.facebook.react.uimanager.ReactAccessibilityDelegate.Role +import org.assertj.core.api.Assertions.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [34]) +class TextLayoutManagerRelaxedLinkRoleTest { + + @Before + fun setUp() { + ReactNativeFeatureFlagsForTests.setUp() + DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(RuntimeEnvironment.getApplication()) + } + + @After + fun tearDown() { + DisplayMetricsHolder.setScreenDisplayMetrics(null) + ReactNativeFeatureFlags.dangerouslyReset() + } + + @Test + fun `role=button becomes a link span when enableRelaxedLinkRole is on`() { + setRelaxedLinkRole(true) + assertThat(spannable(role = Role.BUTTON).clickableSpanCount()).isEqualTo(1) + } + + @Test + fun `role=button stays plain when enableRelaxedLinkRole is off`() { + setRelaxedLinkRole(false) + assertThat(spannable(role = Role.BUTTON).clickableSpanCount()).isZero() + } + + @Test + fun `heading role with link accessibilityRole is detected when enableRelaxedLinkRole is on`() { + setRelaxedLinkRole(true) + assertThat(spannable(role = Role.HEADING, accessibilityRole = "link").clickableSpanCount()) + .isEqualTo(1) + } + + @Test + fun `heading role with link accessibilityRole stays plain when enableRelaxedLinkRole is off`() { + setRelaxedLinkRole(false) + assertThat(spannable(role = Role.HEADING, accessibilityRole = "link").clickableSpanCount()) + .isZero() + } + + private fun setRelaxedLinkRole(enabled: Boolean) = + ReactNativeFeatureFlags.override( + object : ReactNativeFeatureFlagsDefaults() { + override fun enableRelaxedLinkRole(): Boolean = enabled + } + ) + + private fun Spannable.clickableSpanCount(): Int = + getSpans(0, length, ClickableSpan::class.java).size + + private fun spannable(role: Role? = null, accessibilityRole: String? = null): Spannable { + val attrs = + WritableMapBuffer().apply { + put(TextAttributeProps.TA_KEY_FONT_SIZE, 16.0) + role?.let { put(TextAttributeProps.TA_KEY_ROLE, it.ordinal) } + accessibilityRole?.let { put(TextAttributeProps.TA_KEY_ACCESSIBILITY_ROLE, it) } + } + val fragment = + WritableMapBuffer().apply { + put(TextLayoutManager.FR_KEY_STRING, "Follow") + put(TextLayoutManager.FR_KEY_REACT_TAG, 1) + put(TextLayoutManager.FR_KEY_TEXT_ATTRIBUTES, attrs) + } + val attributedString = + WritableMapBuffer().apply { + put(TextLayoutManager.AS_KEY_STRING, "Follow") + put(TextLayoutManager.AS_KEY_FRAGMENTS, WritableMapBuffer().put(0, fragment)) + } + return TextLayoutManager.getOrCreateSpannableForText( + RuntimeEnvironment.getApplication().assets, + attributedString, + null, + ) + } +} diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 2fa66b894d23..f680b4a9ed8d 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<8fd664cb106945e5c6e436a0cf36120d>> + * @generated SignedSource<<42d9db11b0471b5810b7c5f77c36b5ca>> */ /** @@ -198,6 +198,10 @@ bool ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid() { return getAccessor().enablePropsUpdateReconciliationAndroid(); } +bool ReactNativeFeatureFlags::enableRelaxedLinkRole() { + return getAccessor().enableRelaxedLinkRole(); +} + bool ReactNativeFeatureFlags::enableRuntimeSchedulerQueueClearingOnError() { return getAccessor().enableRuntimeSchedulerQueueClearingOnError(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 648d7bf4a0c3..3173e7009fbd 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<196ba25b807fd064ca6150c7d2cd741c>> + * @generated SignedSource<<5989be950dd342f750fd18652dd63534>> */ /** @@ -254,6 +254,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enablePropsUpdateReconciliationAndroid(); + /** + * When enabled, inline text with a link-like role produces a ClickableSpan and appears in the TalkBack links rotor. + */ + RN_EXPORT static bool enableRelaxedLinkRole(); + /** * When enabled, RuntimeScheduler_Modern clears pending tasks and rendering updates before handling an error. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 213255b954b8..f1325cda398b 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<0500b2c3cb7e5f22e7344c7f5a2e6ca4>> + * @generated SignedSource<> */ /** @@ -803,6 +803,24 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableRelaxedLinkRole() { + auto flagValue = enableRelaxedLinkRole_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(43, "enableRelaxedLinkRole"); + + flagValue = currentProvider_->enableRelaxedLinkRole(); + enableRelaxedLinkRole_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableRuntimeSchedulerQueueClearingOnError() { auto flagValue = enableRuntimeSchedulerQueueClearingOnError_.load(); @@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::enableRuntimeSchedulerQueueClearingOnError // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(43, "enableRuntimeSchedulerQueueClearingOnError"); + markFlagAsAccessed(44, "enableRuntimeSchedulerQueueClearingOnError"); flagValue = currentProvider_->enableRuntimeSchedulerQueueClearingOnError(); enableRuntimeSchedulerQueueClearingOnError_ = flagValue; @@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSchedulerDelegateInvalidation() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(44, "enableSchedulerDelegateInvalidation"); + markFlagAsAccessed(45, "enableSchedulerDelegateInvalidation"); flagValue = currentProvider_->enableSchedulerDelegateInvalidation(); enableSchedulerDelegateInvalidation_ = flagValue; @@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSwiftUIBasedFilters() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(45, "enableSwiftUIBasedFilters"); + markFlagAsAccessed(46, "enableSwiftUIBasedFilters"); flagValue = currentProvider_->enableSwiftUIBasedFilters(); enableSwiftUIBasedFilters_ = flagValue; @@ -866,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewCulling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(46, "enableViewCulling"); + markFlagAsAccessed(47, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(47, "enableViewRecycling"); + markFlagAsAccessed(48, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -902,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForImage() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(48, "enableViewRecyclingForImage"); + markFlagAsAccessed(49, "enableViewRecyclingForImage"); flagValue = currentProvider_->enableViewRecyclingForImage(); enableViewRecyclingForImage_ = flagValue; @@ -920,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForScrollView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(49, "enableViewRecyclingForScrollView"); + markFlagAsAccessed(50, "enableViewRecyclingForScrollView"); flagValue = currentProvider_->enableViewRecyclingForScrollView(); enableViewRecyclingForScrollView_ = flagValue; @@ -938,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(50, "enableViewRecyclingForText"); + markFlagAsAccessed(51, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -956,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(51, "enableViewRecyclingForView"); + markFlagAsAccessed(52, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -974,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewContainerStateExperimenta // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(52, "enableVirtualViewContainerStateExperimental"); + markFlagAsAccessed(53, "enableVirtualViewContainerStateExperimental"); flagValue = currentProvider_->enableVirtualViewContainerStateExperimental(); enableVirtualViewContainerStateExperimental_ = flagValue; @@ -992,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::fixDifferentiatorParentTagForUnflattenCase // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(53, "fixDifferentiatorParentTagForUnflattenCase"); + markFlagAsAccessed(54, "fixDifferentiatorParentTagForUnflattenCase"); flagValue = currentProvider_->fixDifferentiatorParentTagForUnflattenCase(); fixDifferentiatorParentTagForUnflattenCase_ = flagValue; @@ -1010,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(54, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(55, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -1028,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::fixYogaFlexBasisFitContentInMainAxis() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(55, "fixYogaFlexBasisFitContentInMainAxis"); + markFlagAsAccessed(56, "fixYogaFlexBasisFitContentInMainAxis"); flagValue = currentProvider_->fixYogaFlexBasisFitContentInMainAxis(); fixYogaFlexBasisFitContentInMainAxis_ = flagValue; @@ -1046,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxAssertSingleHostState() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(56, "fuseboxAssertSingleHostState"); + markFlagAsAccessed(57, "fuseboxAssertSingleHostState"); flagValue = currentProvider_->fuseboxAssertSingleHostState(); fuseboxAssertSingleHostState_ = flagValue; @@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(57, "fuseboxEnabledRelease"); + markFlagAsAccessed(58, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxFrameRecordingEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(58, "fuseboxFrameRecordingEnabled"); + markFlagAsAccessed(59, "fuseboxFrameRecordingEnabled"); flagValue = currentProvider_->fuseboxFrameRecordingEnabled(); fuseboxFrameRecordingEnabled_ = flagValue; @@ -1100,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxScreenshotCaptureEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(59, "fuseboxScreenshotCaptureEnabled"); + markFlagAsAccessed(60, "fuseboxScreenshotCaptureEnabled"); flagValue = currentProvider_->fuseboxScreenshotCaptureEnabled(); fuseboxScreenshotCaptureEnabled_ = flagValue; @@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxWebSocketEventsEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(60, "fuseboxWebSocketEventsEnabled"); + markFlagAsAccessed(61, "fuseboxWebSocketEventsEnabled"); flagValue = currentProvider_->fuseboxWebSocketEventsEnabled(); fuseboxWebSocketEventsEnabled_ = flagValue; @@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::optimizedAnimatedPropUpdates() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "optimizedAnimatedPropUpdates"); + markFlagAsAccessed(62, "optimizedAnimatedPropUpdates"); flagValue = currentProvider_->optimizedAnimatedPropUpdates(); optimizedAnimatedPropUpdates_ = flagValue; @@ -1154,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(63, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1172,7 +1190,7 @@ bool ReactNativeFeatureFlagsAccessor::perfIssuesEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(63, "perfIssuesEnabled"); + markFlagAsAccessed(64, "perfIssuesEnabled"); flagValue = currentProvider_->perfIssuesEnabled(); perfIssuesEnabled_ = flagValue; @@ -1190,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(64, "perfMonitorV2Enabled"); + markFlagAsAccessed(65, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1208,7 +1226,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "preparedTextCacheSize"); + markFlagAsAccessed(66, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1226,7 +1244,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(67, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1244,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2Android() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "redBoxV2Android"); + markFlagAsAccessed(68, "redBoxV2Android"); flagValue = currentProvider_->redBoxV2Android(); redBoxV2Android_ = flagValue; @@ -1262,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2IOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "redBoxV2IOS"); + markFlagAsAccessed(69, "redBoxV2IOS"); flagValue = currentProvider_->redBoxV2IOS(); redBoxV2IOS_ = flagValue; @@ -1280,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(69, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(70, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1298,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldTriggerResponderTransferOnScrollAndr // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(70, "shouldTriggerResponderTransferOnScrollAndroid"); + markFlagAsAccessed(71, "shouldTriggerResponderTransferOnScrollAndroid"); flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid(); shouldTriggerResponderTransferOnScrollAndroid_ = flagValue; @@ -1316,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(71, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(72, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -1334,7 +1352,7 @@ bool ReactNativeFeatureFlagsAccessor::syncAndroidClipBoundsWithOverflow() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(72, "syncAndroidClipBoundsWithOverflow"); + markFlagAsAccessed(73, "syncAndroidClipBoundsWithOverflow"); flagValue = currentProvider_->syncAndroidClipBoundsWithOverflow(); syncAndroidClipBoundsWithOverflow_ = flagValue; @@ -1352,7 +1370,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(73, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(74, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1370,7 +1388,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(74, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(75, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1388,7 +1406,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommitT // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(75, "updateRuntimeShadowNodeReferencesOnCommitThread"); + markFlagAsAccessed(76, "updateRuntimeShadowNodeReferencesOnCommitThread"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommitThread(); updateRuntimeShadowNodeReferencesOnCommitThread_ = flagValue; @@ -1406,7 +1424,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(76, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(77, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1424,7 +1442,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(77, "useFabricInterop"); + markFlagAsAccessed(78, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1442,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(79, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1460,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::useNestedScrollViewAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(79, "useNestedScrollViewAndroid"); + markFlagAsAccessed(80, "useNestedScrollViewAndroid"); flagValue = currentProvider_->useNestedScrollViewAndroid(); useNestedScrollViewAndroid_ = flagValue; @@ -1478,7 +1496,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(80, "useSharedAnimatedBackend"); + markFlagAsAccessed(81, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1496,7 +1514,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(81, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(82, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1514,7 +1532,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(82, "useTurboModuleInterop"); + markFlagAsAccessed(83, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1532,7 +1550,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(83, "viewCullingOutsetRatio"); + markFlagAsAccessed(84, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1550,7 +1568,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(84, "viewTransitionEnabled"); + markFlagAsAccessed(85, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1568,7 +1586,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionUseHardwareBitmapAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(85, "viewTransitionUseHardwareBitmapAndroid"); + markFlagAsAccessed(86, "viewTransitionUseHardwareBitmapAndroid"); flagValue = currentProvider_->viewTransitionUseHardwareBitmapAndroid(); viewTransitionUseHardwareBitmapAndroid_ = flagValue; @@ -1586,7 +1604,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(86, "virtualViewPrerenderRatio"); + markFlagAsAccessed(87, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 76686e57e2aa..542ce4263218 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<88dd99a5753390988c878519a0f468c6>> + * @generated SignedSource<<3a3a4ccd2b59c8518af3d4582e893a1f>> */ /** @@ -75,6 +75,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableNativeCSSParsing(); bool enablePreparedTextLayout(); bool enablePropsUpdateReconciliationAndroid(); + bool enableRelaxedLinkRole(); bool enableRuntimeSchedulerQueueClearingOnError(); bool enableSchedulerDelegateInvalidation(); bool enableSwiftUIBasedFilters(); @@ -130,7 +131,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 87> accessedFeatureFlags_; + std::array, 88> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -175,6 +176,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableNativeCSSParsing_; std::atomic> enablePreparedTextLayout_; std::atomic> enablePropsUpdateReconciliationAndroid_; + std::atomic> enableRelaxedLinkRole_; std::atomic> enableRuntimeSchedulerQueueClearingOnError_; std::atomic> enableSchedulerDelegateInvalidation_; std::atomic> enableSwiftUIBasedFilters_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index c3d560395212..6be4c207afd0 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<9809c179e61abe55f544d6c8227a5c01>> + * @generated SignedSource<<20e1c6b2ed7b5c0a4b554c41c565e605>> */ /** @@ -199,6 +199,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableRelaxedLinkRole() override { + return false; + } + bool enableRuntimeSchedulerQueueClearingOnError() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 4a3915b7b4c6..39e9487b67ad 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<9bdf848a6d63e53f73570aebe566ddbd>> */ /** @@ -432,6 +432,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enablePropsUpdateReconciliationAndroid(); } + bool enableRelaxedLinkRole() override { + auto value = values_["enableRelaxedLinkRole"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableRelaxedLinkRole(); + } + bool enableRuntimeSchedulerQueueClearingOnError() override { auto value = values_["enableRuntimeSchedulerQueueClearingOnError"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 88c71ced84fb..ef52007c1add 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2c49f7d9235fdb157fa1291cf4ee4c52>> + * @generated SignedSource<> */ /** @@ -68,6 +68,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableNativeCSSParsing() = 0; virtual bool enablePreparedTextLayout() = 0; virtual bool enablePropsUpdateReconciliationAndroid() = 0; + virtual bool enableRelaxedLinkRole() = 0; virtual bool enableRuntimeSchedulerQueueClearingOnError() = 0; virtual bool enableSchedulerDelegateInvalidation() = 0; virtual bool enableSwiftUIBasedFilters() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 1e615d440c6d..7b4690bc10f8 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<9c49f2c73941bd43c3a2ab68eb561b6e>> + * @generated SignedSource<<91d16da08640a13288354cfd7f76dc84>> */ /** @@ -259,6 +259,11 @@ bool NativeReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid( return ReactNativeFeatureFlags::enablePropsUpdateReconciliationAndroid(); } +bool NativeReactNativeFeatureFlags::enableRelaxedLinkRole( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableRelaxedLinkRole(); +} + bool NativeReactNativeFeatureFlags::enableRuntimeSchedulerQueueClearingOnError( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableRuntimeSchedulerQueueClearingOnError(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index e96aad170678..3c084abcce00 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<741340cf9c128417368324bb80501f18>> + * @generated SignedSource<<8a0aedeee32a1f7efb9b2589429d0e49>> */ /** @@ -122,6 +122,8 @@ class NativeReactNativeFeatureFlags bool enablePropsUpdateReconciliationAndroid(jsi::Runtime& runtime); + bool enableRelaxedLinkRole(jsi::Runtime& runtime); + bool enableRuntimeSchedulerQueueClearingOnError(jsi::Runtime& runtime); bool enableSchedulerDelegateInvalidation(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 1a0b3f931cbd..a4da146ca3bc 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -507,6 +507,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + enableRelaxedLinkRole: { + defaultValue: false, + metadata: { + dateAdded: '2026-08-06', + description: + 'When enabled, inline text with a link-like role produces a ClickableSpan and appears in the TalkBack links rotor.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, enableRuntimeSchedulerQueueClearingOnError: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 2bd10e7eef76..8ada8423730d 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<57ec5f47418b3283caa251114ed07b3b>> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -91,6 +91,7 @@ export type ReactNativeFeatureFlags = Readonly<{ enableNativeCSSParsing: Getter, enablePreparedTextLayout: Getter, enablePropsUpdateReconciliationAndroid: Getter, + enableRelaxedLinkRole: Getter, enableRuntimeSchedulerQueueClearingOnError: Getter, enableSchedulerDelegateInvalidation: Getter, enableSwiftUIBasedFilters: Getter, @@ -378,6 +379,10 @@ export const enablePreparedTextLayout: Getter = createNativeFlagGetter( * When enabled, Android will receive prop updates based on the differences between the last rendered shadow node and the last committed shadow node. */ export const enablePropsUpdateReconciliationAndroid: Getter = createNativeFlagGetter('enablePropsUpdateReconciliationAndroid', false); +/** + * When enabled, inline text with a link-like role produces a ClickableSpan and appears in the TalkBack links rotor. + */ +export const enableRelaxedLinkRole: Getter = createNativeFlagGetter('enableRelaxedLinkRole', false); /** * When enabled, RuntimeScheduler_Modern clears pending tasks and rendering updates before handling an error. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 300b339d27b6..80a899469679 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<721fb68d3038841ecd6bbaabafccd5cc>> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -68,6 +68,7 @@ export interface Spec extends TurboModule { readonly enableNativeCSSParsing?: () => boolean; readonly enablePreparedTextLayout?: () => boolean; readonly enablePropsUpdateReconciliationAndroid?: () => boolean; + readonly enableRelaxedLinkRole?: () => boolean; readonly enableRuntimeSchedulerQueueClearingOnError?: () => boolean; readonly enableSchedulerDelegateInvalidation?: () => boolean; readonly enableSwiftUIBasedFilters?: () => boolean;