Skip to content

Commit

Permalink
Add infrastructure for passive/non-passive event support for future A…
Browse files Browse the repository at this point in the history
…PI exploration (#15036)

* Add infrastructure for passive/non-passive event support for future event API experimentation
  • Loading branch information
trueadm authored Mar 15, 2019
1 parent ab5fe17 commit 371bbf3
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 101 deletions.
16 changes: 16 additions & 0 deletions packages/events/EventSystemFlags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

export type EventSystemFlags = number;

export const PLUGIN_EVENT_SYSTEM = 1;
export const RESPONDER_EVENT_SYSTEM = 1 << 1;
export const IS_PASSIVE = 1 << 2;
export const IS_ACTIVE = 1 << 3;
export const PASSIVE_NOT_SUPPORTED = 1 << 4;
8 changes: 4 additions & 4 deletions packages/events/ReactGenericBatching.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import {
let _batchedUpdatesImpl = function(fn, bookkeeping) {
return fn(bookkeeping);
};
let _interactiveUpdatesImpl = function(fn, a, b) {
return fn(a, b);
let _interactiveUpdatesImpl = function(fn, a, b, c) {
return fn(a, b, c);
};
let _flushInteractiveUpdatesImpl = function() {};

Expand Down Expand Up @@ -52,8 +52,8 @@ export function batchedUpdates(fn, bookkeeping) {
}
}

export function interactiveUpdates(fn, a, b) {
return _interactiveUpdatesImpl(fn, a, b);
export function interactiveUpdates(fn, a, b, c) {
return _interactiveUpdatesImpl(fn, a, b, c);
}

export function flushInteractiveUpdates() {
Expand Down
5 changes: 4 additions & 1 deletion packages/react-dom/src/client/ReactDOMComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,10 @@ if (__DEV__) {
};
}

function ensureListeningTo(rootContainerElement, registrationName) {
function ensureListeningTo(
rootContainerElement: Element | Node,
registrationName: string,
): void {
const isDocumentOrFragment =
rootContainerElement.nodeType === DOCUMENT_NODE ||
rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE;
Expand Down
13 changes: 11 additions & 2 deletions packages/react-dom/src/events/EventListener.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,26 @@
*/

export function addEventBubbleListener(
element: Document | Element,
element: Document | Element | Node,
eventType: string,
listener: Function,
): void {
element.addEventListener(eventType, listener, false);
}

export function addEventCaptureListener(
element: Document | Element,
element: Document | Element | Node,
eventType: string,
listener: Function,
): void {
element.addEventListener(eventType, listener, true);
}

export function addEventListener(
element: Document | Element | Node,
eventType: string,
listener: Function,
options: {passive: boolean},
): void {
element.addEventListener(eventType, listener, (options: any));
}
51 changes: 27 additions & 24 deletions packages/react-dom/src/events/ReactBrowserEventEmitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import {registrationNameDependencies} from 'events/EventPluginRegistry';
import type {DOMTopLevelEventType} from 'events/TopLevelEventTypes';
import {
TOP_BLUR,
TOP_CANCEL,
Expand Down Expand Up @@ -84,22 +85,23 @@ import isEventSupported from './isEventSupported';
* React Core . General Purpose Event Plugin System
*/

const alreadyListeningTo = {};
let reactTopListenersCounter = 0;
const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
const elementListeningSets:
| WeakMap
| Map<
Document | Element | Node,
Set<DOMTopLevelEventType>,
> = new PossiblyWeakMap();

/**
* To ensure no conflicts with other potential React instances on the page
*/
const topListenersIDKey = '_reactListenersID' + ('' + Math.random()).slice(2);

function getListeningForDocument(mountAt: any) {
// In IE8, `mountAt` is a host object and doesn't have `hasOwnProperty`
// directly.
if (!Object.prototype.hasOwnProperty.call(mountAt, topListenersIDKey)) {
mountAt[topListenersIDKey] = reactTopListenersCounter++;
alreadyListeningTo[mountAt[topListenersIDKey]] = {};
function getListeningSetForElement(
element: Document | Element | Node,
): Set<DOMTopLevelEventType> {
let listeningSet = elementListeningSets.get(element);
if (listeningSet === undefined) {
listeningSet = new Set();
elementListeningSets.set(element, listeningSet);
}
return alreadyListeningTo[mountAt[topListenersIDKey]];
return listeningSet;
}

/**
Expand All @@ -125,14 +127,14 @@ function getListeningForDocument(mountAt: any) {
*/
export function listenTo(
registrationName: string,
mountAt: Document | Element,
) {
const isListening = getListeningForDocument(mountAt);
mountAt: Document | Element | Node,
): void {
const listeningSet = getListeningSetForElement(mountAt);
const dependencies = registrationNameDependencies[registrationName];

for (let i = 0; i < dependencies.length; i++) {
const dependency = dependencies[i];
if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
if (!listeningSet.has(dependency)) {
switch (dependency) {
case TOP_SCROLL:
trapCapturedEvent(TOP_SCROLL, mountAt);
Expand All @@ -143,8 +145,8 @@ export function listenTo(
trapCapturedEvent(TOP_BLUR, mountAt);
// We set the flag for a single dependency later in this function,
// but this ensures we mark both as attached rather than just one.
isListening[TOP_BLUR] = true;
isListening[TOP_FOCUS] = true;
listeningSet.add(TOP_BLUR);
listeningSet.add(TOP_FOCUS);
break;
case TOP_CANCEL:
case TOP_CLOSE:
Expand All @@ -167,20 +169,21 @@ export function listenTo(
}
break;
}
isListening[dependency] = true;
listeningSet.add(dependency);
}
}
}

export function isListeningToAllDependencies(
registrationName: string,
mountAt: Document | Element,
) {
const isListening = getListeningForDocument(mountAt);
): boolean {
const listeningSet = getListeningSetForElement(mountAt);
const dependencies = registrationNameDependencies[registrationName];

for (let i = 0; i < dependencies.length; i++) {
const dependency = dependencies[i];
if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
if (!listeningSet.has(dependency)) {
return false;
}
}
Expand Down
Loading

0 comments on commit 371bbf3

Please sign in to comment.