@@ -35,10 +35,6 @@ import {
35
35
HostComponent ,
36
36
} from 'react-reconciler/src/ReactWorkTags' ;
37
37
38
- import {
39
- addTrappedEventListener ,
40
- removeTrappedEventListener ,
41
- } from './ReactDOMEventListener' ;
42
38
import getEventTarget from './getEventTarget' ;
43
39
import { getListenerMapForElement } from './DOMEventListenerMap' ;
44
40
import {
@@ -76,17 +72,25 @@ import {
76
72
TOP_PLAYING ,
77
73
TOP_CLICK ,
78
74
TOP_SELECTION_CHANGE ,
75
+ getRawEventName ,
79
76
} from './DOMTopLevelEventTypes' ;
80
77
import { getClosestInstanceFromNode } from '../client/ReactDOMComponentTree' ;
81
78
import { COMMENT_NODE } from '../shared/HTMLNodeType' ;
82
79
import { batchedEventUpdates } from './ReactDOMUpdateBatching' ;
83
80
import getListener from './getListener' ;
81
+ import { passiveBrowserEventsSupported } from './checkPassiveEvents' ;
84
82
85
83
import { enableLegacyFBSupport } from 'shared/ReactFeatureFlags' ;
86
84
import {
87
85
invokeGuardedCallbackAndCatchFirstError ,
88
86
rethrowCaughtError ,
89
87
} from 'shared/ReactErrorUtils' ;
88
+ import { createEventListenerWrapperWithPriority } from './ReactDOMEventListener' ;
89
+ import {
90
+ removeEventListener ,
91
+ addEventCaptureListener ,
92
+ addEventBubbleListener ,
93
+ } from './EventListener' ;
90
94
91
95
const capturePhaseEvents = new Set ( [
92
96
TOP_FOCUS ,
@@ -219,25 +223,6 @@ function dispatchEventsForPlugins(
219
223
dispatchEventsInBatch ( syntheticEvents ) ;
220
224
}
221
225
222
- function shouldUpgradeListener (
223
- listenerEntry : void | ElementListenerMapEntry ,
224
- passive : void | boolean ,
225
- ) : boolean {
226
- if ( listenerEntry === undefined ) {
227
- return false ;
228
- }
229
- // Upgrade from passive to active.
230
- if ( passive !== true && listenerEntry . passive ) {
231
- return true ;
232
- }
233
- // Upgrade from default-active (browser default) to active.
234
- if ( passive === false && listenerEntry . passive === undefined ) {
235
- return true ;
236
- }
237
- // Otherwise, do not upgrade
238
- return false ;
239
- }
240
-
241
226
export function listenToTopLevelEvent (
242
227
topLevelType : DOMTopLevelEventType ,
243
228
targetContainer : EventTarget ,
@@ -247,43 +232,19 @@ export function listenToTopLevelEvent(
247
232
priority ?: EventPriority ,
248
233
capture ?: boolean ,
249
234
) : void {
250
- // If we explicitly define capture, then these are for EventTarget objects,
251
- // rather than React managed DOM elements. So we need to ensure we separate
252
- // capture and non-capture events. For React managed DOM nodes we only use
253
- // one or the other, never both. Which one we use is determined by the the
254
- // capturePhaseEvents Set (in this module) that defines if the event listener
255
- // should use the capture phase – otherwise we always use the bubble phase.
256
- // Finally, when we get to dispatching and accumulating event listeners, we
257
- // check if the user wanted capture/bubble and emulate the behavior at that
258
- // point (we call this accumulating two phase listeners).
259
- const typeStr = ( ( topLevelType : any ) : string ) ;
260
- const listenerMapKey =
261
- capture === undefined
262
- ? topLevelType
263
- : `${ typeStr } _${ capture ? 'capture' : 'bubble' } ` ;
264
-
265
235
// TOP_SELECTION_CHANGE needs to be attached to the document
266
236
// otherwise it won't capture incoming events that are only
267
237
// triggered on the document directly.
268
238
if ( topLevelType === TOP_SELECTION_CHANGE ) {
269
239
targetContainer = ( targetContainer : any ) . ownerDocument || targetContainer ;
270
240
listenerMap = getListenerMapForElement ( targetContainer ) ;
271
241
}
272
- const listenerEntry = listenerMap . get ( listenerMapKey ) ;
273
- const shouldUpgrade = shouldUpgradeListener ( listenerEntry , passive ) ;
274
- if ( listenerEntry === undefined || shouldUpgrade ) {
275
- const isCapturePhase =
276
- capture === undefined ? capturePhaseEvents . has ( topLevelType ) : capture ;
277
- // If we should upgrade, then we need to remove the existing trapped
278
- // event listener for the target container.
279
- if ( shouldUpgrade ) {
280
- removeTrappedEventListener (
281
- targetContainer ,
282
- topLevelType ,
283
- isCapturePhase ,
284
- ( ( listenerEntry : any ) : ElementListenerMapEntry ) . listener ,
285
- ) ;
286
- }
242
+ const listenerEntry : ElementListenerMapEntry | void = listenerMap . get (
243
+ topLevelType ,
244
+ ) ;
245
+ const isCapturePhase =
246
+ capture === undefined ? capturePhaseEvents . has ( topLevelType ) : capture ;
247
+ if ( listenerEntry === undefined ) {
287
248
const listener = addTrappedEventListener (
288
249
targetContainer ,
289
250
topLevelType ,
@@ -293,7 +254,7 @@ export function listenToTopLevelEvent(
293
254
passive ,
294
255
priority ,
295
256
) ;
296
- listenerMap . set ( listenerMapKey , { passive, listener} ) ;
257
+ listenerMap . set ( topLevelType , { passive, listener} ) ;
297
258
}
298
259
}
299
260
@@ -315,6 +276,77 @@ export function listenToEvent(
315
276
}
316
277
}
317
278
279
+ function addTrappedEventListener (
280
+ targetContainer : EventTarget ,
281
+ topLevelType : DOMTopLevelEventType ,
282
+ eventSystemFlags : EventSystemFlags ,
283
+ capture : boolean ,
284
+ isDeferredListenerForLegacyFBSupport ?: boolean ,
285
+ passive ?: boolean ,
286
+ priority ?: EventPriority ,
287
+ ) : any => void {
288
+ let listener = createEventListenerWrapperWithPriority (
289
+ targetContainer ,
290
+ topLevelType ,
291
+ eventSystemFlags ,
292
+ priority ,
293
+ ) ;
294
+ // If passive option is not supported, then the event will be
295
+ // active and not passive.
296
+ if ( passive === true && ! passiveBrowserEventsSupported ) {
297
+ passive = false ;
298
+ }
299
+
300
+ targetContainer =
301
+ enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
302
+ ? ( targetContainer : any ) . ownerDocument
303
+ : targetContainer ;
304
+
305
+ const rawEventName = getRawEventName ( topLevelType ) ;
306
+
307
+ let unsubscribeListener ;
308
+ // When legacyFBSupport is enabled, it's for when we
309
+ // want to add a one time event listener to a container.
310
+ // This should only be used with enableLegacyFBSupport
311
+ // due to requirement to provide compatibility with
312
+ // internal FB www event tooling. This works by removing
313
+ // the event listener as soon as it is invoked. We could
314
+ // also attempt to use the {once: true} param on
315
+ // addEventListener, but that requires support and some
316
+ // browsers do not support this today, and given this is
317
+ // to support legacy code patterns, it's likely they'll
318
+ // need support for such browsers.
319
+ if ( enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport ) {
320
+ const originalListener = listener ;
321
+ listener = function ( ...p ) {
322
+ try {
323
+ return originalListener . apply ( this , p ) ;
324
+ } finally {
325
+ removeEventListener (
326
+ targetContainer ,
327
+ rawEventName ,
328
+ unsubscribeListener ,
329
+ capture ,
330
+ ) ;
331
+ }
332
+ } ;
333
+ }
334
+ if ( capture ) {
335
+ unsubscribeListener = addEventCaptureListener (
336
+ targetContainer ,
337
+ rawEventName ,
338
+ listener ,
339
+ ) ;
340
+ } else {
341
+ unsubscribeListener = addEventBubbleListener (
342
+ targetContainer ,
343
+ rawEventName ,
344
+ listener ,
345
+ ) ;
346
+ }
347
+ return unsubscribeListener ;
348
+ }
349
+
318
350
function willDeferLaterForLegacyFBSupport (
319
351
topLevelType : DOMTopLevelEventType ,
320
352
targetContainer : EventTarget ,
0 commit comments