diff --git a/packages/react-dom/src/events/DOMPluginEventSystem.js b/packages/react-dom/src/events/DOMPluginEventSystem.js
index eea047b959879..6a216ace127c4 100644
--- a/packages/react-dom/src/events/DOMPluginEventSystem.js
+++ b/packages/react-dom/src/events/DOMPluginEventSystem.js
@@ -53,6 +53,7 @@ import {
enableLegacyFBSupport,
enableCreateEventHandleAPI,
enableScopeAPI,
+ enablePassiveEventIntervention,
} from 'shared/ReactFeatureFlags';
import {
invokeGuardedCallbackAndCatchFirstError,
@@ -342,6 +343,21 @@ export function listenToNativeEvent(
if (domEventName === 'selectionchange') {
target = (rootContainerElement: any).ownerDocument;
}
+ if (enablePassiveEventIntervention && isPassiveListener === undefined) {
+ // Browsers introduced an intervention, making these events
+ // passive by default on document. React doesn't bind them
+ // to document anymore, but changing this now would undo
+ // the performance wins from the change. So we emulate
+ // the existing behavior manually on the roots now.
+ // https://github.com/facebook/react/issues/19651
+ if (
+ domEventName === 'touchstart' ||
+ domEventName === 'touchmove' ||
+ domEventName === 'wheel'
+ ) {
+ isPassiveListener = true;
+ }
+ }
// If the event can be delegated (or is capture phase), we can
// register it to the root container. Otherwise, we should
// register the event to the target element and mark it as
@@ -506,7 +522,7 @@ function addTrappedEventListener(
};
}
if (isCapturePhaseListener) {
- if (enableCreateEventHandleAPI && isPassiveListener !== undefined) {
+ if (isPassiveListener !== undefined) {
unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
targetContainer,
domEventName,
@@ -521,7 +537,7 @@ function addTrappedEventListener(
);
}
} else {
- if (enableCreateEventHandleAPI && isPassiveListener !== undefined) {
+ if (isPassiveListener !== undefined) {
unsubscribeListener = addEventBubbleListenerWithPassiveFlag(
targetContainer,
domEventName,
diff --git a/packages/react-dom/src/events/checkPassiveEvents.js b/packages/react-dom/src/events/checkPassiveEvents.js
index 415a13395c736..12680a885ebde 100644
--- a/packages/react-dom/src/events/checkPassiveEvents.js
+++ b/packages/react-dom/src/events/checkPassiveEvents.js
@@ -8,13 +8,12 @@
*/
import {canUseDOM} from 'shared/ExecutionEnvironment';
-import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
export let passiveBrowserEventsSupported = false;
// Check if browser support events with passive listeners
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
-if (enableCreateEventHandleAPI && canUseDOM) {
+if (canUseDOM) {
try {
const options = {};
// $FlowFixMe: Ignore Flow complaining about needing a value
diff --git a/packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js b/packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js
index 4ed062404e52f..d8eb2c9219e8d 100644
--- a/packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js
+++ b/packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js
@@ -504,5 +504,44 @@ describe('SimpleEventPlugin', function() {
expect(onClick).toHaveBeenCalledTimes(0);
});
+
+ it('registers passive handlers for events affected by the intervention', () => {
+ container = document.createElement('div');
+
+ const passiveEvents = [];
+ const nativeAddEventListener = container.addEventListener;
+ container.addEventListener = function(type, fn, options) {
+ if (options !== null && typeof options === 'object') {
+ if (options.passive) {
+ passiveEvents.push(type);
+ }
+ }
+ return nativeAddEventListener.apply(this, arguments);
+ };
+
+ ReactDOM.render(
+
{}}
+ onTouchMove={() => {}}
+ onWheel={() => {}}
+ // A few events that should be unaffected:
+ onClick={() => {}}
+ onScroll={() => {}}
+ onTouchEnd={() => {}}
+ onChange={() => {}}
+ onPointerDown={() => {}}
+ onPointerMove={() => {}}
+ />,
+ container,
+ );
+
+ if (gate(flags => flags.enablePassiveEventIntervention)) {
+ expect(passiveEvents).toEqual(['touchstart', 'touchmove', 'wheel']);
+ } else {
+ expect(passiveEvents).toEqual([]);
+ }
+ });
});
});
diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js
index 2ae1805af95c1..81975c25896ce 100644
--- a/packages/shared/ReactFeatureFlags.js
+++ b/packages/shared/ReactFeatureFlags.js
@@ -132,3 +132,6 @@ export const deferRenderPhaseUpdateToNextBatch = true;
export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
+
+// https://github.com/facebook/react/pull/19654
+export const enablePassiveEventIntervention = true;
diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js
index 6b61818c6d044..49f542446f36f 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-fb.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js
@@ -49,6 +49,7 @@ export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
+export const enablePassiveEventIntervention = true;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js
index c8598c23cd924..a050c69e1e1a3 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-oss.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
+export const enablePassiveEventIntervention = true;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
index 47a729c1d0a09..f2a1adc1b4d70 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
+export const enablePassiveEventIntervention = true;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
index 4af72e9d901a7..baee0a991e580 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
+export const enablePassiveEventIntervention = true;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
index 7c25d50d07de9..3ede445fa3ddc 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
+export const enablePassiveEventIntervention = true;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js
index 5e530afbd9db7..acfafaadb255b 100644
--- a/packages/shared/forks/ReactFeatureFlags.testing.js
+++ b/packages/shared/forks/ReactFeatureFlags.testing.js
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
+export const enablePassiveEventIntervention = true;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js
index e11f9f9d13381..ccda44cf343d3 100644
--- a/packages/shared/forks/ReactFeatureFlags.testing.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js
@@ -48,6 +48,7 @@ export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = true;
+export const enablePassiveEventIntervention = true;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js
index c7df70d192989..1397d33d53bd9 100644
--- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js
+++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js
@@ -19,6 +19,7 @@ export const enableFilterEmptyStringAttributesDOM = __VARIANT__;
export const enableLegacyFBSupport = __VARIANT__;
export const decoupleUpdatePriorityFromScheduler = __VARIANT__;
export const skipUnmountedBoundaries = __VARIANT__;
+export const enablePassiveEventIntervention = __VARIANT__;
// Enable this flag to help with concurrent mode debugging.
// It logs information to the console about React scheduling, rendering, and commit phases.
diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js
index 35f5767413fc3..74d4105ea41ad 100644
--- a/packages/shared/forks/ReactFeatureFlags.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.www.js
@@ -27,6 +27,7 @@ export const {
decoupleUpdatePriorityFromScheduler,
enableDebugTracing,
skipUnmountedBoundaries,
+ enablePassiveEventIntervention,
} = dynamicFeatureFlags;
// On WWW, __EXPERIMENTAL__ is used for a new modern build.