@samitouri / QOS-React-2 / commits / f48a5e64e8

Further cleanup of plugin event system (#18056)

Dominic Gannaway committed Feb 18, 2020 at 13:31 UTC f48a5e64e8fd903293f5b854beb795dcc6bae86d
9 files changed +221 -227
packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.internal.js
+14 -10
@@ -14,7 +14,8 @@ let EventPluginRegistry;
14 let React;
15 let ReactDOM;
16 let ReactDOMComponentTree;
17 -let ReactBrowserEventEmitter;
17 +let DOMEventPluginSystem;
18 +let ReactDOMEventListener;
19 let ReactTestUtils;
20
21 let idCallOrder;
@@ -52,18 +53,21 @@ function registerSimpleTestHandler() {
53 return getListener(CHILD, ON_CLICK_KEY);
54 }
55
56 +// We should probably remove this file at some point, it's just full of
57 +// internal API usage. ReactBrowserEventEmitter was refactored out in
58 +// #18056 too. The majority of this code lives in DOMEventPluginSystem.
59 describe('ReactBrowserEventEmitter', () => {
60 beforeEach(() => {
61 jest.resetModules();
62 LISTENER.mockClear();
63
60 - // TODO: can we express this test with only public API?
64 EventPluginGetListener = require('legacy-events/getListener').default;
65 EventPluginRegistry = require('legacy-events/EventPluginRegistry');
66 React = require('react');
67 ReactDOM = require('react-dom');
68 ReactDOMComponentTree = require('../client/ReactDOMComponentTree');
66 - ReactBrowserEventEmitter = require('../events/ReactBrowserEventEmitter');
69 + DOMEventPluginSystem = require('../events/DOMEventPluginSystem');
70 + ReactDOMEventListener = require('../events/ReactDOMEventListener');
71 ReactTestUtils = require('react-dom/test-utils');
72
73 container = document.createElement('div');
@@ -177,12 +181,12 @@ describe('ReactBrowserEventEmitter', () => {
181 expect(LISTENER).toHaveBeenCalledTimes(1);
182 });
183
180 - it('should not invoke handlers if ReactBrowserEventEmitter is disabled', () => {
184 + it('should not invoke handlers if ReactDOMEventListener is disabled', () => {
185 registerSimpleTestHandler();
182 - ReactBrowserEventEmitter.setEnabled(false);
186 + ReactDOMEventListener.setEnabled(false);
187 CHILD.click();
188 expect(LISTENER).toHaveBeenCalledTimes(0);
185 - ReactBrowserEventEmitter.setEnabled(true);
189 + ReactDOMEventListener.setEnabled(true);
190 CHILD.click();
191 expect(LISTENER).toHaveBeenCalledTimes(1);
192 });
@@ -346,15 +350,15 @@ describe('ReactBrowserEventEmitter', () => {
350
351 it('should listen to events only once', () => {
352 spyOnDevAndProd(EventTarget.prototype, 'addEventListener');
349 - ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);
350 - ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);
353 + DOMEventPluginSystem.listenToEvent(ON_CLICK_KEY, document);
354 + DOMEventPluginSystem.listenToEvent(ON_CLICK_KEY, document);
355 expect(EventTarget.prototype.addEventListener).toHaveBeenCalledTimes(1);
356 });
357
358 it('should work with event plugins without dependencies', () => {
359 spyOnDevAndProd(EventTarget.prototype, 'addEventListener');
360
357 - ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);
361 + DOMEventPluginSystem.listenToEvent(ON_CLICK_KEY, document);
362
363 expect(EventTarget.prototype.addEventListener.calls.argsFor(0)[0]).toBe(
364 'click',
@@ -364,7 +368,7 @@ describe('ReactBrowserEventEmitter', () => {
368 it('should work with event plugins with dependencies', () => {
369 spyOnDevAndProd(EventTarget.prototype, 'addEventListener');
370
367 - ReactBrowserEventEmitter.listenTo(ON_CHANGE_KEY, document);
371 + DOMEventPluginSystem.listenToEvent(ON_CHANGE_KEY, document);
372
373 const setEventListeners = [];
374 const listenCalls = EventTarget.prototype.addEventListener.calls.allArgs();
packages/react-dom/src/client/ReactDOMComponent.js
+4 -6
@@ -57,14 +57,11 @@ import {
57 TOP_SUBMIT,
58 TOP_TOGGLE,
59 } from '../events/DOMTopLevelEventTypes';
60 -import {
61 - listenTo,
62 - trapBubbledEvent,
63 - getListenerMapForElement,
64 -} from '../events/ReactBrowserEventEmitter';
60 +import {getListenerMapForElement} from '../events/DOMEventListenerMap';
61 import {
62 addResponderEventSystemEvent,
63 removeActiveResponderEventSystemEvent,
64 + trapBubbledEvent,
65 } from '../events/ReactDOMEventListener.js';
66 import {mediaEventTypes} from '../events/DOMTopLevelEventTypes';
67 import {
@@ -90,6 +87,7 @@ import {
87 enableDeprecatedFlareAPI,
88 enableTrustedTypesIntegration,
89 } from 'shared/ReactFeatureFlags';
90 +import {listenToEvent} from '../events/DOMEventPluginSystem';
91
92 let didWarnInvalidHydration = false;
93 let didWarnShadyDOM = false;
@@ -274,7 +272,7 @@ function ensureListeningTo(
272 const doc = isDocumentOrFragment
273 ? rootContainerElement
274 : rootContainerElement.ownerDocument;
277 - listenTo(registrationName, doc);
275 + listenToEvent(registrationName, doc);
276 }
277
278 function getOwnerDocumentFromRootContainer(
packages/react-dom/src/client/ReactDOMFB.js
+1 -1
@@ -10,7 +10,7 @@
10 import {addUserTimingListener} from 'shared/ReactFeatureFlags';
11
12 import ReactDOM from './ReactDOM';
13 -import {isEnabled} from '../events/ReactBrowserEventEmitter';
13 +import {isEnabled} from '../events/ReactDOMEventListener';
14 import {getClosestInstanceFromNode} from './ReactDOMComponentTree';
15
16 if (__EXPERIMENTAL__) {
packages/react-dom/src/client/ReactDOMHostConfig.js
+1 -1
@@ -34,7 +34,7 @@ import {validateDOMNesting, updatedAncestorInfo} from './validateDOMNesting';
34 import {
35 isEnabled as ReactBrowserEventEmitterIsEnabled,
36 setEnabled as ReactBrowserEventEmitterSetEnabled,
37 -} from '../events/ReactBrowserEventEmitter';
37 +} from '../events/ReactDOMEventListener';
38 import {getChildNamespace} from '../shared/DOMNamespaces';
39 import {
40 ELEMENT_NODE,
packages/react-dom/src/events/DOMEventListenerMap.js new
+31
@@ -0,0 +1,31 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
11 +
12 +const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
13 +// prettier-ignore
14 +const elementListenerMap:
15 + // $FlowFixMe Work around Flow bug
16 + | WeakMap
17 + | Map<
18 + Document | Element | Node,
19 + Map<DOMTopLevelEventType | string, null | (any => void)>,
20 + > = new PossiblyWeakMap();
21 +
22 +export function getListenerMapForElement(
23 + element: Document | Element | Node,
24 +): Map<DOMTopLevelEventType | string, null | (any => void)> {
25 + let listenerMap = elementListenerMap.get(element);
26 + if (listenerMap === undefined) {
27 + listenerMap = new Map();
28 + elementListenerMap.set(element, listenerMap);
29 + }
30 + return listenerMap;
31 +}
packages/react-dom/src/events/DOMEventPluginSystem.js
+166
@@ -21,9 +21,81 @@ import {batchedEventUpdates} from 'legacy-events/ReactGenericBatching';
21 import {runEventsInBatch} from 'legacy-events/EventBatching';
22 import {plugins} from 'legacy-events/EventPluginRegistry';
23 import accumulateInto from 'legacy-events/accumulateInto';
24 +import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
25
26 import getEventTarget from './getEventTarget';
27 import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
28 +import {trapCapturedEvent, trapBubbledEvent} from './ReactDOMEventListener';
29 +import {getListenerMapForElement} from './DOMEventListenerMap';
30 +import isEventSupported from './isEventSupported';
31 +import {
32 + TOP_BLUR,
33 + TOP_CANCEL,
34 + TOP_CLOSE,
35 + TOP_FOCUS,
36 + TOP_INVALID,
37 + TOP_RESET,
38 + TOP_SCROLL,
39 + TOP_SUBMIT,
40 + getRawEventName,
41 + mediaEventTypes,
42 +} from './DOMTopLevelEventTypes';
43 +
44 +/**
45 + * Summary of `DOMEventPluginSystem` event handling:
46 + *
47 + * - Top-level delegation is used to trap most native browser events. This
48 + * may only occur in the main thread and is the responsibility of
49 + * ReactDOMEventListener, which is injected and can therefore support
50 + * pluggable event sources. This is the only work that occurs in the main
51 + * thread.
52 + *
53 + * - We normalize and de-duplicate events to account for browser quirks. This
54 + * may be done in the worker thread.
55 + *
56 + * - Forward these native events (with the associated top-level type used to
57 + * trap it) to `EventPluginRegistry`, which in turn will ask plugins if they want
58 + * to extract any synthetic events.
59 + *
60 + * - The `EventPluginRegistry` will then process each event by annotating them with
61 + * "dispatches", a sequence of listeners and IDs that care about that event.
62 + *
63 + * - The `EventPluginRegistry` then dispatches the events.
64 + *
65 + * Overview of React and the event system:
66 + *
67 + * +------------+ .
68 + * | DOM | .
69 + * +------------+ .
70 + * | .
71 + * v .
72 + * +------------+ .
73 + * | ReactEvent | .
74 + * | Listener | .
75 + * +------------+ . +-----------+
76 + * | . +--------+|SimpleEvent|
77 + * | . | |Plugin |
78 + * +-----|------+ . v +-----------+
79 + * | | | . +--------------+ +------------+
80 + * | +-----------.--->|PluginRegistry| | Event |
81 + * | | . | | +-----------+ | Propagators|
82 + * | ReactEvent | . | | |TapEvent | |------------|
83 + * | Emitter | . | |<---+|Plugin | |other plugin|
84 + * | | . | | +-----------+ | utilities |
85 + * | +-----------.--->| | +------------+
86 + * | | | . +--------------+
87 + * +-----|------+ . ^ +-----------+
88 + * | . | |Enter/Leave|
89 + * + . +-------+|Plugin |
90 + * +-------------+ . +-----------+
91 + * | application | .
92 + * |-------------| .
93 + * | | .
94 + * | | .
95 + * +-------------+ .
96 + * .
97 + * React Core . General Purpose Event Plugin System
98 + */
99
100 const CALLBACK_BOOKKEEPING_POOL_SIZE = 10;
101 const callbackBookkeepingPool = [];
@@ -213,3 +285,97 @@ export function dispatchEventForPluginEventSystem(
285 releaseTopLevelCallbackBookKeeping(bookKeeping);
286 }
287 }
288 +
289 +/**
290 + * We listen for bubbled touch events on the document object.
291 + *
292 + * Firefox v8.01 (and possibly others) exhibited strange behavior when
293 + * mounting `onmousemove` events at some node that was not the document
294 + * element. The symptoms were that if your mouse is not moving over something
295 + * contained within that mount point (for example on the background) the
296 + * top-level listeners for `onmousemove` won't be called. However, if you
297 + * register the `mousemove` on the document object, then it will of course
298 + * catch all `mousemove`s. This along with iOS quirks, justifies restricting
299 + * top-level listeners to the document object only, at least for these
300 + * movement types of events and possibly all events.
301 + *
302 + * @see http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
303 + *
304 + * Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but
305 + * they bubble to document.
306 + *
307 + * @param {string} registrationName Name of listener (e.g. `onClick`).
308 + * @param {object} mountAt Container where to mount the listener
309 + */
310 +export function listenToEvent(
311 + registrationName: string,
312 + mountAt: Document | Element | Node,
313 +): void {
314 + const listeningSet = getListenerMapForElement(mountAt);
315 + const dependencies = registrationNameDependencies[registrationName];
316 +
317 + for (let i = 0; i < dependencies.length; i++) {
318 + const dependency = dependencies[i];
319 + listenToTopLevelEvent(dependency, mountAt, listeningSet);
320 + }
321 +}
322 +
323 +export function listenToTopLevelEvent(
324 + topLevelType: DOMTopLevelEventType,
325 + mountAt: Document | Element | Node,
326 + listenerMap: Map<DOMTopLevelEventType | string, null | (any => void)>,
327 +): void {
328 + if (!listenerMap.has(topLevelType)) {
329 + switch (topLevelType) {
330 + case TOP_SCROLL:
331 + trapCapturedEvent(TOP_SCROLL, mountAt);
332 + break;
333 + case TOP_FOCUS:
334 + case TOP_BLUR:
335 + trapCapturedEvent(TOP_FOCUS, mountAt);
336 + trapCapturedEvent(TOP_BLUR, mountAt);
337 + // We set the flag for a single dependency later in this function,
338 + // but this ensures we mark both as attached rather than just one.
339 + listenerMap.set(TOP_BLUR, null);
340 + listenerMap.set(TOP_FOCUS, null);
341 + break;
342 + case TOP_CANCEL:
343 + case TOP_CLOSE:
344 + if (isEventSupported(getRawEventName(topLevelType))) {
345 + trapCapturedEvent(topLevelType, mountAt);
346 + }
347 + break;
348 + case TOP_INVALID:
349 + case TOP_SUBMIT:
350 + case TOP_RESET:
351 + // We listen to them on the target DOM elements.
352 + // Some of them bubble so we don't want them to fire twice.
353 + break;
354 + default:
355 + // By default, listen on the top level to all non-media events.
356 + // Media events don't bubble so adding the listener wouldn't do anything.
357 + const isMediaEvent = mediaEventTypes.indexOf(topLevelType) !== -1;
358 + if (!isMediaEvent) {
359 + trapBubbledEvent(topLevelType, mountAt);
360 + }
361 + break;
362 + }
363 + listenerMap.set(topLevelType, null);
364 + }
365 +}
366 +
367 +export function isListeningToAllDependencies(
368 + registrationName: string,
369 + mountAt: Document | Element,
370 +): boolean {
371 + const listenerMap = getListenerMapForElement(mountAt);
372 + const dependencies = registrationNameDependencies[registrationName];
373 +
374 + for (let i = 0; i < dependencies.length; i++) {
375 + const dependency = dependencies[i];
376 + if (!listenerMap.has(dependency)) {
377 + return false;
378 + }
379 + }
380 + return true;
381 +}
packages/react-dom/src/events/ReactBrowserEventEmitter.js deleted
-203
@@ -1,203 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
11 -import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
12 -import {
13 - TOP_BLUR,
14 - TOP_CANCEL,
15 - TOP_CLOSE,
16 - TOP_FOCUS,
17 - TOP_INVALID,
18 - TOP_RESET,
19 - TOP_SCROLL,
20 - TOP_SUBMIT,
21 - getRawEventName,
22 - mediaEventTypes,
23 -} from './DOMTopLevelEventTypes';
24 -import {
25 - setEnabled,
26 - isEnabled,
27 - trapBubbledEvent,
28 - trapCapturedEvent,
29 -} from './ReactDOMEventListener';
30 -import isEventSupported from './isEventSupported';
31 -
32 -/**
33 - * Summary of `ReactBrowserEventEmitter` event handling:
34 - *
35 - * - Top-level delegation is used to trap most native browser events. This
36 - * may only occur in the main thread and is the responsibility of
37 - * ReactDOMEventListener, which is injected and can therefore support
38 - * pluggable event sources. This is the only work that occurs in the main
39 - * thread.
40 - *
41 - * - We normalize and de-duplicate events to account for browser quirks. This
42 - * may be done in the worker thread.
43 - *
44 - * - Forward these native events (with the associated top-level type used to
45 - * trap it) to `EventPluginRegistry`, which in turn will ask plugins if they want
46 - * to extract any synthetic events.
47 - *
48 - * - The `EventPluginRegistry` will then process each event by annotating them with
49 - * "dispatches", a sequence of listeners and IDs that care about that event.
50 - *
51 - * - The `EventPluginRegistry` then dispatches the events.
52 - *
53 - * Overview of React and the event system:
54 - *
55 - * +------------+ .
56 - * | DOM | .
57 - * +------------+ .
58 - * | .
59 - * v .
60 - * +------------+ .
61 - * | ReactEvent | .
62 - * | Listener | .
63 - * +------------+ . +-----------+
64 - * | . +--------+|SimpleEvent|
65 - * | . | |Plugin |
66 - * +-----|------+ . v +-----------+
67 - * | | | . +--------------+ +------------+
68 - * | +-----------.--->|PluginRegistry| | Event |
69 - * | | . | | +-----------+ | Propagators|
70 - * | ReactEvent | . | | |TapEvent | |------------|
71 - * | Emitter | . | |<---+|Plugin | |other plugin|
72 - * | | . | | +-----------+ | utilities |
73 - * | +-----------.--->| | +------------+
74 - * | | | . +--------------+
75 - * +-----|------+ . ^ +-----------+
76 - * | . | |Enter/Leave|
77 - * + . +-------+|Plugin |
78 - * +-------------+ . +-----------+
79 - * | application | .
80 - * |-------------| .
81 - * | | .
82 - * | | .
83 - * +-------------+ .
84 - * .
85 - * React Core . General Purpose Event Plugin System
86 - */
87 -
88 -const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
89 -// prettier-ignore
90 -const elementListenerMap:
91 - // $FlowFixMe Work around Flow bug
92 - | WeakMap
93 - | Map<
94 - Document | Element | Node,
95 - Map<DOMTopLevelEventType | string, null | (any => void)>,
96 - > = new PossiblyWeakMap();
97 -
98 -export function getListenerMapForElement(
99 - element: Document | Element | Node,
100 -): Map<DOMTopLevelEventType | string, null | (any => void)> {
101 - let listenerMap = elementListenerMap.get(element);
102 - if (listenerMap === undefined) {
103 - listenerMap = new Map();
104 - elementListenerMap.set(element, listenerMap);
105 - }
106 - return listenerMap;
107 -}
108 -
109 -/**
110 - * We listen for bubbled touch events on the document object.
111 - *
112 - * Firefox v8.01 (and possibly others) exhibited strange behavior when
113 - * mounting `onmousemove` events at some node that was not the document
114 - * element. The symptoms were that if your mouse is not moving over something
115 - * contained within that mount point (for example on the background) the
116 - * top-level listeners for `onmousemove` won't be called. However, if you
117 - * register the `mousemove` on the document object, then it will of course
118 - * catch all `mousemove`s. This along with iOS quirks, justifies restricting
119 - * top-level listeners to the document object only, at least for these
120 - * movement types of events and possibly all events.
121 - *
122 - * @see http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
123 - *
124 - * Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but
125 - * they bubble to document.
126 - *
127 - * @param {string} registrationName Name of listener (e.g. `onClick`).
128 - * @param {object} mountAt Container where to mount the listener
129 - */
130 -export function listenTo(
131 - registrationName: string,
132 - mountAt: Document | Element | Node,
133 -): void {
134 - const listeningSet = getListenerMapForElement(mountAt);
135 - const dependencies = registrationNameDependencies[registrationName];
136 -
137 - for (let i = 0; i < dependencies.length; i++) {
138 - const dependency = dependencies[i];
139 - listenToTopLevel(dependency, mountAt, listeningSet);
140 - }
141 -}
142 -
143 -export function listenToTopLevel(
144 - topLevelType: DOMTopLevelEventType,
145 - mountAt: Document | Element | Node,
146 - listenerMap: Map<DOMTopLevelEventType | string, null | (any => void)>,
147 -): void {
148 - if (!listenerMap.has(topLevelType)) {
149 - switch (topLevelType) {
150 - case TOP_SCROLL:
151 - trapCapturedEvent(TOP_SCROLL, mountAt);
152 - break;
153 - case TOP_FOCUS:
154 - case TOP_BLUR:
155 - trapCapturedEvent(TOP_FOCUS, mountAt);
156 - trapCapturedEvent(TOP_BLUR, mountAt);
157 - // We set the flag for a single dependency later in this function,
158 - // but this ensures we mark both as attached rather than just one.
159 - listenerMap.set(TOP_BLUR, null);
160 - listenerMap.set(TOP_FOCUS, null);
161 - break;
162 - case TOP_CANCEL:
163 - case TOP_CLOSE:
164 - if (isEventSupported(getRawEventName(topLevelType))) {
165 - trapCapturedEvent(topLevelType, mountAt);
166 - }
167 - break;
168 - case TOP_INVALID:
169 - case TOP_SUBMIT:
170 - case TOP_RESET:
171 - // We listen to them on the target DOM elements.
172 - // Some of them bubble so we don't want them to fire twice.
173 - break;
174 - default:
175 - // By default, listen on the top level to all non-media events.
176 - // Media events don't bubble so adding the listener wouldn't do anything.
177 - const isMediaEvent = mediaEventTypes.indexOf(topLevelType) !== -1;
178 - if (!isMediaEvent) {
179 - trapBubbledEvent(topLevelType, mountAt);
180 - }
181 - break;
182 - }
183 - listenerMap.set(topLevelType, null);
184 - }
185 -}
186 -
187 -export function isListeningToAllDependencies(
188 - registrationName: string,
189 - mountAt: Document | Element,
190 -): boolean {
191 - const listenerMap = getListenerMapForElement(mountAt);
192 - const dependencies = registrationNameDependencies[registrationName];
193 -
194 - for (let i = 0; i < dependencies.length; i++) {
195 - const dependency = dependencies[i];
196 - if (!listenerMap.has(dependency)) {
197 - return false;
198 - }
199 - }
200 - return true;
201 -}
202 -
203 -export {setEnabled, isEnabled, trapBubbledEvent, trapCapturedEvent};
packages/react-dom/src/events/ReactDOMEventReplaying.js
+3 -5
@@ -32,10 +32,7 @@ import {
32 attemptToDispatchEvent,
33 addResponderEventSystemEvent,
34 } from './ReactDOMEventListener';
35 -import {
36 - getListenerMapForElement,
37 - listenToTopLevel,
38 -} from './ReactBrowserEventEmitter';
35 +import {getListenerMapForElement} from './DOMEventListenerMap';
36 import {
37 getInstanceFromNode,
38 getClosestInstanceFromNode,
@@ -120,6 +117,7 @@ import {
117 TOP_BLUR,
118 } from './DOMTopLevelEventTypes';
119 import {IS_REPLAYED} from 'legacy-events/EventSystemFlags';
120 +import {listenToTopLevelEvent} from './DOMEventPluginSystem';
121
122 type QueuedReplayableEvent = {|
123 blockedOn: null | Container | SuspenseInstance,
@@ -217,7 +215,7 @@ function trapReplayableEvent(
215 document: Document,
216 listenerMap: Map<DOMTopLevelEventType | string, null | (any => void)>,
217 ) {
220 - listenToTopLevel(topLevelType, document, listenerMap);
218 + listenToTopLevelEvent(topLevelType, document, listenerMap);
219 if (enableDeprecatedFlareAPI) {
220 // Trap events for the responder system.
221 const topLevelTypeString = unsafeCastDOMTopLevelTypeToString(topLevelType);
packages/react-dom/src/events/SelectEventPlugin.js
+1 -1
@@ -22,11 +22,11 @@ import {
22 TOP_MOUSE_UP,
23 TOP_SELECTION_CHANGE,
24 } from './DOMTopLevelEventTypes';
25 -import {isListeningToAllDependencies} from './ReactBrowserEventEmitter';
25 import getActiveElement from '../client/getActiveElement';
26 import {getNodeFromInstance} from '../client/ReactDOMComponentTree';
27 import {hasSelectionCapabilities} from '../client/ReactInputSelection';
28 import {DOCUMENT_NODE} from '../shared/HTMLNodeType';
29 +import {isListeningToAllDependencies} from './DOMEventPluginSystem';
30
31 const skipSelectionChangeEvent =
32 canUseDOM && 'documentMode' in document && document.documentMode <= 11;