import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import type {ReactScopeInstance} from 'shared/ReactTypes';
import type {
ReactDOMEventHandle,
ReactDOMEventHandleListener,
} from './ReactDOMEventHandleTypes';
import type {
Container,
TextInstance,
Instance,
ActivityInstance,
SuspenseInstance,
Props,
HoistableRoot,
RootResources,
} from './ReactFiberConfigDOM';
import {
HostComponent,
HostHoistable,
HostSingleton,
HostText,
HostRoot,
SuspenseComponent,
ActivityComponent,
} from 'react-reconciler/src/ReactWorkTags';
import {getParentHydrationBoundary} from './ReactFiberConfigDOM';
import {enableScopeAPI} from 'shared/ReactFeatureFlags';
import {enableInternalInstanceMap} from 'shared/ReactFeatureFlags';
const randomKey = Math.random().toString(36).slice(2);
const internalInstanceKey = '__reactFiber$' + randomKey;
const internalPropsKey = '__reactProps$' + randomKey;
const internalContainerInstanceKey = '__reactContainer$' + randomKey;
const internalEventHandlersKey = '__reactEvents$' + randomKey;
const internalEventHandlerListenersKey = '__reactListeners$' + randomKey;
const internalEventHandlesSetKey = '__reactHandles$' + randomKey;
const internalRootNodeResourcesKey = '__reactResources$' + randomKey;
const internalHoistableMarker = '__reactMarker$' + randomKey;
const internalScrollTimer = '__reactScroll$' + randomKey;
type InstanceUnion =
| Instance
| TextInstance
| SuspenseInstance
| ActivityInstance
| ReactScopeInstance
| Container;
const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
const internalInstanceMap:
| WeakMap<InstanceUnion, Fiber>
| Map<InstanceUnion, Fiber> = new PossiblyWeakMap();
const internalPropsMap:
| WeakMap<InstanceUnion, Props>
| Map<InstanceUnion, Props> = new PossiblyWeakMap();
export function detachDeletedInstance(node: Instance): void {
if (enableInternalInstanceMap) {
internalInstanceMap.delete(node);
internalPropsMap.delete(node);
delete (node: any)[internalEventHandlersKey];
delete (node: any)[internalEventHandlerListenersKey];
delete (node: any)[internalEventHandlesSetKey];
delete (node: any)[internalRootNodeResourcesKey];
if (__DEV__) {
delete (node: any)[internalInstanceKey];
}
return;
}
delete (node: any)[internalInstanceKey];
delete (node: any)[internalPropsKey];
delete (node: any)[internalEventHandlersKey];
delete (node: any)[internalEventHandlerListenersKey];
delete (node: any)[internalEventHandlesSetKey];
}
export function precacheFiberNode(
hostInst: Fiber,
node:
| Instance
| TextInstance
| SuspenseInstance
| ActivityInstance
| ReactScopeInstance,
): void {
if (enableInternalInstanceMap) {
internalInstanceMap.set(node, hostInst);
if (__DEV__) {
(node: any)[internalInstanceKey] = hostInst;
}
return;
}
(node: any)[internalInstanceKey] = hostInst;
}
export function markContainerAsRoot(hostRoot: Fiber, node: Container): void {
node[internalContainerInstanceKey] = hostRoot;
}
export function unmarkContainerAsRoot(node: Container): void {
node[internalContainerInstanceKey] = null;
}
export function isContainerMarkedAsRoot(node: Container): boolean {
return !!node[internalContainerInstanceKey];
}
export function getClosestInstanceFromNode(targetNode: Node): null | Fiber {
let targetInst: void | Fiber;
if (enableInternalInstanceMap) {
targetInst = internalInstanceMap.get(((targetNode: any): InstanceUnion));
} else {
targetInst = (targetNode: any)[internalInstanceKey];
}
if (targetInst) {
return targetInst;
}
let parentNode = targetNode.parentNode;
while (parentNode) {
if (enableInternalInstanceMap) {
targetInst =
(parentNode: any)[internalContainerInstanceKey] ||
internalInstanceMap.get(((parentNode: any): InstanceUnion));
} else {
targetInst =
(parentNode: any)[internalContainerInstanceKey] ||
(parentNode: any)[internalInstanceKey];
}
if (targetInst) {
const alternate = targetInst.alternate;
if (
targetInst.child !== null ||
(alternate !== null && alternate.child !== null)
) {
let hydrationInstance = getParentHydrationBoundary(targetNode);
while (hydrationInstance !== null) {
const targetFiber = enableInternalInstanceMap
? internalInstanceMap.get(hydrationInstance)
:
hydrationInstance[internalInstanceKey];
if (targetFiber) {
return targetFiber;
}
hydrationInstance = getParentHydrationBoundary(hydrationInstance);
}
}
return targetInst;
}
targetNode = parentNode;
parentNode = targetNode.parentNode;
}
return null;
}
export function getInstanceFromNode(node: Node): Fiber | null {
let inst: void | null | Fiber;
if (enableInternalInstanceMap) {
inst =
internalInstanceMap.get(((node: any): InstanceUnion)) ||
(node: any)[internalContainerInstanceKey];
} else {
inst =
(node: any)[internalInstanceKey] ||
(node: any)[internalContainerInstanceKey];
}
if (inst) {
const tag = inst.tag;
if (
tag === HostComponent ||
tag === HostText ||
tag === SuspenseComponent ||
tag === ActivityComponent ||
tag === HostHoistable ||
tag === HostSingleton ||
tag === HostRoot
) {
return inst;
} else {
return null;
}
}
return null;
}
export function getNodeFromInstance(inst: Fiber): Instance | TextInstance {
const tag = inst.tag;
if (
tag === HostComponent ||
tag === HostHoistable ||
tag === HostSingleton ||
tag === HostText
) {
return inst.stateNode;
}
throw new Error('getNodeFromInstance: Invalid argument.');
}
export function getFiberCurrentPropsFromNode(
node:
| Container
| Instance
| TextInstance
| SuspenseInstance
| ActivityInstance,
): Props | null {
if (enableInternalInstanceMap) {
return internalPropsMap.get(node) || null;
}
return (node: any)[internalPropsKey] || null;
}
export function updateFiberProps(node: Instance, props: Props): void {
if (enableInternalInstanceMap) {
internalPropsMap.set(node, props);
return;
}
(node: any)[internalPropsKey] = props;
}
export function getEventListenerSet(node: EventTarget): Set<string> {
let elementListenerSet: Set<string> | void = (node: any)[
internalEventHandlersKey
];
if (elementListenerSet === undefined) {
elementListenerSet = (node: any)[internalEventHandlersKey] = new Set();
}
return elementListenerSet;
}
export function getFiberFromScopeInstance(
scope: ReactScopeInstance,
): null | Fiber {
if (enableScopeAPI) {
if (enableInternalInstanceMap) {
return internalInstanceMap.get(((scope: any): InstanceUnion)) || null;
}
return (scope: any)[internalInstanceKey] || null;
}
return null;
}
export function setEventHandlerListeners(
scope: EventTarget | ReactScopeInstance,
listeners: Set<ReactDOMEventHandleListener>,
): void {
(scope: any)[internalEventHandlerListenersKey] = listeners;
}
export function getEventHandlerListeners(
scope: EventTarget | ReactScopeInstance,
): null | Set<ReactDOMEventHandleListener> {
return (scope: any)[internalEventHandlerListenersKey] || null;
}
export function addEventHandleToTarget(
target: EventTarget | ReactScopeInstance,
eventHandle: ReactDOMEventHandle,
): void {
let eventHandles = (target: any)[internalEventHandlesSetKey];
if (eventHandles === undefined) {
eventHandles = (target: any)[internalEventHandlesSetKey] = new Set();
}
eventHandles.add(eventHandle);
}
export function doesTargetHaveEventHandle(
target: EventTarget | ReactScopeInstance,
eventHandle: ReactDOMEventHandle,
): boolean {
const eventHandles = (target: any)[internalEventHandlesSetKey];
if (eventHandles === undefined) {
return false;
}
return eventHandles.has(eventHandle);
}
export function getResourcesFromRoot(root: HoistableRoot): RootResources {
let resources = (root: any)[internalRootNodeResourcesKey];
if (!resources) {
resources = (root: any)[internalRootNodeResourcesKey] = {
hoistableStyles: new Map(),
hoistableScripts: new Map(),
};
}
return resources;
}
export function isMarkedHoistable(node: Node): boolean {
return !!(node: any)[internalHoistableMarker];
}
export function markNodeAsHoistable(node: Node) {
(node: any)[internalHoistableMarker] = true;
}
export function getScrollEndTimer(node: EventTarget): ?TimeoutID {
return (node: any)[internalScrollTimer];
}
export function setScrollEndTimer(node: EventTarget, timer: TimeoutID): void {
(node: any)[internalScrollTimer] = timer;
}
export function clearScrollEndTimer(node: EventTarget): void {
(node: any)[internalScrollTimer] = undefined;
}
export function isOwnedInstance(node: Node): boolean {
if (enableInternalInstanceMap) {
return !!(
(node: any)[internalHoistableMarker] ||
internalInstanceMap.has((node: any))
);
}
return !!(
(node: any)[internalHoistableMarker] || (node: any)[internalInstanceKey]
);
}