import type {DOMEventName} from '../events/DOMEventNames';
import type {Fiber, FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
import type {
BoundingRect,
IntersectionObserverOptions,
ObserveVisibleRectsCallback,
} from 'react-reconciler/src/ReactTestSelectors';
import type {ReactContext, ReactScopeInstance} from 'shared/ReactTypes';
import type {AncestorInfoDev} from './validateDOMNesting';
import type {FormStatus} from 'react-dom-bindings/src/shared/ReactDOMFormActions';
import type {
CrossOriginEnum,
PreloadImplOptions,
PreloadModuleImplOptions,
PreinitStyleOptions,
PreinitScriptOptions,
PreinitModuleScriptOptions,
} from 'react-dom/src/shared/ReactDOMTypes';
import type {TransitionTypes} from 'react/src/ReactTransitionType';
import {NotPending} from '../shared/ReactDOMFormActions';
import {setSrcObject} from './ReactDOMSrcObject';
import {getCurrentRootHostContainer} from 'react-reconciler/src/ReactFiberHostContext';
import {runWithFiberInDEV} from 'react-reconciler/src/ReactCurrentFiber';
import hasOwnProperty from 'shared/hasOwnProperty';
import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion';
import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
import {
isFiberContainedBy,
isFiberFollowing,
isFiberPreceding,
} from 'react-reconciler/src/ReactFiberTreeReflection';
export {
setCurrentUpdatePriority,
getCurrentUpdatePriority,
resolveUpdatePriority,
} from './ReactDOMUpdatePriority';
import {
precacheFiberNode,
updateFiberProps,
getFiberCurrentPropsFromNode,
getInstanceFromNode,
getClosestInstanceFromNode,
getFiberFromScopeInstance,
getInstanceFromNode as getInstanceFromNodeDOMTree,
isContainerMarkedAsRoot,
detachDeletedInstance,
getResourcesFromRoot,
isMarkedHoistable,
markNodeAsHoistable,
isOwnedInstance,
} from './ReactDOMComponentTree';
import {
traverseFragmentInstance,
getFragmentParentHostFiber,
getNextSiblingHostFiber,
getInstanceFromHostFiber,
traverseFragmentInstanceDeeply,
} from 'react-reconciler/src/ReactFiberTreeReflection';
export {detachDeletedInstance};
import {hasRole} from './DOMAccessibilityRoles';
import {
setInitialProperties,
updateProperties,
hydrateProperties,
hydrateText,
diffHydratedProperties,
getPropsFromElement,
diffHydratedText,
trapClickOnNonInteractiveElement,
} from './ReactDOMComponent';
import {hydrateInput} from './ReactDOMInput';
import {hydrateTextarea} from './ReactDOMTextarea';
import {hydrateSelect} from './ReactDOMSelect';
import {getSelectionInformation, restoreSelection} from './ReactInputSelection';
import setTextContent from './setTextContent';
import {
validateDOMNesting,
validateTextNesting,
updatedAncestorInfoDev,
} from './validateDOMNesting';
import {
isEnabled as ReactBrowserEventEmitterIsEnabled,
setEnabled as ReactBrowserEventEmitterSetEnabled,
} from '../events/ReactDOMEventListener';
import {SVG_NAMESPACE, MATH_NAMESPACE} from './DOMNamespaces';
import {
ELEMENT_NODE,
TEXT_NODE,
COMMENT_NODE,
DOCUMENT_NODE,
DOCUMENT_TYPE_NODE,
DOCUMENT_FRAGMENT_NODE,
} from './HTMLNodeType';
import {
flushEventReplaying,
retryIfBlockedOn,
} from '../events/ReactDOMEventReplaying';
import {
enableCreateEventHandleAPI,
enableScopeAPI,
enableTrustedTypesIntegration,
disableLegacyMode,
enableMoveBefore,
disableCommentsAsDOMContainers,
enableSuspenseyImages,
enableSrcObject,
enableViewTransition,
enableHydrationChangeEvent,
} from 'shared/ReactFeatureFlags';
import {
HostComponent,
HostHoistable,
HostText,
HostSingleton,
} from 'react-reconciler/src/ReactWorkTags';
import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';
import {validateLinkPropsForStyleResource} from '../shared/ReactDOMResourceValidation';
import escapeSelectorAttributeValueInsideDoubleQuotes from './escapeSelectorAttributeValueInsideDoubleQuotes';
import {flushSyncWork as flushSyncWorkOnAllRoots} from 'react-reconciler/src/ReactFiberWorkLoop';
import {requestFormReset as requestFormResetOnFiber} from 'react-reconciler/src/ReactFiberHooks';
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
export {default as rendererVersion} from 'shared/ReactVersion';
import noop from 'shared/noop';
export const rendererPackageName = 'react-dom';
export const extraDevToolsConfig = null;
export type Type = string;
export type Props = {
autoFocus?: boolean,
children?: mixed,
disabled?: boolean,
hidden?: boolean,
suppressHydrationWarning?: boolean,
dangerouslySetInnerHTML?: mixed,
style?: {
display?: string,
viewTransitionName?: string,
'view-transition-name'?: string,
viewTransitionClass?: string,
'view-transition-class'?: string,
margin?: string,
marginTop?: string,
'margin-top'?: string,
marginBottom?: string,
'margin-bottom'?: string,
...
},
bottom?: null | number,
left?: null | number,
right?: null | number,
top?: null | number,
is?: string,
size?: number,
value?: string,
defaultValue?: string,
checked?: boolean,
defaultChecked?: boolean,
multiple?: boolean,
src?: string | Blob | MediaSource | MediaStream,
srcSet?: string,
loading?: 'eager' | 'lazy',
onLoad?: (event: any) => void,
...
};
type RawProps = {
[string]: mixed,
};
export type EventTargetChildElement = {
type: string,
props: null | {
style?: {
position?: string,
zIndex?: number,
bottom?: string,
left?: string,
right?: string,
top?: string,
...
},
...
},
...
};
export type Container =
| interface extends Element {_reactRootContainer?: FiberRoot}
| interface extends Document {_reactRootContainer?: FiberRoot}
| interface extends DocumentFragment {_reactRootContainer?: FiberRoot};
export type Instance = Element;
export type TextInstance = Text;
declare class ActivityInterface extends Comment {}
declare class SuspenseInterface extends Comment {
_reactRetry: void | (() => void);
}
export type ActivityInstance = ActivityInterface;
export type SuspenseInstance = SuspenseInterface;
type FormStateMarkerInstance = Comment;
export type HydratableInstance =
| Instance
| TextInstance
| ActivityInstance
| SuspenseInstance
| FormStateMarkerInstance;
export type PublicInstance = Element | Text;
export type HostContextDev = {
context: HostContextProd,
ancestorInfo: AncestorInfoDev,
};
type HostContextProd = HostContextNamespace;
export type HostContext = HostContextDev | HostContextProd;
export type UpdatePayload = Array<mixed>;
export type ChildSet = void;
export type TimeoutHandle = TimeoutID;
export type NoTimeout = -1;
export type RendererInspectionConfig = $ReadOnly<{}>;
export type TransitionStatus = FormStatus;
export type ViewTransitionInstance = {
name: string,
group: Animatable,
imagePair: Animatable,
old: Animatable,
new: Animatable,
};
type SelectionInformation = {
focusedElem: null | HTMLElement,
selectionRange: mixed,
};
const SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
const ACTIVITY_START_DATA = '&';
const ACTIVITY_END_DATA = '/&';
const SUSPENSE_START_DATA = '$';
const SUSPENSE_END_DATA = '/$';
const SUSPENSE_PENDING_START_DATA = '$?';
const SUSPENSE_QUEUED_START_DATA = '$~';
const SUSPENSE_FALLBACK_START_DATA = '$!';
const PREAMBLE_CONTRIBUTION_HTML = 'html';
const PREAMBLE_CONTRIBUTION_BODY = 'body';
const PREAMBLE_CONTRIBUTION_HEAD = 'head';
const FORM_STATE_IS_MATCHING = 'F!';
const FORM_STATE_IS_NOT_MATCHING = 'F';
const DOCUMENT_READY_STATE_LOADING = 'loading';
const STYLE = 'style';
opaque type HostContextNamespace = 0 | 1 | 2;
export const HostContextNamespaceNone: HostContextNamespace = 0;
const HostContextNamespaceSvg: HostContextNamespace = 1;
const HostContextNamespaceMath: HostContextNamespace = 2;
let eventsEnabled: ?boolean = null;
let selectionInformation: null | SelectionInformation = null;
export * from 'react-reconciler/src/ReactFiberConfigWithNoPersistence';
function getOwnerDocumentFromRootContainer(
rootContainerElement: Element | Document | DocumentFragment,
): Document {
return rootContainerElement.nodeType === DOCUMENT_NODE
? (rootContainerElement: any)
: rootContainerElement.ownerDocument;
}
export function getRootHostContext(
rootContainerInstance: Container,
): HostContext {
let type;
let context: HostContextProd;
const nodeType = rootContainerInstance.nodeType;
switch (nodeType) {
case DOCUMENT_NODE:
case DOCUMENT_FRAGMENT_NODE: {
type = nodeType === DOCUMENT_NODE ? '#document' : '#fragment';
const root = (rootContainerInstance: any).documentElement;
if (root) {
const namespaceURI = root.namespaceURI;
context = namespaceURI
? getOwnHostContext(namespaceURI)
: HostContextNamespaceNone;
} else {
context = HostContextNamespaceNone;
}
break;
}
default: {
const container: any =
!disableCommentsAsDOMContainers && nodeType === COMMENT_NODE
? rootContainerInstance.parentNode
: rootContainerInstance;
type = container.tagName;
const namespaceURI = container.namespaceURI;
if (!namespaceURI) {
switch (type) {
case 'svg':
context = HostContextNamespaceSvg;
break;
case 'math':
context = HostContextNamespaceMath;
break;
default:
context = HostContextNamespaceNone;
break;
}
} else {
const ownContext = getOwnHostContext(namespaceURI);
context = getChildHostContextProd(ownContext, type);
}
break;
}
}
if (__DEV__) {
const validatedTag = type.toLowerCase();
const ancestorInfo = updatedAncestorInfoDev(null, validatedTag);
return {context, ancestorInfo};
}
return context;
}
function getOwnHostContext(namespaceURI: string): HostContextNamespace {
switch (namespaceURI) {
case SVG_NAMESPACE:
return HostContextNamespaceSvg;
case MATH_NAMESPACE:
return HostContextNamespaceMath;
default:
return HostContextNamespaceNone;
}
}
function getChildHostContextProd(
parentNamespace: HostContextNamespace,
type: string,
): HostContextNamespace {
if (parentNamespace === HostContextNamespaceNone) {
switch (type) {
case 'svg':
return HostContextNamespaceSvg;
case 'math':
return HostContextNamespaceMath;
default:
return HostContextNamespaceNone;
}
}
if (parentNamespace === HostContextNamespaceSvg && type === 'foreignObject') {
return HostContextNamespaceNone;
}
return parentNamespace;
}
export function getChildHostContext(
parentHostContext: HostContext,
type: string,
): HostContext {
if (__DEV__) {
const parentHostContextDev = ((parentHostContext: any): HostContextDev);
const context = getChildHostContextProd(parentHostContextDev.context, type);
const ancestorInfo = updatedAncestorInfoDev(
parentHostContextDev.ancestorInfo,
type,
);
return {context, ancestorInfo};
}
const parentNamespace = ((parentHostContext: any): HostContextProd);
return getChildHostContextProd(parentNamespace, type);
}
export function getPublicInstance(instance: Instance): Instance {
return instance;
}
export function prepareForCommit(containerInfo: Container): Object | null {
eventsEnabled = ReactBrowserEventEmitterIsEnabled();
selectionInformation = getSelectionInformation(containerInfo);
let activeInstance = null;
if (enableCreateEventHandleAPI) {
const focusedElem = selectionInformation.focusedElem;
if (focusedElem !== null) {
activeInstance = getClosestInstanceFromNode(focusedElem);
}
}
ReactBrowserEventEmitterSetEnabled(false);
return activeInstance;
}
export function beforeActiveInstanceBlur(internalInstanceHandle: Object): void {
if (enableCreateEventHandleAPI) {
ReactBrowserEventEmitterSetEnabled(true);
dispatchBeforeDetachedBlur(
(selectionInformation: any).focusedElem,
internalInstanceHandle,
);
ReactBrowserEventEmitterSetEnabled(false);
}
}
export function afterActiveInstanceBlur(): void {
if (enableCreateEventHandleAPI) {
ReactBrowserEventEmitterSetEnabled(true);
dispatchAfterDetachedBlur((selectionInformation: any).focusedElem);
ReactBrowserEventEmitterSetEnabled(false);
}
}
export function resetAfterCommit(containerInfo: Container): void {
restoreSelection(selectionInformation, containerInfo);
ReactBrowserEventEmitterSetEnabled(eventsEnabled);
eventsEnabled = null;
selectionInformation = null;
}
export function createHoistableInstance(
type: string,
props: Props,
rootContainerInstance: Container,
internalInstanceHandle: Object,
): Instance {
const ownerDocument = getOwnerDocumentFromRootContainer(
rootContainerInstance,
);
const domElement: Instance = ownerDocument.createElement(type);
precacheFiberNode(internalInstanceHandle, domElement);
updateFiberProps(domElement, props);
setInitialProperties(domElement, type, props);
markNodeAsHoistable(domElement);
return domElement;
}
let didWarnScriptTags = false;
const warnedUnknownTags: {
[key: string]: boolean,
} = {
dialog: true,
webview: true,
};
export function createInstance(
type: string,
props: Props,
rootContainerInstance: Container,
hostContext: HostContext,
internalInstanceHandle: Object,
): Instance {
let hostContextProd: HostContextProd;
if (__DEV__) {
const hostContextDev: HostContextDev = (hostContext: any);
validateDOMNesting(type, hostContextDev.ancestorInfo);
hostContextProd = hostContextDev.context;
} else {
hostContextProd = (hostContext: any);
}
const ownerDocument = getOwnerDocumentFromRootContainer(
rootContainerInstance,
);
let domElement: Instance;
switch (hostContextProd) {
case HostContextNamespaceSvg:
domElement = ownerDocument.createElementNS(SVG_NAMESPACE, type);
break;
case HostContextNamespaceMath:
domElement = ownerDocument.createElementNS(MATH_NAMESPACE, type);
break;
default:
switch (type) {
case 'svg': {
domElement = ownerDocument.createElementNS(SVG_NAMESPACE, type);
break;
}
case 'math': {
domElement = ownerDocument.createElementNS(MATH_NAMESPACE, type);
break;
}
case 'script': {
const div = ownerDocument.createElement('div');
if (__DEV__) {
if (enableTrustedTypesIntegration && !didWarnScriptTags) {
console.error(
'Encountered a script tag while rendering React component. ' +
'Scripts inside React components are never executed when rendering ' +
'on the client. Consider using template tag instead ' +
'(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).',
);
didWarnScriptTags = true;
}
}
div.innerHTML = '<script><' + '/script>';
const firstChild = ((div.firstChild: any): HTMLScriptElement);
domElement = div.removeChild(firstChild);
break;
}
case 'select': {
if (typeof props.is === 'string') {
domElement = ownerDocument.createElement('select', {is: props.is});
} else {
domElement = ownerDocument.createElement('select');
}
if (props.multiple) {
domElement.multiple = true;
} else if (props.size) {
domElement.size = props.size;
}
break;
}
default: {
if (typeof props.is === 'string') {
domElement = ownerDocument.createElement(type, {is: props.is});
} else {
domElement = ownerDocument.createElement(type);
}
if (__DEV__) {
if (type.indexOf('-') === -1) {
if (type !== type.toLowerCase()) {
console.error(
'<%s /> is using incorrect casing. ' +
'Use PascalCase for React components, ' +
'or lowercase for HTML elements.',
type,
);
}
if (
Object.prototype.toString.call(domElement) ===
'[object HTMLUnknownElement]' &&
!hasOwnProperty.call(warnedUnknownTags, type)
) {
warnedUnknownTags[type] = true;
console.error(
'The tag <%s> is unrecognized in this browser. ' +
'If you meant to render a React component, start its name with ' +
'an uppercase letter.',
type,
);
}
}
}
}
}
}
precacheFiberNode(internalInstanceHandle, domElement);
updateFiberProps(domElement, props);
return domElement;
}
export function cloneMutableInstance(
instance: Instance,
keepChildren: boolean,
): Instance {
return instance.cloneNode(keepChildren);
}
export function appendInitialChild(
parentInstance: Instance,
child: Instance | TextInstance,
): void {
parentInstance.appendChild(child);
}
export function finalizeInitialChildren(
domElement: Instance,
type: string,
props: Props,
hostContext: HostContext,
): boolean {
setInitialProperties(domElement, type, props);
switch (type) {
case 'button':
case 'input':
case 'select':
case 'textarea':
return !!props.autoFocus;
case 'img':
return true;
default:
return false;
}
}
export function finalizeHydratedChildren(
domElement: Instance,
type: string,
props: Props,
hostContext: HostContext,
): boolean {
if (!enableHydrationChangeEvent) {
return false;
}
switch (type) {
case 'input':
case 'select':
case 'textarea':
case 'img':
return true;
default:
return false;
}
}
export function shouldSetTextContent(type: string, props: Props): boolean {
return (
type === 'textarea' ||
type === 'noscript' ||
typeof props.children === 'string' ||
typeof props.children === 'number' ||
typeof props.children === 'bigint' ||
(typeof props.dangerouslySetInnerHTML === 'object' &&
props.dangerouslySetInnerHTML !== null &&
props.dangerouslySetInnerHTML.__html != null)
);
}
export function createTextInstance(
text: string,
rootContainerInstance: Container,
hostContext: HostContext,
internalInstanceHandle: Object,
): TextInstance {
if (__DEV__) {
const hostContextDev = ((hostContext: any): HostContextDev);
const ancestor = hostContextDev.ancestorInfo.current;
if (ancestor != null) {
validateTextNesting(
text,
ancestor.tag,
hostContextDev.ancestorInfo.implicitRootScope,
);
}
}
const textNode: TextInstance = getOwnerDocumentFromRootContainer(
rootContainerInstance,
).createTextNode(text);
precacheFiberNode(internalInstanceHandle, textNode);
return textNode;
}
export function cloneMutableTextInstance(
textInstance: TextInstance,
): TextInstance {
return textInstance.cloneNode(false);
}
let currentPopstateTransitionEvent: Event | null = null;
export function shouldAttemptEagerTransition(): boolean {
const event = window.event;
if (event && event.type === 'popstate') {
if (event === currentPopstateTransitionEvent) {
return false;
} else {
currentPopstateTransitionEvent = event;
return true;
}
}
currentPopstateTransitionEvent = null;
return false;
}
let schedulerEvent: void | Event = undefined;
export function trackSchedulerEvent(): void {
schedulerEvent = window.event;
}
export function resolveEventType(): null | string {
const event = window.event;
return event && event !== schedulerEvent ? event.type : null;
}
export function resolveEventTimeStamp(): number {
const event = window.event;
return event && event !== schedulerEvent ? event.timeStamp : -1.1;
}
export const isPrimaryRenderer = true;
export const warnsIfNotActing = true;
export const scheduleTimeout: any =
typeof setTimeout === 'function' ? setTimeout : (undefined: any);
export const cancelTimeout: any =
typeof clearTimeout === 'function' ? clearTimeout : (undefined: any);
export const noTimeout = -1;
const localPromise = typeof Promise === 'function' ? Promise : undefined;
const localRequestAnimationFrame =
typeof requestAnimationFrame === 'function'
? requestAnimationFrame
: scheduleTimeout;
export {getClosestInstanceFromNode as getInstanceFromNode};
export function preparePortalMount(portalInstance: Instance): void {
listenToAllSupportedEvents(portalInstance);
}
export function prepareScopeUpdate(
scopeInstance: ReactScopeInstance,
internalInstanceHandle: Object,
): void {
if (enableScopeAPI) {
precacheFiberNode(internalInstanceHandle, scopeInstance);
}
}
export function getInstanceFromScope(
scopeInstance: ReactScopeInstance,
): null | Object {
if (enableScopeAPI) {
return getFiberFromScopeInstance(scopeInstance);
}
return null;
}
export const supportsMicrotasks = true;
export const scheduleMicrotask: any =
typeof queueMicrotask === 'function'
? queueMicrotask
: typeof localPromise !== 'undefined'
? callback =>
localPromise.resolve(null).then(callback).catch(handleErrorInNextTick)
: scheduleTimeout;
function handleErrorInNextTick(error: any) {
setTimeout(() => {
throw error;
});
}
export const supportsMutation = true;
export function commitMount(
domElement: Instance,
type: string,
newProps: Props,
internalInstanceHandle: Object,
): void {
switch (type) {
case 'button':
case 'input':
case 'select':
case 'textarea':
if (newProps.autoFocus) {
((domElement: any):
| HTMLButtonElement
| HTMLInputElement
| HTMLSelectElement
| HTMLTextAreaElement).focus();
}
return;
case 'img': {
if (newProps.src) {
const src = (newProps: any).src;
if (enableSrcObject && typeof src === 'object') {
try {
setSrcObject(domElement, type, src);
return;
} catch (x) {
}
}
((domElement: any): HTMLImageElement).src = src;
} else if (newProps.srcSet) {
((domElement: any): HTMLImageElement).srcset = (newProps: any).srcSet;
}
return;
}
}
}
export function commitHydratedInstance(
domElement: Instance,
type: string,
props: Props,
internalInstanceHandle: Object,
): void {
if (!enableHydrationChangeEvent) {
return;
}
switch (type) {
case 'input': {
hydrateInput(
domElement,
props.value,
props.defaultValue,
props.checked,
props.defaultChecked,
);
break;
}
case 'select': {
hydrateSelect(
domElement,
props.value,
props.defaultValue,
props.multiple,
);
break;
}
case 'textarea':
hydrateTextarea(domElement, props.value, props.defaultValue);
break;
case 'img':
break;
}
}
export function commitUpdate(
domElement: Instance,
type: string,
oldProps: Props,
newProps: Props,
internalInstanceHandle: Object,
): void {
updateProperties(domElement, type, oldProps, newProps);
updateFiberProps(domElement, newProps);
}
export function resetTextContent(domElement: Instance): void {
setTextContent(domElement, '');
}
export function commitTextUpdate(
textInstance: TextInstance,
oldText: string,
newText: string,
): void {
textInstance.nodeValue = newText;
}
const supportsMoveBefore =
enableMoveBefore &&
typeof window !== 'undefined' &&
typeof window.Element.prototype.moveBefore === 'function';
export function appendChild(
parentInstance: Instance,
child: Instance | TextInstance,
): void {
if (supportsMoveBefore && child.parentNode !== null) {
parentInstance.moveBefore(child, null);
} else {
parentInstance.appendChild(child);
}
}
function warnForReactChildrenConflict(container: Container): void {
if (__DEV__) {
if ((container: any).__reactWarnedAboutChildrenConflict) {
return;
}
const props = getFiberCurrentPropsFromNode(container);
if (props !== null) {
const fiber = getInstanceFromNode(container);
if (fiber !== null) {
if (
typeof props.children === 'string' ||
typeof props.children === 'number'
) {
(container: any).__reactWarnedAboutChildrenConflict = true;
runWithFiberInDEV(fiber, () => {
console.error(
'Cannot use a ref on a React element as a container to `createRoot` or `createPortal` ' +
'if that element also sets "children" text content using React. It should be a leaf with no children. ' +
"Otherwise it's ambiguous which children should be used.",
);
});
} else if (props.dangerouslySetInnerHTML != null) {
(container: any).__reactWarnedAboutChildrenConflict = true;
runWithFiberInDEV(fiber, () => {
console.error(
'Cannot use a ref on a React element as a container to `createRoot` or `createPortal` ' +
'if that element also sets "dangerouslySetInnerHTML" using React. It should be a leaf with no children. ' +
"Otherwise it's ambiguous which children should be used.",
);
});
}
}
}
}
}
export function appendChildToContainer(
container: Container,
child: Instance | TextInstance,
): void {
if (__DEV__) {
warnForReactChildrenConflict(container);
}
let parentNode: DocumentFragment | Element;
if (container.nodeType === DOCUMENT_NODE) {
parentNode = (container: any).body;
} else if (
!disableCommentsAsDOMContainers &&
container.nodeType === COMMENT_NODE
) {
parentNode = (container.parentNode: any);
if (supportsMoveBefore && child.parentNode !== null) {
parentNode.moveBefore(child, container);
} else {
parentNode.insertBefore(child, container);
}
return;
} else if (container.nodeName === 'HTML') {
parentNode = (container.ownerDocument.body: any);
} else {
parentNode = (container: any);
}
if (supportsMoveBefore && child.parentNode !== null) {
parentNode.moveBefore(child, null);
} else {
parentNode.appendChild(child);
}
const reactRootContainer = container._reactRootContainer;
if (
(reactRootContainer === null || reactRootContainer === undefined) &&
parentNode.onclick === null
) {
trapClickOnNonInteractiveElement(((parentNode: any): HTMLElement));
}
}
export function insertBefore(
parentInstance: Instance,
child: Instance | TextInstance,
beforeChild: Instance | TextInstance | SuspenseInstance | ActivityInstance,
): void {
if (supportsMoveBefore && child.parentNode !== null) {
parentInstance.moveBefore(child, beforeChild);
} else {
parentInstance.insertBefore(child, beforeChild);
}
}
export function insertInContainerBefore(
container: Container,
child: Instance | TextInstance,
beforeChild: Instance | TextInstance | SuspenseInstance | ActivityInstance,
): void {
if (__DEV__) {
warnForReactChildrenConflict(container);
}
let parentNode: DocumentFragment | Element;
if (container.nodeType === DOCUMENT_NODE) {
parentNode = (container: any).body;
} else if (
!disableCommentsAsDOMContainers &&
container.nodeType === COMMENT_NODE
) {
parentNode = (container.parentNode: any);
} else if (container.nodeName === 'HTML') {
parentNode = (container.ownerDocument.body: any);
} else {
parentNode = (container: any);
}
if (supportsMoveBefore && child.parentNode !== null) {
parentNode.moveBefore(child, beforeChild);
} else {
parentNode.insertBefore(child, beforeChild);
}
}
export function isSingletonScope(type: string): boolean {
return type === 'head';
}
function createEvent(type: DOMEventName, bubbles: boolean): Event {
const event = document.createEvent('Event');
event.initEvent(((type: any): string), bubbles, false);
return event;
}
function dispatchBeforeDetachedBlur(
target: HTMLElement,
internalInstanceHandle: Object,
): void {
if (enableCreateEventHandleAPI) {
const event = createEvent('beforeblur', true);
event._detachedInterceptFiber = internalInstanceHandle;
target.dispatchEvent(event);
}
}
function dispatchAfterDetachedBlur(target: HTMLElement): void {
if (enableCreateEventHandleAPI) {
const event = createEvent('afterblur', false);
(event: any).relatedTarget = target;
document.dispatchEvent(event);
}
}
export function removeChild(
parentInstance: Instance,
child: Instance | TextInstance | SuspenseInstance | ActivityInstance,
): void {
parentInstance.removeChild(child);
}
export function removeChildFromContainer(
container: Container,
child: Instance | TextInstance | SuspenseInstance | ActivityInstance,
): void {
let parentNode: DocumentFragment | Element;
if (container.nodeType === DOCUMENT_NODE) {
parentNode = (container: any).body;
} else if (
!disableCommentsAsDOMContainers &&
container.nodeType === COMMENT_NODE
) {
parentNode = (container.parentNode: any);
} else if (container.nodeName === 'HTML') {
parentNode = (container.ownerDocument.body: any);
} else {
parentNode = (container: any);
}
parentNode.removeChild(child);
}
function clearHydrationBoundary(
parentInstance: Instance,
hydrationInstance: SuspenseInstance | ActivityInstance,
): void {
let node: Node = hydrationInstance;
let depth = 0;
do {
const nextNode = node.nextSibling;
parentInstance.removeChild(node);
if (nextNode && nextNode.nodeType === COMMENT_NODE) {
const data = ((nextNode: any).data: string);
if (data === SUSPENSE_END_DATA || data === ACTIVITY_END_DATA) {
if (depth === 0) {
parentInstance.removeChild(nextNode);
retryIfBlockedOn(hydrationInstance);
return;
} else {
depth--;
}
} else if (
data === SUSPENSE_START_DATA ||
data === SUSPENSE_PENDING_START_DATA ||
data === SUSPENSE_QUEUED_START_DATA ||
data === SUSPENSE_FALLBACK_START_DATA ||
data === ACTIVITY_START_DATA
) {
depth++;
} else if (data === PREAMBLE_CONTRIBUTION_HTML) {
const ownerDocument = parentInstance.ownerDocument;
const documentElement: Element = (ownerDocument.documentElement: any);
releaseSingletonInstance(documentElement);
} else if (data === PREAMBLE_CONTRIBUTION_HEAD) {
const ownerDocument = parentInstance.ownerDocument;
const head: Element = (ownerDocument.head: any);
releaseSingletonInstance(head);
clearHead(head);
} else if (data === PREAMBLE_CONTRIBUTION_BODY) {
const ownerDocument = parentInstance.ownerDocument;
const body: Element = (ownerDocument.body: any);
releaseSingletonInstance(body);
}
}
node = nextNode;
} while (node);
retryIfBlockedOn(hydrationInstance);
}
export function clearActivityBoundary(
parentInstance: Instance,
activityInstance: ActivityInstance,
): void {
clearHydrationBoundary(parentInstance, activityInstance);
}
export function clearSuspenseBoundary(
parentInstance: Instance,
suspenseInstance: SuspenseInstance,
): void {
clearHydrationBoundary(parentInstance, suspenseInstance);
}
function clearHydrationBoundaryFromContainer(
container: Container,
hydrationInstance: SuspenseInstance | ActivityInstance,
): void {
let parentNode: DocumentFragment | Element;
if (container.nodeType === DOCUMENT_NODE) {
parentNode = (container: any).body;
} else if (
!disableCommentsAsDOMContainers &&
container.nodeType === COMMENT_NODE
) {
parentNode = (container.parentNode: any);
} else if (container.nodeName === 'HTML') {
parentNode = (container.ownerDocument.body: any);
} else {
parentNode = (container: any);
}
clearHydrationBoundary(parentNode, hydrationInstance);
retryIfBlockedOn(container);
}
export function clearActivityBoundaryFromContainer(
container: Container,
activityInstance: ActivityInstance,
): void {
clearHydrationBoundaryFromContainer(container, activityInstance);
}
export function clearSuspenseBoundaryFromContainer(
container: Container,
suspenseInstance: SuspenseInstance,
): void {
clearHydrationBoundaryFromContainer(container, suspenseInstance);
}
function hideOrUnhideDehydratedBoundary(
suspenseInstance: SuspenseInstance | ActivityInstance,
isHidden: boolean,
) {
let node: Node = suspenseInstance;
let depth = 0;
do {
const nextNode = node.nextSibling;
if (node.nodeType === ELEMENT_NODE) {
const instance = ((node: any): HTMLElement & {_stashedDisplay?: string});
if (isHidden) {
instance._stashedDisplay = instance.style.display;
instance.style.display = 'none';
} else {
instance.style.display = instance._stashedDisplay || '';
if (instance.getAttribute('style') === '') {
instance.removeAttribute('style');
}
}
} else if (node.nodeType === TEXT_NODE) {
const textNode = ((node: any): Text & {_stashedText?: string});
if (isHidden) {
textNode._stashedText = textNode.nodeValue;
textNode.nodeValue = '';
} else {
textNode.nodeValue = textNode._stashedText || '';
}
}
if (nextNode && nextNode.nodeType === COMMENT_NODE) {
const data = ((nextNode: any).data: string);
if (data === SUSPENSE_END_DATA) {
if (depth === 0) {
return;
} else {
depth--;
}
} else if (
data === SUSPENSE_START_DATA ||
data === SUSPENSE_PENDING_START_DATA ||
data === SUSPENSE_QUEUED_START_DATA ||
data === SUSPENSE_FALLBACK_START_DATA
) {
depth++;
}
}
node = nextNode;
} while (node);
}
export function hideDehydratedBoundary(
suspenseInstance: SuspenseInstance,
): void {
hideOrUnhideDehydratedBoundary(suspenseInstance, true);
}
export function hideInstance(instance: Instance): void {
instance = ((instance: any): HTMLElement);
const style = instance.style;
if (typeof style.setProperty === 'function') {
style.setProperty('display', 'none', 'important');
} else {
style.display = 'none';
}
}
export function hideTextInstance(textInstance: TextInstance): void {
textInstance.nodeValue = '';
}
export function unhideDehydratedBoundary(
dehydratedInstance: SuspenseInstance | ActivityInstance,
): void {
hideOrUnhideDehydratedBoundary(dehydratedInstance, false);
}
export function unhideInstance(instance: Instance, props: Props): void {
instance = ((instance: any): HTMLElement);
const styleProp = props[STYLE];
const display =
styleProp !== undefined &&
styleProp !== null &&
styleProp.hasOwnProperty('display')
? styleProp.display
: null;
instance.style.display =
display == null || typeof display === 'boolean'
? ''
:
('' + display).trim();
}
export function unhideTextInstance(
textInstance: TextInstance,
text: string,
): void {
textInstance.nodeValue = text;
}
function warnForBlockInsideInline(instance: HTMLElement) {
if (__DEV__) {
let nextNode = instance.firstChild;
outer: while (nextNode != null) {
let node: Node = nextNode;
if (
node.nodeType === ELEMENT_NODE &&
getComputedStyle((node: any)).display === 'block'
) {
console.error(
"You're about to start a <ViewTransition> around a display: inline " +
'element <%s>, which itself has a display: block element <%s> inside it. ' +
'This might trigger a bug in Safari which causes the View Transition to ' +
'be skipped with a duplicate name error.\n' +
'https://bugs.webkit.org/show_bug.cgi?id=290923',
instance.tagName.toLocaleLowerCase(),
(node: any).tagName.toLocaleLowerCase(),
);
break;
}
if (node.firstChild != null) {
nextNode = node.firstChild;
continue;
}
if (node === instance) {
break;
}
while (node.nextSibling == null) {
if (node.parentNode == null || node.parentNode === instance) {
break;
}
node = node.parentNode;
}
nextNode = node.nextSibling;
}
}
}
function countClientRects(rects: Array<ClientRect>): number {
if (rects.length === 1) {
return 1;
}
let count = 0;
for (let i = 0; i < rects.length; i++) {
const rect = rects[i];
if (rect.width > 0 && rect.height > 0) {
count++;
}
}
return count;
}
export function applyViewTransitionName(
instance: Instance,
name: string,
className: ?string,
): void {
instance = ((instance: any): HTMLElement);
instance.style.viewTransitionName = name;
if (className != null) {
instance.style.viewTransitionClass = className;
}
const computedStyle = getComputedStyle(instance);
if (computedStyle.display === 'inline') {
const rects = instance.getClientRects();
if (countClientRects(rects) === 1) {
const style = instance.style;
style.display = rects.length === 1 ? 'inline-block' : 'block';
style.marginTop = '-' + computedStyle.paddingTop;
style.marginBottom = '-' + computedStyle.paddingBottom;
} else {
warnForBlockInsideInline(instance);
}
}
}
export function restoreViewTransitionName(
instance: Instance,
props: Props,
): void {
instance = ((instance: any): HTMLElement);
const style = instance.style;
const styleProp = props[STYLE];
const viewTransitionName =
styleProp != null
? styleProp.hasOwnProperty('viewTransitionName')
? styleProp.viewTransitionName
: styleProp.hasOwnProperty('view-transition-name')
? styleProp['view-transition-name']
: null
: null;
style.viewTransitionName =
viewTransitionName == null || typeof viewTransitionName === 'boolean'
? ''
:
('' + viewTransitionName).trim();
const viewTransitionClass =
styleProp != null
? styleProp.hasOwnProperty('viewTransitionClass')
? styleProp.viewTransitionClass
: styleProp.hasOwnProperty('view-transition-class')
? styleProp['view-transition-class']
: null
: null;
style.viewTransitionClass =
viewTransitionClass == null || typeof viewTransitionClass === 'boolean'
? ''
:
('' + viewTransitionClass).trim();
if (style.display === 'inline-block') {
if (styleProp == null) {
style.display = style.margin = '';
} else {
const display = styleProp.display;
style.display =
display == null || typeof display === 'boolean' ? '' : display;
const margin = styleProp.margin;
if (margin != null) {
style.margin = margin;
} else {
const marginTop = styleProp.hasOwnProperty('marginTop')
? styleProp.marginTop
: styleProp['margin-top'];
style.marginTop =
marginTop == null || typeof marginTop === 'boolean' ? '' : marginTop;
const marginBottom = styleProp.hasOwnProperty('marginBottom')
? styleProp.marginBottom
: styleProp['margin-bottom'];
style.marginBottom =
marginBottom == null || typeof marginBottom === 'boolean'
? ''
: marginBottom;
}
}
}
}
export function cancelViewTransitionName(
instance: Instance,
oldName: string,
props: Props,
): void {
restoreViewTransitionName(instance, props);
const documentElement = instance.ownerDocument.documentElement;
if (documentElement !== null) {
documentElement.animate(
{opacity: [0, 0], pointerEvents: ['none', 'none']},
{
duration: 0,
fill: 'forwards',
pseudoElement: '::view-transition-group(' + oldName + ')',
},
);
}
}
export function cancelRootViewTransitionName(rootContainer: Container): void {
const documentElement: null | HTMLElement =
rootContainer.nodeType === DOCUMENT_NODE
? (rootContainer: any).documentElement
: rootContainer.ownerDocument.documentElement;
if (
!disableCommentsAsDOMContainers &&
rootContainer.nodeType === COMMENT_NODE
) {
if (__DEV__) {
console.warn(
'Cannot cancel root view transition on a comment node. All view transitions will be globally scoped.',
);
}
return;
}
if (
documentElement !== null &&
documentElement.style.viewTransitionName === ''
) {
documentElement.style.viewTransitionName = 'none';
documentElement.animate(
{opacity: [0, 0], pointerEvents: ['none', 'none']},
{
duration: 0,
fill: 'forwards',
pseudoElement: '::view-transition-group(root)',
},
);
documentElement.animate(
{width: [0, 0], height: [0, 0]},
{
duration: 0,
fill: 'forwards',
pseudoElement: '::view-transition',
},
);
}
}
export function restoreRootViewTransitionName(rootContainer: Container): void {
let containerInstance: Instance;
if (rootContainer.nodeType === DOCUMENT_NODE) {
containerInstance = (rootContainer: any).body;
} else if (rootContainer.nodeName === 'HTML') {
containerInstance = (rootContainer.ownerDocument.body: any);
} else {
containerInstance = (rootContainer: any);
}
if (
!disableCommentsAsDOMContainers &&
containerInstance.nodeType === COMMENT_NODE
) {
return;
}
if (
containerInstance.style.viewTransitionName === 'root'
) {
containerInstance.style.viewTransitionName = '';
}
const documentElement: null | HTMLElement =
containerInstance.ownerDocument.documentElement;
if (
documentElement !== null &&
documentElement.style.viewTransitionName === 'none'
) {
documentElement.style.viewTransitionName = '';
}
}
function getComputedTransform(style: CSSStyleDeclaration): string {
const computedStyle: any = style;
let transform: string = computedStyle.transform;
if (transform === 'none') {
transform = '';
}
const scale: string = computedStyle.scale;
if (scale !== 'none' && scale !== '') {
const parts = scale.split(' ');
transform =
(parts.length === 3 ? 'scale3d' : 'scale') +
'(' +
parts.join(', ') +
') ' +
transform;
}
const rotate: string = computedStyle.rotate;
if (rotate !== 'none' && rotate !== '') {
const parts = rotate.split(' ');
if (parts.length === 1) {
transform = 'rotate(' + parts[0] + ') ' + transform;
} else if (parts.length === 2) {
transform =
'rotate' + parts[0].toUpperCase() + '(' + parts[1] + ') ' + transform;
} else {
transform = 'rotate3d(' + parts.join(', ') + ') ' + transform;
}
}
const translate: string = computedStyle.translate;
if (translate !== 'none' && translate !== '') {
const parts = translate.split(' ');
transform =
(parts.length === 3 ? 'translate3d' : 'translate') +
'(' +
parts.join(', ') +
') ' +
transform;
}
return transform;
}
function moveOutOfViewport(
originalStyle: CSSStyleDeclaration,
element: HTMLElement,
): void {
const transform = getComputedTransform(originalStyle);
element.style.translate = 'none';
element.style.scale = 'none';
element.style.rotate = 'none';
element.style.transform = 'translate(-20000px, -20000px) ' + transform;
}
function moveOldFrameIntoViewport(keyframe: any): void {
const computedTransform: ?string = keyframe.transform;
if (computedTransform != null) {
let transform = computedTransform === 'none' ? '' : computedTransform;
transform = 'translate(20000px, 20000px) ' + transform;
keyframe.transform = transform;
}
}
export function cloneRootViewTransitionContainer(
rootContainer: Container,
): Instance {
const documentElement: null | HTMLElement =
rootContainer.nodeType === DOCUMENT_NODE
? (rootContainer: any).documentElement
: rootContainer.ownerDocument.documentElement;
if (
documentElement !== null &&
documentElement.style.viewTransitionName === ''
) {
documentElement.style.viewTransitionName = 'none';
}
let containerInstance: HTMLElement;
if (rootContainer.nodeType === DOCUMENT_NODE) {
containerInstance = (rootContainer: any).body;
} else if (rootContainer.nodeName === 'HTML') {
containerInstance = (rootContainer.ownerDocument.body: any);
} else if (
!disableCommentsAsDOMContainers &&
rootContainer.nodeType === COMMENT_NODE
) {
throw new Error(
'Cannot use a startGestureTransition() with a comment node root.',
);
} else {
containerInstance = (rootContainer: any);
}
const containerParent = containerInstance.parentNode;
if (containerParent === null) {
throw new Error(
'Cannot use a startGestureTransition() on a detached root.',
);
}
const clone: HTMLElement = containerInstance.cloneNode(false);
const computedStyle = getComputedStyle(containerInstance);
if (
computedStyle.position === 'absolute' ||
computedStyle.position === 'fixed'
) {
} else {
let positionedAncestor: HTMLElement = containerParent;
while (
positionedAncestor.parentNode != null &&
positionedAncestor.parentNode.nodeType !== DOCUMENT_NODE
) {
if (getComputedStyle(positionedAncestor).position !== 'static') {
break;
}
positionedAncestor = positionedAncestor.parentNode;
}
const positionedAncestorStyle: any = positionedAncestor.style;
const containerInstanceStyle: any = containerInstance.style;
const prevAncestorTranslate = positionedAncestorStyle.translate;
const prevAncestorScale = positionedAncestorStyle.scale;
const prevAncestorRotate = positionedAncestorStyle.rotate;
const prevAncestorTransform = positionedAncestorStyle.transform;
const prevTranslate = containerInstanceStyle.translate;
const prevScale = containerInstanceStyle.scale;
const prevRotate = containerInstanceStyle.rotate;
const prevTransform = containerInstanceStyle.transform;
positionedAncestorStyle.translate = 'none';
positionedAncestorStyle.scale = 'none';
positionedAncestorStyle.rotate = 'none';
positionedAncestorStyle.transform = 'none';
containerInstanceStyle.translate = 'none';
containerInstanceStyle.scale = 'none';
containerInstanceStyle.rotate = 'none';
containerInstanceStyle.transform = 'none';
const ancestorRect = positionedAncestor.getBoundingClientRect();
const rect = containerInstance.getBoundingClientRect();
const cloneStyle = clone.style;
cloneStyle.position = 'absolute';
cloneStyle.top = rect.top - ancestorRect.top + 'px';
cloneStyle.left = rect.left - ancestorRect.left + 'px';
cloneStyle.width = rect.width + 'px';
cloneStyle.height = rect.height + 'px';
cloneStyle.margin = '0px';
cloneStyle.boxSizing = 'border-box';
positionedAncestorStyle.translate = prevAncestorTranslate;
positionedAncestorStyle.scale = prevAncestorScale;
positionedAncestorStyle.rotate = prevAncestorRotate;
positionedAncestorStyle.transform = prevAncestorTransform;
containerInstanceStyle.translate = prevTranslate;
containerInstanceStyle.scale = prevScale;
containerInstanceStyle.rotate = prevRotate;
containerInstanceStyle.transform = prevTransform;
}
clone.style.viewTransitionName = 'root';
moveOutOfViewport(computedStyle, clone);
containerInstance.parentNode.insertBefore(
clone,
containerInstance.nextSibling,
);
return clone;
}
export function removeRootViewTransitionClone(
rootContainer: Container,
clone: Instance,
): void {
let containerInstance: Instance;
if (rootContainer.nodeType === DOCUMENT_NODE) {
containerInstance = (rootContainer: any).body;
} else if (rootContainer.nodeName === 'HTML') {
containerInstance = (rootContainer.ownerDocument.body: any);
} else {
containerInstance = (rootContainer: any);
}
const containerParent = containerInstance.parentNode;
if (containerParent === null) {
throw new Error(
'Cannot use a startGestureTransition() on a detached root.',
);
}
containerParent.removeChild(clone);
containerInstance.style.viewTransitionName = 'root';
}
export type InstanceMeasurement = {
rect: ClientRect | DOMRect,
abs: boolean,
clip: boolean,
view: boolean,
};
function createMeasurement(
rect: ClientRect | DOMRect,
computedStyle: CSSStyleDeclaration,
element: Element,
): InstanceMeasurement {
const ownerWindow = element.ownerDocument.defaultView;
return {
rect: rect,
abs:
computedStyle.position === 'absolute' ||
computedStyle.position === 'fixed',
clip:
computedStyle.clipPath !== 'none' ||
computedStyle.overflow !== 'visible' ||
computedStyle.filter !== 'none' ||
computedStyle.mask !== 'none' ||
computedStyle.mask !== 'none' ||
computedStyle.borderRadius !== '0px',
view:
rect.bottom >= 0 &&
rect.right >= 0 &&
rect.top <= ownerWindow.innerHeight &&
rect.left <= ownerWindow.innerWidth,
};
}
export function measureInstance(instance: Instance): InstanceMeasurement {
const rect = instance.getBoundingClientRect();
const computedStyle = getComputedStyle(instance);
return createMeasurement(rect, computedStyle, instance);
}
export function measureClonedInstance(instance: Instance): InstanceMeasurement {
const measuredRect = instance.getBoundingClientRect();
const rect = new DOMRect(
measuredRect.x + 20000,
measuredRect.y + 20000,
measuredRect.width,
measuredRect.height,
);
const computedStyle = getComputedStyle(instance);
return createMeasurement(rect, computedStyle, instance);
}
export function wasInstanceInViewport(
measurement: InstanceMeasurement,
): boolean {
return measurement.view;
}
export function hasInstanceChanged(
oldMeasurement: InstanceMeasurement,
newMeasurement: InstanceMeasurement,
): boolean {
if (newMeasurement.clip) {
return true;
}
const oldRect = oldMeasurement.rect;
const newRect = newMeasurement.rect;
return (
oldRect.y !== newRect.y ||
oldRect.x !== newRect.x ||
oldRect.height !== newRect.height ||
oldRect.width !== newRect.width
);
}
export function hasInstanceAffectedParent(
oldMeasurement: InstanceMeasurement,
newMeasurement: InstanceMeasurement,
): boolean {
if (newMeasurement.abs) {
return !oldMeasurement.abs;
}
const oldRect = oldMeasurement.rect;
const newRect = newMeasurement.rect;
return oldRect.height !== newRect.height || oldRect.width !== newRect.width;
}
function cancelAllViewTransitionAnimations(scope: Element) {
const animations = scope.getAnimations({subtree: true});
for (let i = 0; i < animations.length; i++) {
const anim = animations[i];
const effect: KeyframeEffect = (anim.effect: any);
const pseudo: ?string = effect.pseudoElement;
if (
pseudo != null &&
pseudo.startsWith('::view-transition') &&
effect.target === scope
) {
anim.cancel();
}
}
}
const SUSPENSEY_FONT_TIMEOUT = 500;
function customizeViewTransitionError(
error: Object,
ignoreAbort: boolean,
): mixed {
if (typeof error === 'object' && error !== null) {
switch (error.name) {
case 'TimeoutError': {
if (__DEV__) {
return new Error(
'A ViewTransition timed out because a Navigation stalled. ' +
'This can happen if a Navigation is blocked on React itself. ' +
"Such as if it's resolved inside useEffect. " +
'This can be solved by moving the resolution to useLayoutEffect.',
{cause: error},
);
}
break;
}
case 'AbortError': {
if (ignoreAbort) {
return null;
}
if (__DEV__) {
return new Error(
'A ViewTransition was aborted early. This might be because you have ' +
'other View Transition libraries on the page and only one can run at ' +
"a time. To avoid this, use only React's built-in <ViewTransition> " +
'to coordinate.',
{cause: error},
);
}
break;
}
case 'InvalidStateError': {
if (
error.message ===
'View transition was skipped because document visibility state is hidden.' ||
error.message ===
'Skipping view transition because document visibility state has become hidden.' ||
error.message ===
'Skipping view transition because viewport size changed.'
) {
return null;
}
if (__DEV__) {
if (
error.message === 'Transition was aborted because of invalid state'
) {
return new Error(
'A ViewTransition could not start. See the console for more details.',
{cause: error},
);
}
}
break;
}
}
}
return error;
}
function forceLayout(ownerDocument: Document) {
return (ownerDocument.documentElement: any).clientHeight;
}
export function startViewTransition(
rootContainer: Container,
transitionTypes: null | TransitionTypes,
mutationCallback: () => void,
layoutCallback: () => void,
afterMutationCallback: () => void,
spawnedWorkCallback: () => void,
passiveCallback: () => mixed,
errorCallback: mixed => void,
): null | RunningViewTransition {
const ownerDocument: Document =
rootContainer.nodeType === DOCUMENT_NODE
? (rootContainer: any)
: rootContainer.ownerDocument;
try {
const transition = ownerDocument.startViewTransition({
update() {
const ownerWindow = ownerDocument.defaultView;
const pendingNavigation =
ownerWindow.navigation && ownerWindow.navigation.transition;
const previousFontLoadingStatus = ownerDocument.fonts.status;
mutationCallback();
if (previousFontLoadingStatus === 'loaded') {
forceLayout(ownerDocument);
if (
ownerDocument.fonts.status === 'loading'
) {
const fontsReady = Promise.race([
ownerDocument.fonts.ready,
new Promise(resolve =>
setTimeout(resolve, SUSPENSEY_FONT_TIMEOUT),
),
]).then(layoutCallback, layoutCallback);
const allReady = pendingNavigation
? Promise.allSettled([pendingNavigation.finished, fontsReady])
: fontsReady;
return allReady.then(afterMutationCallback, afterMutationCallback);
}
}
layoutCallback();
if (pendingNavigation) {
return pendingNavigation.finished.then(
afterMutationCallback,
afterMutationCallback,
);
} else {
afterMutationCallback();
}
},
types: transitionTypes,
});
ownerDocument.__reactViewTransition = transition;
const readyCallback = () => {
const documentElement: Element = (ownerDocument.documentElement: any);
const animations = documentElement.getAnimations({subtree: true});
for (let i = 0; i < animations.length; i++) {
const animation = animations[i];
const effect: KeyframeEffect = (animation.effect: any);
const pseudoElement: ?string = effect.pseudoElement;
if (
pseudoElement != null &&
pseudoElement.startsWith('::view-transition')
) {
const keyframes = effect.getKeyframes();
let width;
let height;
let unchangedDimensions = true;
for (let j = 0; j < keyframes.length; j++) {
const keyframe = keyframes[j];
const w = keyframe.width;
if (width === undefined) {
width = w;
} else if (width !== w) {
unchangedDimensions = false;
break;
}
const h = keyframe.height;
if (height === undefined) {
height = h;
} else if (height !== h) {
unchangedDimensions = false;
break;
}
delete keyframe.width;
delete keyframe.height;
if (keyframe.transform === 'none') {
delete keyframe.transform;
}
}
if (
unchangedDimensions &&
width !== undefined &&
height !== undefined
) {
effect.setKeyframes(keyframes);
const computedStyle = getComputedStyle(
effect.target,
effect.pseudoElement,
);
if (
computedStyle.width !== width ||
computedStyle.height !== height
) {
const first = keyframes[0];
first.width = width;
first.height = height;
const last = keyframes[keyframes.length - 1];
last.width = width;
last.height = height;
effect.setKeyframes(keyframes);
}
}
}
}
spawnedWorkCallback();
};
const handleError = (error: mixed) => {
try {
error = customizeViewTransitionError(error, false);
if (error !== null) {
errorCallback(error);
}
} finally {
mutationCallback();
layoutCallback();
spawnedWorkCallback();
}
};
transition.ready.then(readyCallback, handleError);
transition.finished.finally(() => {
cancelAllViewTransitionAnimations((ownerDocument.documentElement: any));
if (ownerDocument.__reactViewTransition === transition) {
ownerDocument.__reactViewTransition = null;
}
passiveCallback();
});
return transition;
} catch (x) {
mutationCallback();
layoutCallback();
spawnedWorkCallback();
return null;
}
}
export type RunningViewTransition = {
skipTransition(): void,
...
};
function mergeTranslate(translateA: ?string, translateB: ?string): string {
if (!translateA || translateA === 'none') {
return translateB || '';
}
if (!translateB || translateB === 'none') {
return translateA || '';
}
const partsA = translateA.split(' ');
const partsB = translateB.split(' ');
let i;
let result = '';
for (i = 0; i < partsA.length && i < partsB.length; i++) {
if (i > 0) {
result += ' ';
}
result += 'calc(' + partsA[i] + ' + ' + partsB[i] + ')';
}
for (; i < partsA.length; i++) {
result += ' ' + partsA[i];
}
for (; i < partsB.length; i++) {
result += ' ' + partsB[i];
}
return result;
}
function animateGesture(
keyframes: any,
targetElement: Element,
pseudoElement: string,
timeline: GestureTimeline,
customTimelineCleanup: Array<() => void>,
rangeStart: number,
rangeEnd: number,
moveFirstFrameIntoViewport: boolean,
moveAllFramesIntoViewport: boolean,
) {
let width;
let height;
let unchangedDimensions = true;
for (let i = 0; i < keyframes.length; i++) {
const keyframe = keyframes[i];
delete keyframe.easing;
delete keyframe.computedOffset;
const w = keyframe.width;
if (width === undefined) {
width = w;
} else if (width !== w) {
unchangedDimensions = false;
}
const h = keyframe.height;
if (height === undefined) {
height = h;
} else if (height !== h) {
unchangedDimensions = false;
}
if (keyframe.width === 'auto') {
delete keyframe.width;
}
if (keyframe.height === 'auto') {
delete keyframe.height;
}
if (keyframe.transform === 'none') {
delete keyframe.transform;
}
if (moveAllFramesIntoViewport) {
if (keyframe.transform == null) {
if (keyframe.translate == null || keyframe.translate === '') {
const elementTranslate: ?string = (getComputedStyle(
targetElement,
pseudoElement,
): any).translate;
keyframe.translate = mergeTranslate(
elementTranslate,
'20000px 20000px',
);
} else {
keyframe.translate = mergeTranslate(
keyframe.translate,
'20000px 20000px',
);
}
}
}
}
if (moveFirstFrameIntoViewport) {
moveOldFrameIntoViewport(keyframes[0]);
}
if (unchangedDimensions && width !== undefined && height !== undefined) {
const computedStyle = getComputedStyle(targetElement, pseudoElement);
if (computedStyle.width === width && computedStyle.height === height) {
for (let i = 0; i < keyframes.length; i++) {
const keyframe = keyframes[i];
delete keyframe.width;
delete keyframe.height;
}
}
}
const reverse = rangeStart > rangeEnd;
if (timeline instanceof AnimationTimeline) {
targetElement.animate(keyframes, {
pseudoElement: pseudoElement,
timeline: timeline,
easing: 'linear',
fill: 'both',
direction: reverse ? 'normal' : 'reverse',
rangeStart: (reverse ? rangeEnd : rangeStart) + '%',
rangeEnd: (reverse ? rangeStart : rangeEnd) + '%',
});
} else {
const animation = targetElement.animate(keyframes, {
pseudoElement: pseudoElement,
easing: 'linear',
fill: 'both',
direction: reverse ? 'normal' : 'reverse',
delay: reverse ? rangeEnd : rangeStart,
duration: reverse ? rangeStart - rangeEnd : rangeEnd - rangeStart,
});
const cleanup = timeline.animate(animation);
if (cleanup) {
customTimelineCleanup.push(cleanup);
}
}
}
export function startGestureTransition(
rootContainer: Container,
timeline: GestureTimeline,
rangeStart: number,
rangeEnd: number,
transitionTypes: null | TransitionTypes,
mutationCallback: () => void,
animateCallback: () => void,
errorCallback: mixed => void,
): null | RunningViewTransition {
const ownerDocument: Document =
rootContainer.nodeType === DOCUMENT_NODE
? (rootContainer: any)
: rootContainer.ownerDocument;
try {
forceLayout(ownerDocument);
const transition = ownerDocument.startViewTransition({
update: mutationCallback,
types: transitionTypes,
});
ownerDocument.__reactViewTransition = transition;
const customTimelineCleanup: Array<() => void> = [];
const readyCallback = () => {
const documentElement: Element = (ownerDocument.documentElement: any);
const animations = documentElement.getAnimations({subtree: true});
const foundGroups: Set<string> = new Set();
const foundNews: Set<string> = new Set();
let longestDuration = 0;
for (let i = 0; i < animations.length; i++) {
const effect: KeyframeEffect = (animations[i].effect: any);
const pseudoElement: ?string = effect.pseudoElement;
if (pseudoElement == null) {
} else if (pseudoElement.startsWith('::view-transition')) {
const timing = effect.getTiming();
const duration =
typeof timing.duration === 'number' ? timing.duration : 0;
const durationWithDelay = timing.delay + duration;
if (durationWithDelay > longestDuration) {
longestDuration = durationWithDelay;
}
if (pseudoElement.startsWith('::view-transition-group')) {
foundGroups.add(pseudoElement.slice(23));
} else if (pseudoElement.startsWith('::view-transition-new')) {
foundNews.add(pseudoElement.slice(21));
}
}
}
const durationToRangeMultipler =
(rangeEnd - rangeStart) / longestDuration;
for (let i = 0; i < animations.length; i++) {
const anim = animations[i];
if (anim.playState !== 'running') {
continue;
}
const effect: KeyframeEffect = (anim.effect: any);
const pseudoElement: ?string = effect.pseudoElement;
if (
pseudoElement != null &&
pseudoElement.startsWith('::view-transition') &&
effect.target === documentElement
) {
anim.cancel();
let isGeneratedGroupAnim = false;
let isExitGroupAnim = false;
if (pseudoElement.startsWith('::view-transition-group')) {
const groupName = pseudoElement.slice(23);
if (foundNews.has(groupName)) {
const animationName: ?string = anim.animationName;
isGeneratedGroupAnim =
animationName != null &&
animationName.startsWith('-ua-view-transition-group-anim-');
} else {
isExitGroupAnim = true;
}
}
const timing = effect.getTiming();
const duration =
typeof timing.duration === 'number' ? timing.duration : 0;
let adjustedRangeStart =
rangeEnd - (duration + timing.delay) * durationToRangeMultipler;
let adjustedRangeEnd =
rangeEnd - timing.delay * durationToRangeMultipler;
if (
timing.direction === 'reverse' ||
timing.direction === 'alternate-reverse'
) {
const temp = adjustedRangeStart;
adjustedRangeStart = adjustedRangeEnd;
adjustedRangeEnd = temp;
}
animateGesture(
effect.getKeyframes(),
effect.target,
pseudoElement,
timeline,
customTimelineCleanup,
adjustedRangeStart,
adjustedRangeEnd,
isGeneratedGroupAnim,
isExitGroupAnim,
);
if (pseudoElement.startsWith('::view-transition-old')) {
const groupName = pseudoElement.slice(21);
if (!foundGroups.has(groupName) && !foundNews.has(groupName)) {
foundGroups.add(groupName);
const pseudoElementName = '::view-transition-group' + groupName;
animateGesture(
[{}, {}],
effect.target,
pseudoElementName,
timeline,
customTimelineCleanup,
rangeStart,
rangeEnd,
false,
true,
);
}
}
}
}
const blockingAnim = documentElement.animate([{}, {}], {
pseudoElement: '::view-transition',
duration: 1,
});
blockingAnim.pause();
animateCallback();
};
const readyForAnimations =
navigator.userAgent.indexOf('Chrome') !== -1
? () => requestAnimationFrame(readyCallback)
: readyCallback;
const handleError = (error: mixed) => {
try {
error = customizeViewTransitionError(error, true);
if (error !== null) {
errorCallback(error);
}
} finally {
mutationCallback();
animateCallback();
}
};
transition.ready.then(readyForAnimations, handleError);
transition.finished.finally(() => {
cancelAllViewTransitionAnimations((ownerDocument.documentElement: any));
for (let i = 0; i < customTimelineCleanup.length; i++) {
const cleanup = customTimelineCleanup[i];
cleanup();
}
if (ownerDocument.__reactViewTransition === transition) {
ownerDocument.__reactViewTransition = null;
}
});
return transition;
} catch (x) {
mutationCallback();
animateCallback();
return null;
}
}
export function stopViewTransition(transition: RunningViewTransition) {
transition.skipTransition();
}
interface ViewTransitionPseudoElementType extends Animatable {
_scope: HTMLElement;
_selector: string;
getComputedStyle(): CSSStyleDeclaration;
}
function ViewTransitionPseudoElement(
this: ViewTransitionPseudoElementType,
pseudo: string,
name: string,
) {
this._scope = (document.documentElement: any);
this._selector = '::view-transition-' + pseudo + '(' + name + ')';
}
ViewTransitionPseudoElement.prototype.animate = function (
this: ViewTransitionPseudoElementType,
keyframes: Keyframe[] | PropertyIndexedKeyframes | null,
options?: number | KeyframeAnimationOptions,
): Animation {
const opts: any =
typeof options === 'number'
? {
duration: options,
}
: Object.assign(({}: KeyframeAnimationOptions), options);
opts.pseudoElement = this._selector;
return this._scope.animate(keyframes, opts);
};
ViewTransitionPseudoElement.prototype.getAnimations = function (
this: ViewTransitionPseudoElementType,
options?: GetAnimationsOptions,
): Animation[] {
const scope = this._scope;
const selector = this._selector;
const animations = scope.getAnimations({subtree: true});
const result = [];
for (let i = 0; i < animations.length; i++) {
const effect: null | {
target?: Element,
pseudoElement?: string,
...
} = (animations[i].effect: any);
if (
effect !== null &&
effect.target === scope &&
effect.pseudoElement === selector
) {
result.push(animations[i]);
}
}
return result;
};
ViewTransitionPseudoElement.prototype.getComputedStyle = function (
this: ViewTransitionPseudoElementType,
): CSSStyleDeclaration {
const scope = this._scope;
const selector = this._selector;
return getComputedStyle(scope, selector);
};
export function createViewTransitionInstance(
name: string,
): ViewTransitionInstance {
return {
name: name,
group: new (ViewTransitionPseudoElement: any)('group', name),
imagePair: new (ViewTransitionPseudoElement: any)('image-pair', name),
old: new (ViewTransitionPseudoElement: any)('old', name),
new: new (ViewTransitionPseudoElement: any)('new', name),
};
}
interface CustomTimeline {
currentTime: number;
animate(animation: Animation): void | (() => void);
}
export type GestureTimeline = AnimationTimeline | CustomTimeline;
export function getCurrentGestureOffset(timeline: GestureTimeline): number {
const time = timeline.currentTime;
if (time === null) {
throw new Error(
'Cannot start a gesture with a disconnected AnimationTimeline.',
);
}
return typeof time === 'number' ? time : time.value;
}
type StoredEventListener = {
type: string,
listener: EventListener,
optionsOrUseCapture: void | EventListenerOptionsOrUseCapture,
};
export type FragmentInstanceType = {
_fragmentFiber: Fiber,
_eventListeners: null | Array<StoredEventListener>,
_observers: null | Set<IntersectionObserver | ResizeObserver>,
addEventListener(
type: string,
listener: EventListener,
optionsOrUseCapture?: EventListenerOptionsOrUseCapture,
): void,
removeEventListener(
type: string,
listener: EventListener,
optionsOrUseCapture?: EventListenerOptionsOrUseCapture,
): void,
dispatchEvent(event: Event): boolean,
focus(focusOptions?: FocusOptions): void,
focusLast(focusOptions?: FocusOptions): void,
blur(): void,
observeUsing(observer: IntersectionObserver | ResizeObserver): void,
unobserveUsing(observer: IntersectionObserver | ResizeObserver): void,
getClientRects(): Array<DOMRect>,
getRootNode(getRootNodeOptions?: {
composed: boolean,
}): Document | ShadowRoot | FragmentInstanceType,
compareDocumentPosition(otherNode: Instance): number,
};
function FragmentInstance(this: FragmentInstanceType, fragmentFiber: Fiber) {
this._fragmentFiber = fragmentFiber;
this._eventListeners = null;
this._observers = null;
}
FragmentInstance.prototype.addEventListener = function (
this: FragmentInstanceType,
type: string,
listener: EventListener,
optionsOrUseCapture?: EventListenerOptionsOrUseCapture,
): void {
if (this._eventListeners === null) {
this._eventListeners = [];
}
const listeners = this._eventListeners;
const isNewEventListener =
indexOfEventListener(listeners, type, listener, optionsOrUseCapture) === -1;
if (isNewEventListener) {
listeners.push({type, listener, optionsOrUseCapture});
traverseFragmentInstance(
this._fragmentFiber,
addEventListenerToChild,
type,
listener,
optionsOrUseCapture,
);
}
this._eventListeners = listeners;
};
function addEventListenerToChild(
child: Fiber,
type: string,
listener: EventListener,
optionsOrUseCapture?: EventListenerOptionsOrUseCapture,
): boolean {
const instance = getInstanceFromHostFiber<Instance>(child);
instance.addEventListener(type, listener, optionsOrUseCapture);
return false;
}
FragmentInstance.prototype.removeEventListener = function (
this: FragmentInstanceType,
type: string,
listener: EventListener,
optionsOrUseCapture?: EventListenerOptionsOrUseCapture,
): void {
const listeners = this._eventListeners;
if (listeners === null) {
return;
}
if (typeof listeners !== 'undefined' && listeners.length > 0) {
traverseFragmentInstance(
this._fragmentFiber,
removeEventListenerFromChild,
type,
listener,
optionsOrUseCapture,
);
const index = indexOfEventListener(
listeners,
type,
listener,
optionsOrUseCapture,
);
if (this._eventListeners !== null) {
this._eventListeners.splice(index, 1);
}
}
};
function removeEventListenerFromChild(
child: Fiber,
type: string,
listener: EventListener,
optionsOrUseCapture?: EventListenerOptionsOrUseCapture,
): boolean {
const instance = getInstanceFromHostFiber<Instance>(child);
instance.removeEventListener(type, listener, optionsOrUseCapture);
return false;
}
FragmentInstance.prototype.dispatchEvent = function (
this: FragmentInstanceType,
event: Event,
): boolean {
const parentHostFiber = getFragmentParentHostFiber(this._fragmentFiber);
if (parentHostFiber === null) {
return true;
}
const parentHostInstance =
getInstanceFromHostFiber<Instance>(parentHostFiber);
const eventListeners = this._eventListeners;
if (
(eventListeners !== null && eventListeners.length > 0) ||
!event.bubbles
) {
const temp = document.createTextNode('');
if (eventListeners) {
for (let i = 0; i < eventListeners.length; i++) {
const {type, listener, optionsOrUseCapture} = eventListeners[i];
temp.addEventListener(type, listener, optionsOrUseCapture);
}
}
parentHostInstance.appendChild(temp);
const cancelable = temp.dispatchEvent(event);
if (eventListeners) {
for (let i = 0; i < eventListeners.length; i++) {
const {type, listener, optionsOrUseCapture} = eventListeners[i];
temp.removeEventListener(type, listener, optionsOrUseCapture);
}
}
parentHostInstance.removeChild(temp);
return cancelable;
} else {
return parentHostInstance.dispatchEvent(event);
}
};
FragmentInstance.prototype.focus = function (
this: FragmentInstanceType,
focusOptions?: FocusOptions,
): void {
traverseFragmentInstanceDeeply(
this._fragmentFiber,
setFocusOnFiberIfFocusable,
focusOptions,
);
};
function setFocusOnFiberIfFocusable(
fiber: Fiber,
focusOptions?: FocusOptions,
): boolean {
const instance = getInstanceFromHostFiber<Instance>(fiber);
return setFocusIfFocusable(instance, focusOptions);
}
FragmentInstance.prototype.focusLast = function (
this: FragmentInstanceType,
focusOptions?: FocusOptions,
): void {
const children: Array<Fiber> = [];
traverseFragmentInstanceDeeply(
this._fragmentFiber,
collectChildren,
children,
);
for (let i = children.length - 1; i >= 0; i--) {
const child = children[i];
if (setFocusOnFiberIfFocusable(child, focusOptions)) {
break;
}
}
};
function collectChildren(child: Fiber, collection: Array<Fiber>): boolean {
collection.push(child);
return false;
}
FragmentInstance.prototype.blur = function (this: FragmentInstanceType): void {
traverseFragmentInstance(
this._fragmentFiber,
blurActiveElementWithinFragment,
);
};
function blurActiveElementWithinFragment(child: Fiber): boolean {
const instance = getInstanceFromHostFiber<Instance>(child);
const ownerDocument = instance.ownerDocument;
if (instance === ownerDocument.activeElement) {
instance.blur();
return true;
}
return false;
}
FragmentInstance.prototype.observeUsing = function (
this: FragmentInstanceType,
observer: IntersectionObserver | ResizeObserver,
): void {
if (this._observers === null) {
this._observers = new Set();
}
this._observers.add(observer);
traverseFragmentInstance(this._fragmentFiber, observeChild, observer);
};
function observeChild(
child: Fiber,
observer: IntersectionObserver | ResizeObserver,
) {
const instance = getInstanceFromHostFiber<Instance>(child);
observer.observe(instance);
return false;
}
FragmentInstance.prototype.unobserveUsing = function (
this: FragmentInstanceType,
observer: IntersectionObserver | ResizeObserver,
): void {
if (this._observers === null || !this._observers.has(observer)) {
if (__DEV__) {
console.error(
'You are calling unobserveUsing() with an observer that is not being observed with this fragment ' +
'instance. First attach the observer with observeUsing()',
);
}
} else {
this._observers.delete(observer);
traverseFragmentInstance(this._fragmentFiber, unobserveChild, observer);
}
};
function unobserveChild(
child: Fiber,
observer: IntersectionObserver | ResizeObserver,
) {
const instance = getInstanceFromHostFiber<Instance>(child);
observer.unobserve(instance);
return false;
}
FragmentInstance.prototype.getClientRects = function (
this: FragmentInstanceType,
): Array<DOMRect> {
const rects: Array<DOMRect> = [];
traverseFragmentInstance(this._fragmentFiber, collectClientRects, rects);
return rects;
};
function collectClientRects(child: Fiber, rects: Array<DOMRect>): boolean {
const instance = getInstanceFromHostFiber<Instance>(child);
rects.push.apply(rects, instance.getClientRects());
return false;
}
FragmentInstance.prototype.getRootNode = function (
this: FragmentInstanceType,
getRootNodeOptions?: {composed: boolean},
): Document | ShadowRoot | FragmentInstanceType {
const parentHostFiber = getFragmentParentHostFiber(this._fragmentFiber);
if (parentHostFiber === null) {
return this;
}
const parentHostInstance =
getInstanceFromHostFiber<Instance>(parentHostFiber);
const rootNode =
(parentHostInstance.getRootNode(getRootNodeOptions): Document | ShadowRoot);
return rootNode;
};
FragmentInstance.prototype.compareDocumentPosition = function (
this: FragmentInstanceType,
otherNode: Instance,
): number {
const parentHostFiber = getFragmentParentHostFiber(this._fragmentFiber);
if (parentHostFiber === null) {
return Node.DOCUMENT_POSITION_DISCONNECTED;
}
const children: Array<Fiber> = [];
traverseFragmentInstance(this._fragmentFiber, collectChildren, children);
let result = Node.DOCUMENT_POSITION_DISCONNECTED;
if (children.length === 0) {
const parentHostInstance =
getInstanceFromHostFiber<Instance>(parentHostFiber);
const parentResult = parentHostInstance.compareDocumentPosition(otherNode);
result = parentResult;
if (parentHostInstance === otherNode) {
result = Node.DOCUMENT_POSITION_CONTAINS;
} else {
if (parentResult & Node.DOCUMENT_POSITION_CONTAINED_BY) {
const nextSiblingFiber = getNextSiblingHostFiber(this._fragmentFiber);
if (nextSiblingFiber === null) {
result = Node.DOCUMENT_POSITION_PRECEDING;
} else {
const nextSiblingInstance =
getInstanceFromHostFiber<Instance>(nextSiblingFiber);
const nextSiblingResult =
nextSiblingInstance.compareDocumentPosition(otherNode);
if (
nextSiblingResult === 0 ||
nextSiblingResult & Node.DOCUMENT_POSITION_FOLLOWING
) {
result = Node.DOCUMENT_POSITION_FOLLOWING;
} else {
result = Node.DOCUMENT_POSITION_PRECEDING;
}
}
}
}
result |= Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
return result;
}
const firstElement = getInstanceFromHostFiber<Instance>(children[0]);
const lastElement = getInstanceFromHostFiber<Instance>(
children[children.length - 1],
);
const firstResult = firstElement.compareDocumentPosition(otherNode);
const lastResult = lastElement.compareDocumentPosition(otherNode);
if (
(firstResult & Node.DOCUMENT_POSITION_FOLLOWING &&
lastResult & Node.DOCUMENT_POSITION_PRECEDING) ||
otherNode === firstElement ||
otherNode === lastElement
) {
result = Node.DOCUMENT_POSITION_CONTAINED_BY;
} else {
result = firstResult;
}
if (
result & Node.DOCUMENT_POSITION_DISCONNECTED ||
result & Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
) {
return result;
}
const documentPositionMatchesFiberPosition =
validateDocumentPositionWithFiberTree(
result,
this._fragmentFiber,
children[0],
children[children.length - 1],
otherNode,
);
if (documentPositionMatchesFiberPosition) {
return result;
}
return Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
};
function validateDocumentPositionWithFiberTree(
documentPosition: number,
fragmentFiber: Fiber,
precedingBoundaryFiber: Fiber,
followingBoundaryFiber: Fiber,
otherNode: Instance,
): boolean {
const otherFiber = getClosestInstanceFromNode(otherNode);
if (documentPosition & Node.DOCUMENT_POSITION_CONTAINED_BY) {
return !!otherFiber && isFiberContainedBy(fragmentFiber, otherFiber);
}
if (documentPosition & Node.DOCUMENT_POSITION_CONTAINS) {
if (otherFiber === null) {
const ownerDocument = otherNode.ownerDocument;
return otherNode === ownerDocument || otherNode === ownerDocument.body;
}
return isFiberContainedBy(otherFiber, fragmentFiber);
}
if (documentPosition & Node.DOCUMENT_POSITION_PRECEDING) {
return (
!!otherFiber &&
(otherFiber === precedingBoundaryFiber ||
isFiberPreceding(precedingBoundaryFiber, otherFiber))
);
}
if (documentPosition & Node.DOCUMENT_POSITION_FOLLOWING) {
return (
!!otherFiber &&
(otherFiber === followingBoundaryFiber ||
isFiberFollowing(followingBoundaryFiber, otherFiber))
);
}
return false;
}
function normalizeListenerOptions(
opts: ?EventListenerOptionsOrUseCapture,
): string {
if (opts == null) {
return '0';
}
if (typeof opts === 'boolean') {
return `c=${opts ? '1' : '0'}`;
}
return `c=${opts.capture ? '1' : '0'}&o=${opts.once ? '1' : '0'}&p=${opts.passive ? '1' : '0'}`;
}
function indexOfEventListener(
eventListeners: Array<StoredEventListener>,
type: string,
listener: EventListener,
optionsOrUseCapture: void | EventListenerOptionsOrUseCapture,
): number {
for (let i = 0; i < eventListeners.length; i++) {
const item = eventListeners[i];
if (
item.type === type &&
item.listener === listener &&
normalizeListenerOptions(item.optionsOrUseCapture) ===
normalizeListenerOptions(optionsOrUseCapture)
) {
return i;
}
}
return -1;
}
export function createFragmentInstance(
fragmentFiber: Fiber,
): FragmentInstanceType {
return new (FragmentInstance: any)(fragmentFiber);
}
export function updateFragmentInstanceFiber(
fragmentFiber: Fiber,
instance: FragmentInstanceType,
): void {
instance._fragmentFiber = fragmentFiber;
}
export function commitNewChildToFragmentInstance(
childInstance: Instance,
fragmentInstance: FragmentInstanceType,
): void {
const eventListeners = fragmentInstance._eventListeners;
if (eventListeners !== null) {
for (let i = 0; i < eventListeners.length; i++) {
const {type, listener, optionsOrUseCapture} = eventListeners[i];
childInstance.addEventListener(type, listener, optionsOrUseCapture);
}
}
if (fragmentInstance._observers !== null) {
fragmentInstance._observers.forEach(observer => {
observer.observe(childInstance);
});
}
}
export function deleteChildFromFragmentInstance(
childElement: Instance,
fragmentInstance: FragmentInstanceType,
): void {
const eventListeners = fragmentInstance._eventListeners;
if (eventListeners !== null) {
for (let i = 0; i < eventListeners.length; i++) {
const {type, listener, optionsOrUseCapture} = eventListeners[i];
childElement.removeEventListener(type, listener, optionsOrUseCapture);
}
}
}
export function clearContainer(container: Container): void {
const nodeType = container.nodeType;
if (nodeType === DOCUMENT_NODE) {
clearContainerSparingly(container);
} else if (nodeType === ELEMENT_NODE) {
switch (container.nodeName) {
case 'HEAD':
case 'HTML':
case 'BODY':
clearContainerSparingly(container);
return;
default: {
container.textContent = '';
}
}
}
}
function clearContainerSparingly(container: Node) {
let node;
let nextNode: ?Node = container.firstChild;
if (nextNode && nextNode.nodeType === DOCUMENT_TYPE_NODE) {
nextNode = nextNode.nextSibling;
}
while (nextNode) {
node = nextNode;
nextNode = nextNode.nextSibling;
switch (node.nodeName) {
case 'HTML':
case 'HEAD':
case 'BODY': {
const element: Element = (node: any);
clearContainerSparingly(element);
detachDeletedInstance(element);
continue;
}
case 'SCRIPT':
case 'STYLE': {
continue;
}
case 'LINK': {
if (((node: any): HTMLLinkElement).rel.toLowerCase() === 'stylesheet') {
continue;
}
}
}
container.removeChild(node);
}
return;
}
function clearHead(head: Element): void {
let node = head.firstChild;
while (node) {
const nextNode = node.nextSibling;
const nodeName = node.nodeName;
if (
isMarkedHoistable(node) ||
nodeName === 'SCRIPT' ||
nodeName === 'STYLE' ||
(nodeName === 'LINK' &&
((node: any): HTMLLinkElement).rel.toLowerCase() === 'stylesheet')
) {
} else {
head.removeChild(node);
}
node = nextNode;
}
return;
}
export function bindInstance(
instance: Instance,
props: Props,
internalInstanceHandle: mixed,
) {
precacheFiberNode((internalInstanceHandle: any), instance);
updateFiberProps(instance, props);
}
export const supportsHydration = true;
export function canHydrateInstance(
instance: HydratableInstance,
type: string,
props: Props,
inRootOrSingleton: boolean,
): null | Instance {
while (instance.nodeType === ELEMENT_NODE) {
const element: Element = (instance: any);
const anyProps = (props: any);
if (element.nodeName.toLowerCase() !== type.toLowerCase()) {
if (!inRootOrSingleton) {
if (element.nodeName === 'INPUT' && (element: any).type === 'hidden') {
} else {
return null;
}
}
} else if (!inRootOrSingleton) {
if (type === 'input' && (element: any).type === 'hidden') {
if (__DEV__) {
checkAttributeStringCoercion(anyProps.name, 'name');
}
const name = anyProps.name == null ? null : '' + anyProps.name;
if (
anyProps.type !== 'hidden' ||
element.getAttribute('name') !== name
) {
} else {
return element;
}
} else {
return element;
}
} else if (isMarkedHoistable(element)) {
} else {
switch (type) {
case 'meta': {
if (!element.hasAttribute('itemprop')) {
break;
}
return element;
}
case 'link': {
const rel = element.getAttribute('rel');
if (rel === 'stylesheet' && element.hasAttribute('data-precedence')) {
break;
} else if (
rel !== anyProps.rel ||
element.getAttribute('href') !==
(anyProps.href == null || anyProps.href === ''
? null
: anyProps.href) ||
element.getAttribute('crossorigin') !==
(anyProps.crossOrigin == null ? null : anyProps.crossOrigin) ||
element.getAttribute('title') !==
(anyProps.title == null ? null : anyProps.title)
) {
break;
}
return element;
}
case 'style': {
if (element.hasAttribute('data-precedence')) {
break;
}
return element;
}
case 'script': {
const srcAttr = element.getAttribute('src');
if (
srcAttr !== (anyProps.src == null ? null : anyProps.src) ||
element.getAttribute('type') !==
(anyProps.type == null ? null : anyProps.type) ||
element.getAttribute('crossorigin') !==
(anyProps.crossOrigin == null ? null : anyProps.crossOrigin)
) {
if (
srcAttr &&
element.hasAttribute('async') &&
!element.hasAttribute('itemprop')
) {
break;
}
}
return element;
}
default: {
return element;
}
}
}
const nextInstance = getNextHydratableSibling(element);
if (nextInstance === null) {
break;
}
instance = nextInstance;
}
return null;
}
export function canHydrateTextInstance(
instance: HydratableInstance,
text: string,
inRootOrSingleton: boolean,
): null | TextInstance {
if (text === '') return null;
while (instance.nodeType !== TEXT_NODE) {
if (
instance.nodeType === ELEMENT_NODE &&
instance.nodeName === 'INPUT' &&
(instance: any).type === 'hidden'
) {
} else if (!inRootOrSingleton) {
return null;
}
const nextInstance = getNextHydratableSibling(instance);
if (nextInstance === null) {
return null;
}
instance = nextInstance;
}
return ((instance: any): TextInstance);
}
function canHydrateHydrationBoundary(
instance: HydratableInstance,
inRootOrSingleton: boolean,
): null | SuspenseInstance | ActivityInstance {
while (instance.nodeType !== COMMENT_NODE) {
if (!inRootOrSingleton) {
return null;
}
const nextInstance = getNextHydratableSibling(instance);
if (nextInstance === null) {
return null;
}
instance = nextInstance;
}
return (instance: any);
}
export function canHydrateActivityInstance(
instance: HydratableInstance,
inRootOrSingleton: boolean,
): null | ActivityInstance {
const hydratableInstance = canHydrateHydrationBoundary(
instance,
inRootOrSingleton,
);
if (
hydratableInstance !== null &&
hydratableInstance.data === ACTIVITY_START_DATA
) {
return (hydratableInstance: any);
}
return null;
}
export function canHydrateSuspenseInstance(
instance: HydratableInstance,
inRootOrSingleton: boolean,
): null | SuspenseInstance {
const hydratableInstance = canHydrateHydrationBoundary(
instance,
inRootOrSingleton,
);
if (
hydratableInstance !== null &&
hydratableInstance.data !== ACTIVITY_START_DATA
) {
return (hydratableInstance: any);
}
return null;
}
export function isSuspenseInstancePending(instance: SuspenseInstance): boolean {
return (
instance.data === SUSPENSE_PENDING_START_DATA ||
instance.data === SUSPENSE_QUEUED_START_DATA
);
}
export function isSuspenseInstanceFallback(
instance: SuspenseInstance,
): boolean {
return (
instance.data === SUSPENSE_FALLBACK_START_DATA ||
(instance.data === SUSPENSE_PENDING_START_DATA &&
instance.ownerDocument.readyState !== DOCUMENT_READY_STATE_LOADING)
);
}
export function getSuspenseInstanceFallbackErrorDetails(
instance: SuspenseInstance,
): {
digest: ?string,
message?: string,
stack?: string,
componentStack?: string,
} {
const dataset =
instance.nextSibling && ((instance.nextSibling: any): HTMLElement).dataset;
let digest, message, stack, componentStack;
if (dataset) {
digest = dataset.dgst;
if (__DEV__) {
message = dataset.msg;
stack = dataset.stck;
componentStack = dataset.cstck;
}
}
if (__DEV__) {
return {
message,
digest,
stack,
componentStack,
};
} else {
return {
digest,
};
}
}
export function registerSuspenseInstanceRetry(
instance: SuspenseInstance,
callback: () => void,
) {
const ownerDocument = instance.ownerDocument;
if (instance.data === SUSPENSE_QUEUED_START_DATA) {
instance._reactRetry = callback;
} else if (
instance.data !== SUSPENSE_PENDING_START_DATA ||
ownerDocument.readyState !== DOCUMENT_READY_STATE_LOADING
) {
callback();
} else {
const listener = () => {
callback();
ownerDocument.removeEventListener('DOMContentLoaded', listener);
};
ownerDocument.addEventListener('DOMContentLoaded', listener);
instance._reactRetry = listener;
}
}
export function canHydrateFormStateMarker(
instance: HydratableInstance,
inRootOrSingleton: boolean,
): null | FormStateMarkerInstance {
while (instance.nodeType !== COMMENT_NODE) {
if (!inRootOrSingleton) {
return null;
}
const nextInstance = getNextHydratableSibling(instance);
if (nextInstance === null) {
return null;
}
instance = nextInstance;
}
const nodeData = (instance: any).data;
if (
nodeData === FORM_STATE_IS_MATCHING ||
nodeData === FORM_STATE_IS_NOT_MATCHING
) {
const markerInstance: FormStateMarkerInstance = (instance: any);
return markerInstance;
}
return null;
}
export function isFormStateMarkerMatching(
markerInstance: FormStateMarkerInstance,
): boolean {
return markerInstance.data === FORM_STATE_IS_MATCHING;
}
function getNextHydratable(node: ?Node) {
for (; node != null; node = ((node: any): Node).nextSibling) {
const nodeType = node.nodeType;
if (nodeType === ELEMENT_NODE || nodeType === TEXT_NODE) {
break;
}
if (nodeType === COMMENT_NODE) {
const data = (node: any).data;
if (
data === SUSPENSE_START_DATA ||
data === SUSPENSE_FALLBACK_START_DATA ||
data === SUSPENSE_PENDING_START_DATA ||
data === SUSPENSE_QUEUED_START_DATA ||
data === ACTIVITY_START_DATA ||
data === FORM_STATE_IS_MATCHING ||
data === FORM_STATE_IS_NOT_MATCHING
) {
break;
}
if (data === SUSPENSE_END_DATA || data === ACTIVITY_END_DATA) {
return null;
}
}
}
return (node: any);
}
export function getNextHydratableSibling(
instance: HydratableInstance,
): null | HydratableInstance {
return getNextHydratable(instance.nextSibling);
}
export function getFirstHydratableChild(
parentInstance: Instance,
): null | HydratableInstance {
return getNextHydratable(parentInstance.firstChild);
}
export function getFirstHydratableChildWithinContainer(
parentContainer: Container,
): null | HydratableInstance {
let parentElement: Element;
switch (parentContainer.nodeType) {
case DOCUMENT_NODE:
parentElement = (parentContainer: any).body;
break;
default: {
if (parentContainer.nodeName === 'HTML') {
parentElement = (parentContainer: any).ownerDocument.body;
} else {
parentElement = (parentContainer: any);
}
}
}
return getNextHydratable(parentElement.firstChild);
}
export function getFirstHydratableChildWithinActivityInstance(
parentInstance: ActivityInstance,
): null | HydratableInstance {
return getNextHydratable(parentInstance.nextSibling);
}
export function getFirstHydratableChildWithinSuspenseInstance(
parentInstance: SuspenseInstance,
): null | HydratableInstance {
return getNextHydratable(parentInstance.nextSibling);
}
let previousHydratableOnEnteringScopedSingleton: null | HydratableInstance =
null;
export function getFirstHydratableChildWithinSingleton(
type: string,
singletonInstance: Instance,
currentHydratableInstance: null | HydratableInstance,
): null | HydratableInstance {
if (isSingletonScope(type)) {
previousHydratableOnEnteringScopedSingleton = currentHydratableInstance;
return getNextHydratable(singletonInstance.firstChild);
} else {
return currentHydratableInstance;
}
}
export function getNextHydratableSiblingAfterSingleton(
type: string,
currentHydratableInstance: null | HydratableInstance,
): null | HydratableInstance {
if (isSingletonScope(type)) {
const previousHydratableInstance =
previousHydratableOnEnteringScopedSingleton;
previousHydratableOnEnteringScopedSingleton = null;
return previousHydratableInstance;
} else {
return currentHydratableInstance;
}
}
export function describeHydratableInstanceForDevWarnings(
instance: HydratableInstance,
): string | {type: string, props: $ReadOnly<Props>} {
if (instance.nodeType === ELEMENT_NODE) {
return {
type: instance.nodeName.toLowerCase(),
props: getPropsFromElement((instance: any)),
};
} else if (instance.nodeType === COMMENT_NODE) {
if (instance.data === ACTIVITY_START_DATA) {
return {
type: 'Activity',
props: {},
};
}
return {
type: 'Suspense',
props: {},
};
} else {
return instance.nodeValue;
}
}
export function validateHydratableInstance(
type: string,
props: Props,
hostContext: HostContext,
): boolean {
if (__DEV__) {
const hostContextDev: HostContextDev = (hostContext: any);
return validateDOMNesting(type, hostContextDev.ancestorInfo);
}
return true;
}
export function hydrateInstance(
instance: Instance,
type: string,
props: Props,
hostContext: HostContext,
internalInstanceHandle: Object,
): boolean {
precacheFiberNode(internalInstanceHandle, instance);
updateFiberProps(instance, props);
return hydrateProperties(instance, type, props, hostContext);
}
export function diffHydratedPropsForDevWarnings(
instance: Instance,
type: string,
props: Props,
hostContext: HostContext,
): null | $ReadOnly<Props> {
return diffHydratedProperties(instance, type, props, hostContext);
}
export function validateHydratableTextInstance(
text: string,
hostContext: HostContext,
): boolean {
if (__DEV__) {
const hostContextDev = ((hostContext: any): HostContextDev);
const ancestor = hostContextDev.ancestorInfo.current;
if (ancestor != null) {
return validateTextNesting(
text,
ancestor.tag,
hostContextDev.ancestorInfo.implicitRootScope,
);
}
}
return true;
}
export function hydrateTextInstance(
textInstance: TextInstance,
text: string,
internalInstanceHandle: Object,
parentInstanceProps: null | Props,
): boolean {
precacheFiberNode(internalInstanceHandle, textInstance);
return hydrateText(textInstance, text, parentInstanceProps);
}
export function diffHydratedTextForDevWarnings(
textInstance: TextInstance,
text: string,
parentProps: null | Props,
): null | string {
if (
parentProps === null ||
parentProps[SUPPRESS_HYDRATION_WARNING] !== true
) {
return diffHydratedText(textInstance, text);
}
return null;
}
export function hydrateActivityInstance(
activityInstance: ActivityInstance,
internalInstanceHandle: Object,
) {
precacheFiberNode(internalInstanceHandle, activityInstance);
}
export function hydrateSuspenseInstance(
suspenseInstance: SuspenseInstance,
internalInstanceHandle: Object,
) {
precacheFiberNode(internalInstanceHandle, suspenseInstance);
}
function getNextHydratableInstanceAfterHydrationBoundary(
hydrationInstance: SuspenseInstance | ActivityInstance,
): null | HydratableInstance {
let node = hydrationInstance.nextSibling;
let depth = 0;
while (node) {
if (node.nodeType === COMMENT_NODE) {
const data = ((node: any).data: string);
if (data === SUSPENSE_END_DATA || data === ACTIVITY_END_DATA) {
if (depth === 0) {
return getNextHydratableSibling((node: any));
} else {
depth--;
}
} else if (
data === SUSPENSE_START_DATA ||
data === SUSPENSE_FALLBACK_START_DATA ||
data === SUSPENSE_PENDING_START_DATA ||
data === SUSPENSE_QUEUED_START_DATA ||
data === ACTIVITY_START_DATA
) {
depth++;
}
}
node = node.nextSibling;
}
return null;
}
export function getNextHydratableInstanceAfterActivityInstance(
activityInstance: ActivityInstance,
): null | HydratableInstance {
return getNextHydratableInstanceAfterHydrationBoundary(activityInstance);
}
export function getNextHydratableInstanceAfterSuspenseInstance(
suspenseInstance: SuspenseInstance,
): null | HydratableInstance {
return getNextHydratableInstanceAfterHydrationBoundary(suspenseInstance);
}
export function getParentHydrationBoundary(
targetInstance: Node,
): null | SuspenseInstance | ActivityInstance {
let node = targetInstance.previousSibling;
let depth = 0;
while (node) {
if (node.nodeType === COMMENT_NODE) {
const data = ((node: any).data: string);
if (
data === SUSPENSE_START_DATA ||
data === SUSPENSE_FALLBACK_START_DATA ||
data === SUSPENSE_PENDING_START_DATA ||
data === SUSPENSE_QUEUED_START_DATA ||
data === ACTIVITY_START_DATA
) {
if (depth === 0) {
return ((node: any): SuspenseInstance | ActivityInstance);
} else {
depth--;
}
} else if (data === SUSPENSE_END_DATA || data === ACTIVITY_END_DATA) {
depth++;
}
}
node = node.previousSibling;
}
return null;
}
export function commitHydratedContainer(container: Container): void {
retryIfBlockedOn(container);
}
export function commitHydratedActivityInstance(
activityInstance: ActivityInstance,
): void {
retryIfBlockedOn(activityInstance);
}
export function commitHydratedSuspenseInstance(
suspenseInstance: SuspenseInstance,
): void {
retryIfBlockedOn(suspenseInstance);
}
export function flushHydrationEvents(): void {
if (enableHydrationChangeEvent) {
flushEventReplaying();
}
}
export function shouldDeleteUnhydratedTailInstances(
parentType: string,
): boolean {
return parentType !== 'form' && parentType !== 'button';
}
export const supportsTestSelectors = true;
export function findFiberRoot(node: Instance): null | FiberRoot {
const stack = [node];
let index = 0;
while (index < stack.length) {
const current = stack[index++];
if (isContainerMarkedAsRoot(current)) {
return ((getInstanceFromNodeDOMTree(current): any): FiberRoot);
}
stack.push(...current.children);
}
return null;
}
export function getBoundingRect(node: Instance): BoundingRect {
const rect = node.getBoundingClientRect();
return {
x: rect.left,
y: rect.top,
width: rect.width,
height: rect.height,
};
}
export function matchAccessibilityRole(node: Instance, role: string): boolean {
if (hasRole(node, role)) {
return true;
}
return false;
}
export function getTextContent(fiber: Fiber): string | null {
switch (fiber.tag) {
case HostHoistable:
case HostSingleton:
case HostComponent:
let textContent = '';
const childNodes = fiber.stateNode.childNodes;
for (let i = 0; i < childNodes.length; i++) {
const childNode = childNodes[i];
if (childNode.nodeType === Node.TEXT_NODE) {
textContent += childNode.textContent;
}
}
return textContent;
case HostText:
return fiber.stateNode.textContent;
}
return null;
}
export function isHiddenSubtree(fiber: Fiber): boolean {
return fiber.tag === HostComponent && fiber.memoizedProps.hidden === true;
}
export function setFocusIfFocusable(
node: Instance,
focusOptions?: FocusOptions,
): boolean {
let didFocus = false;
const handleFocus = () => {
didFocus = true;
};
const element = ((node: any): HTMLElement);
try {
element.addEventListener('focus', handleFocus);
(element.focus || HTMLElement.prototype.focus).call(element, focusOptions);
} finally {
element.removeEventListener('focus', handleFocus);
}
return didFocus;
}
type RectRatio = {
ratio: number,
rect: BoundingRect,
};
export function setupIntersectionObserver(
targets: Array<Instance>,
callback: ObserveVisibleRectsCallback,
options?: IntersectionObserverOptions,
): {
disconnect: () => void,
observe: (instance: Instance) => void,
unobserve: (instance: Instance) => void,
} {
const rectRatioCache: Map<Instance, RectRatio> = new Map();
targets.forEach(target => {
rectRatioCache.set(target, {
rect: getBoundingRect(target),
ratio: 0,
});
});
const handleIntersection = (entries: Array<IntersectionObserverEntry>) => {
entries.forEach(entry => {
const {boundingClientRect, intersectionRatio, target} = entry;
rectRatioCache.set(target, {
rect: {
x: boundingClientRect.left,
y: boundingClientRect.top,
width: boundingClientRect.width,
height: boundingClientRect.height,
},
ratio: intersectionRatio,
});
});
callback(Array.from(rectRatioCache.values()));
};
const observer = new IntersectionObserver(handleIntersection, options);
targets.forEach(target => {
observer.observe((target: any));
});
return {
disconnect: () => observer.disconnect(),
observe: target => {
rectRatioCache.set(target, {
rect: getBoundingRect(target),
ratio: 0,
});
observer.observe((target: any));
},
unobserve: target => {
rectRatioCache.delete(target);
observer.unobserve((target: any));
},
};
}
export function requestPostPaintCallback(callback: (time: number) => void) {
localRequestAnimationFrame(() => {
localRequestAnimationFrame(time => callback(time));
});
}
export const supportsSingletons = true;
export function isHostSingletonType(type: string): boolean {
return type === 'html' || type === 'head' || type === 'body';
}
export function resolveSingletonInstance(
type: string,
props: Props,
rootContainerInstance: Container,
hostContext: HostContext,
validateDOMNestingDev: boolean,
): Instance {
if (__DEV__) {
const hostContextDev = ((hostContext: any): HostContextDev);
if (validateDOMNestingDev) {
validateDOMNesting(type, hostContextDev.ancestorInfo);
}
}
const ownerDocument = getOwnerDocumentFromRootContainer(
rootContainerInstance,
);
switch (type) {
case 'html': {
const documentElement = ownerDocument.documentElement;
if (!documentElement) {
throw new Error(
'React expected an <html> element (document.documentElement) to exist in the Document but one was' +
' not found. React never removes the documentElement for any Document it renders into so' +
' the cause is likely in some other script running on this page.',
);
}
return documentElement;
}
case 'head': {
const head = ownerDocument.head;
if (!head) {
throw new Error(
'React expected a <head> element (document.head) to exist in the Document but one was' +
' not found. React never removes the head for any Document it renders into so' +
' the cause is likely in some other script running on this page.',
);
}
return head;
}
case 'body': {
const body = ownerDocument.body;
if (!body) {
throw new Error(
'React expected a <body> element (document.body) to exist in the Document but one was' +
' not found. React never removes the body for any Document it renders into so' +
' the cause is likely in some other script running on this page.',
);
}
return body;
}
default: {
throw new Error(
'resolveSingletonInstance was called with an element type that is not supported. This is a bug in React.',
);
}
}
}
export function acquireSingletonInstance(
type: string,
props: Props,
instance: Instance,
internalInstanceHandle: Object,
): void {
if (__DEV__) {
if (
!isContainerMarkedAsRoot(instance) &&
getInstanceFromNodeDOMTree(instance)
) {
const tagName = instance.tagName.toLowerCase();
console.error(
'You are mounting a new %s component when a previous one has not first unmounted. It is an' +
' error to render more than one %s component at a time and attributes and children of these' +
' components will likely fail in unpredictable ways. Please only render a single instance of' +
' <%s> and if you need to mount a new one, ensure any previous ones have unmounted first.',
tagName,
tagName,
tagName,
);
}
switch (type) {
case 'html':
case 'head':
case 'body': {
break;
}
default: {
console.error(
'acquireSingletonInstance was called with an element type that is not supported. This is a bug in React.',
);
}
}
}
const attributes = instance.attributes;
while (attributes.length) {
instance.removeAttributeNode(attributes[0]);
}
setInitialProperties(instance, type, props);
precacheFiberNode(internalInstanceHandle, instance);
updateFiberProps(instance, props);
}
export function releaseSingletonInstance(instance: Instance): void {
const attributes = instance.attributes;
while (attributes.length) {
instance.removeAttributeNode(attributes[0]);
}
detachDeletedInstance(instance);
}
export const supportsResources = true;
type HoistableTagType = 'link' | 'meta' | 'title';
type TResource<
T: 'stylesheet' | 'style' | 'script' | 'void',
S: null | {...},
> = {
type: T,
instance: null | Instance,
count: number,
state: S,
};
type StylesheetResource = TResource<'stylesheet', StylesheetState>;
type StyleTagResource = TResource<'style', null>;
type StyleResource = StyleTagResource | StylesheetResource;
type ScriptResource = TResource<'script', null>;
type VoidResource = TResource<'void', null>;
export type Resource = StyleResource | ScriptResource | VoidResource;
type LoadingState = number;
const NotLoaded = 0b000;
const Loaded = 0b001;
const Errored = 0b010;
const Settled = 0b011;
const Inserted = 0b100;
type StylesheetState = {
loading: LoadingState,
preload: null | HTMLLinkElement,
};
type StyleTagProps = {
'data-href': string,
'data-precedence': string,
[string]: mixed,
};
type StylesheetProps = {
rel: 'stylesheet',
href: string,
'data-precedence': string,
[string]: mixed,
};
type ScriptProps = {
src: string,
async: true,
[string]: mixed,
};
type PreloadProps = {
rel: 'preload',
href: ?string,
[string]: mixed,
};
type PreloadModuleProps = {
rel: 'modulepreload',
href: string,
[string]: mixed,
};
export type RootResources = {
hoistableStyles: Map<string, StyleResource>,
hoistableScripts: Map<string, ScriptResource>,
};
export function prepareToCommitHoistables() {
tagCaches = null;
}
const preloadPropsMap: Map<string, PreloadProps | PreloadModuleProps> =
new Map();
const preconnectsSet: Set<string> = new Set();
export type HoistableRoot = Document | ShadowRoot;
export function getHoistableRoot(container: Container): HoistableRoot {
return typeof container.getRootNode === 'function'
?
(container.getRootNode(): Document | ShadowRoot)
: container.nodeType === DOCUMENT_NODE
?
(container: Document)
: container.ownerDocument;
}
function getCurrentResourceRoot(): null | HoistableRoot {
const currentContainer = getCurrentRootHostContainer();
return currentContainer ? getHoistableRoot(currentContainer) : null;
}
function getDocumentFromRoot(root: HoistableRoot): Document {
return root.ownerDocument || root;
}
const previousDispatcher =
ReactDOMSharedInternals.d;
ReactDOMSharedInternals.d = {
f : disableLegacyMode
? flushSyncWork
: previousDispatcher.f ,
r: requestFormReset,
D : prefetchDNS,
C : preconnect,
L : preload,
m : preloadModule,
X : preinitScript,
S : preinitStyle,
M : preinitModuleScript,
};
function flushSyncWork() {
if (disableLegacyMode) {
const previousWasRendering = previousDispatcher.f();
const wasRendering = flushSyncWorkOnAllRoots();
return previousWasRendering || wasRendering;
} else {
throw new Error(
'flushSyncWork should not be called from builds that support legacy mode. This is a bug in React.',
);
}
}
function requestFormReset(form: HTMLFormElement) {
const formInst = getInstanceFromNodeDOMTree(form);
if (
formInst !== null &&
formInst.tag === HostComponent &&
formInst.type === 'form'
) {
requestFormResetOnFiber(formInst);
} else {
previousDispatcher.r( form);
}
}
const globalDocument = typeof document === 'undefined' ? null : document;
function getGlobalDocument(): ?Document {
return globalDocument;
}
function preconnectAs(
rel: 'preconnect' | 'dns-prefetch',
href: string,
crossOrigin: ?CrossOriginEnum,
) {
const ownerDocument = getGlobalDocument();
if (ownerDocument && typeof href === 'string' && href) {
const limitedEscapedHref =
escapeSelectorAttributeValueInsideDoubleQuotes(href);
let key = `link[rel="${rel}"][href="${limitedEscapedHref}"]`;
if (typeof crossOrigin === 'string') {
key += `[crossorigin="${crossOrigin}"]`;
}
if (!preconnectsSet.has(key)) {
preconnectsSet.add(key);
const preconnectProps = {rel, crossOrigin, href};
if (null === ownerDocument.querySelector(key)) {
const instance = ownerDocument.createElement('link');
setInitialProperties(instance, 'link', preconnectProps);
markNodeAsHoistable(instance);
(ownerDocument.head: any).appendChild(instance);
}
}
}
}
function prefetchDNS(href: string) {
previousDispatcher.D( href);
preconnectAs('dns-prefetch', href, null);
}
function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) {
previousDispatcher.C( href, crossOrigin);
preconnectAs('preconnect', href, crossOrigin);
}
function preload(href: string, as: string, options?: ?PreloadImplOptions) {
previousDispatcher.L( href, as, options);
const ownerDocument = getGlobalDocument();
if (ownerDocument && href && as) {
let preloadSelector = `link[rel="preload"][as="${escapeSelectorAttributeValueInsideDoubleQuotes(
as,
)}"]`;
if (as === 'image') {
if (options && options.imageSrcSet) {
preloadSelector += `[imagesrcset="${escapeSelectorAttributeValueInsideDoubleQuotes(
options.imageSrcSet,
)}"]`;
if (typeof options.imageSizes === 'string') {
preloadSelector += `[imagesizes="${escapeSelectorAttributeValueInsideDoubleQuotes(
options.imageSizes,
)}"]`;
}
} else {
preloadSelector += `[href="${escapeSelectorAttributeValueInsideDoubleQuotes(
href,
)}"]`;
}
} else {
preloadSelector += `[href="${escapeSelectorAttributeValueInsideDoubleQuotes(
href,
)}"]`;
}
let key = preloadSelector;
switch (as) {
case 'style':
key = getStyleKey(href);
break;
case 'script':
key = getScriptKey(href);
break;
}
if (!preloadPropsMap.has(key)) {
const preloadProps = Object.assign(
({
rel: 'preload',
href:
as === 'image' && options && options.imageSrcSet ? undefined : href,
as,
}: PreloadProps),
options,
);
preloadPropsMap.set(key, preloadProps);
if (null === ownerDocument.querySelector(preloadSelector)) {
if (
as === 'style' &&
ownerDocument.querySelector(getStylesheetSelectorFromKey(key))
) {
return;
} else if (
as === 'script' &&
ownerDocument.querySelector(getScriptSelectorFromKey(key))
) {
return;
}
const instance = ownerDocument.createElement('link');
setInitialProperties(instance, 'link', preloadProps);
markNodeAsHoistable(instance);
(ownerDocument.head: any).appendChild(instance);
}
}
}
}
function preloadModule(href: string, options?: ?PreloadModuleImplOptions) {
previousDispatcher.m( href, options);
const ownerDocument = getGlobalDocument();
if (ownerDocument && href) {
const as =
options && typeof options.as === 'string' ? options.as : 'script';
const preloadSelector = `link[rel="modulepreload"][as="${escapeSelectorAttributeValueInsideDoubleQuotes(
as,
)}"][href="${escapeSelectorAttributeValueInsideDoubleQuotes(href)}"]`;
let key = preloadSelector;
switch (as) {
case 'audioworklet':
case 'paintworklet':
case 'serviceworker':
case 'sharedworker':
case 'worker':
case 'script': {
key = getScriptKey(href);
break;
}
}
if (!preloadPropsMap.has(key)) {
const props: PreloadModuleProps = Object.assign(
({
rel: 'modulepreload',
href,
}: PreloadModuleProps),
options,
);
preloadPropsMap.set(key, props);
if (null === ownerDocument.querySelector(preloadSelector)) {
switch (as) {
case 'audioworklet':
case 'paintworklet':
case 'serviceworker':
case 'sharedworker':
case 'worker':
case 'script': {
if (ownerDocument.querySelector(getScriptSelectorFromKey(key))) {
return;
}
}
}
const instance = ownerDocument.createElement('link');
setInitialProperties(instance, 'link', props);
markNodeAsHoistable(instance);
(ownerDocument.head: any).appendChild(instance);
}
}
}
}
function preinitStyle(
href: string,
precedence: ?string,
options?: ?PreinitStyleOptions,
) {
previousDispatcher.S( href, precedence, options);
const ownerDocument = getGlobalDocument();
if (ownerDocument && href) {
const styles = getResourcesFromRoot(ownerDocument).hoistableStyles;
const key = getStyleKey(href);
precedence = precedence || 'default';
let resource = styles.get(key);
if (resource) {
return;
}
const state = {
loading: NotLoaded,
preload: null,
};
let instance: null | Instance = ownerDocument.querySelector(
getStylesheetSelectorFromKey(key),
);
if (instance) {
state.loading = Loaded | Inserted;
} else {
const stylesheetProps = Object.assign(
({
rel: 'stylesheet',
href,
'data-precedence': precedence,
}: StylesheetProps),
options,
);
const preloadProps = preloadPropsMap.get(key);
if (preloadProps) {
adoptPreloadPropsForStylesheet(stylesheetProps, preloadProps);
}
const link = (instance = ownerDocument.createElement('link'));
markNodeAsHoistable(link);
setInitialProperties(link, 'link', stylesheetProps);
(link: any)._p = new Promise((resolve, reject) => {
link.onload = resolve;
link.onerror = reject;
});
link.addEventListener('load', () => {
state.loading |= Loaded;
});
link.addEventListener('error', () => {
state.loading |= Errored;
});
state.loading |= Inserted;
insertStylesheet(instance, precedence, ownerDocument);
}
resource = {
type: 'stylesheet',
instance,
count: 1,
state,
};
styles.set(key, resource);
return;
}
}
function preinitScript(src: string, options?: ?PreinitScriptOptions) {
previousDispatcher.X( src, options);
const ownerDocument = getGlobalDocument();
if (ownerDocument && src) {
const scripts = getResourcesFromRoot(ownerDocument).hoistableScripts;
const key = getScriptKey(src);
let resource = scripts.get(key);
if (resource) {
return;
}
let instance: null | Instance = ownerDocument.querySelector(
getScriptSelectorFromKey(key),
);
if (!instance) {
const scriptProps = Object.assign(
({
src,
async: true,
}: ScriptProps),
options,
);
const preloadProps = preloadPropsMap.get(key);
if (preloadProps) {
adoptPreloadPropsForScript(scriptProps, preloadProps);
}
instance = ownerDocument.createElement('script');
markNodeAsHoistable(instance);
setInitialProperties(instance, 'link', scriptProps);
(ownerDocument.head: any).appendChild(instance);
}
resource = {
type: 'script',
instance,
count: 1,
state: null,
};
scripts.set(key, resource);
return;
}
}
function preinitModuleScript(
src: string,
options?: ?PreinitModuleScriptOptions,
) {
previousDispatcher.M( src, options);
const ownerDocument = getGlobalDocument();
if (ownerDocument && src) {
const scripts = getResourcesFromRoot(ownerDocument).hoistableScripts;
const key = getScriptKey(src);
let resource = scripts.get(key);
if (resource) {
return;
}
let instance: null | Instance = ownerDocument.querySelector(
getScriptSelectorFromKey(key),
);
if (!instance) {
const scriptProps = Object.assign(
({
src,
async: true,
type: 'module',
}: ScriptProps),
options,
);
const preloadProps = preloadPropsMap.get(key);
if (preloadProps) {
adoptPreloadPropsForScript(scriptProps, preloadProps);
}
instance = ownerDocument.createElement('script');
markNodeAsHoistable(instance);
setInitialProperties(instance, 'link', scriptProps);
(ownerDocument.head: any).appendChild(instance);
}
resource = {
type: 'script',
instance,
count: 1,
state: null,
};
scripts.set(key, resource);
return;
}
}
type StyleTagQualifyingProps = {
href: string,
precedence: string,
[string]: mixed,
};
type StylesheetQualifyingProps = {
rel: 'stylesheet',
href: string,
precedence: string,
[string]: mixed,
};
export function getResource(
type: string,
currentProps: any,
pendingProps: any,
currentResource: null | Resource,
): null | Resource {
const resourceRoot = getCurrentResourceRoot();
if (!resourceRoot) {
throw new Error(
'"resourceRoot" was expected to exist. This is a bug in React.',
);
}
switch (type) {
case 'meta':
case 'title': {
return null;
}
case 'style': {
if (
typeof pendingProps.precedence === 'string' &&
typeof pendingProps.href === 'string'
) {
const key = getStyleKey(pendingProps.href);
const styles = getResourcesFromRoot(resourceRoot).hoistableStyles;
let resource = styles.get(key);
if (!resource) {
resource = {
type: 'style',
instance: null,
count: 0,
state: null,
};
styles.set(key, resource);
}
return resource;
}
return {
type: 'void',
instance: null,
count: 0,
state: null,
};
}
case 'link': {
if (
pendingProps.rel === 'stylesheet' &&
typeof pendingProps.href === 'string' &&
typeof pendingProps.precedence === 'string'
) {
const qualifiedProps: StylesheetQualifyingProps = pendingProps;
const key = getStyleKey(qualifiedProps.href);
const styles = getResourcesFromRoot(resourceRoot).hoistableStyles;
let resource = styles.get(key);
if (!resource) {
const ownerDocument = getDocumentFromRoot(resourceRoot);
resource = ({
type: 'stylesheet',
instance: null,
count: 0,
state: {
loading: NotLoaded,
preload: null,
},
}: StylesheetResource);
styles.set(key, resource);
const instance = ownerDocument.querySelector(
getStylesheetSelectorFromKey(key),
);
if (instance) {
const loadingState: ?Promise<mixed> = (instance: any)._p;
if (loadingState) {
} else {
resource.instance = instance;
resource.state.loading = Loaded | Inserted;
}
}
if (!preloadPropsMap.has(key)) {
const preloadProps = preloadPropsFromStylesheet(qualifiedProps);
preloadPropsMap.set(key, preloadProps);
if (!instance) {
preloadStylesheet(
ownerDocument,
key,
preloadProps,
resource.state,
);
}
}
}
if (currentProps && currentResource === null) {
let diff = '';
if (__DEV__) {
diff = `
- ${describeLinkForResourceErrorDEV(currentProps)}
+ ${describeLinkForResourceErrorDEV(pendingProps)}`;
}
throw new Error(
'Expected <link> not to update to be updated to a stylesheet with precedence.' +
' Check the `rel`, `href`, and `precedence` props of this component.' +
' Alternatively, check whether two different <link> components render in the same slot or share the same key.' +
diff,
);
}
return resource;
} else {
if (currentProps && currentResource !== null) {
let diff = '';
if (__DEV__) {
diff = `
- ${describeLinkForResourceErrorDEV(currentProps)}
+ ${describeLinkForResourceErrorDEV(pendingProps)}`;
}
throw new Error(
'Expected stylesheet with precedence to not be updated to a different kind of <link>.' +
' Check the `rel`, `href`, and `precedence` props of this component.' +
' Alternatively, check whether two different <link> components render in the same slot or share the same key.' +
diff,
);
}
return null;
}
}
case 'script': {
const async = pendingProps.async;
const src = pendingProps.src;
if (
typeof src === 'string' &&
async &&
typeof async !== 'function' &&
typeof async !== 'symbol'
) {
const key = getScriptKey(src);
const scripts = getResourcesFromRoot(resourceRoot).hoistableScripts;
let resource = scripts.get(key);
if (!resource) {
resource = {
type: 'script',
instance: null,
count: 0,
state: null,
};
scripts.set(key, resource);
}
return resource;
}
return {
type: 'void',
instance: null,
count: 0,
state: null,
};
}
default: {
throw new Error(
`getResource encountered a type it did not expect: "${type}". this is a bug in React.`,
);
}
}
}
function describeLinkForResourceErrorDEV(props: any) {
if (__DEV__) {
let describedProps = 0;
let description = '<link';
if (typeof props.rel === 'string') {
describedProps++;
description += ` rel="${props.rel}"`;
} else if (hasOwnProperty.call(props, 'rel')) {
describedProps++;
description += ` rel="${
props.rel === null ? 'null' : 'invalid type ' + typeof props.rel
}"`;
}
if (typeof props.href === 'string') {
describedProps++;
description += ` href="${props.href}"`;
} else if (hasOwnProperty.call(props, 'href')) {
describedProps++;
description += ` href="${
props.href === null ? 'null' : 'invalid type ' + typeof props.href
}"`;
}
if (typeof props.precedence === 'string') {
describedProps++;
description += ` precedence="${props.precedence}"`;
} else if (hasOwnProperty.call(props, 'precedence')) {
describedProps++;
description += ` precedence={${
props.precedence === null
? 'null'
: 'invalid type ' + typeof props.precedence
}}`;
}
if (Object.getOwnPropertyNames(props).length > describedProps) {
description += ' ...';
}
description += ' />';
return description;
}
return '';
}
function styleTagPropsFromRawProps(
rawProps: StyleTagQualifyingProps,
): StyleTagProps {
return {
...rawProps,
'data-href': rawProps.href,
'data-precedence': rawProps.precedence,
href: null,
precedence: null,
};
}
function getStyleKey(href: string) {
const limitedEscapedHref =
escapeSelectorAttributeValueInsideDoubleQuotes(href);
return `href="${limitedEscapedHref}"`;
}
function getStyleTagSelector(href: string) {
const limitedEscapedHref =
escapeSelectorAttributeValueInsideDoubleQuotes(href);
return `style[data-href~="${limitedEscapedHref}"]`;
}
function getStylesheetSelectorFromKey(key: string) {
return `link[rel="stylesheet"][${key}]`;
}
function getPreloadStylesheetSelectorFromKey(key: string) {
return `link[rel="preload"][as="style"][${key}]`;
}
function stylesheetPropsFromRawProps(
rawProps: StylesheetQualifyingProps,
): StylesheetProps {
return {
...rawProps,
'data-precedence': rawProps.precedence,
precedence: null,
};
}
function preloadStylesheet(
ownerDocument: Document,
key: string,
preloadProps: PreloadProps,
state: StylesheetState,
) {
const preloadEl = ownerDocument.querySelector(
getPreloadStylesheetSelectorFromKey(key),
);
if (preloadEl) {
state.loading = Loaded;
} else {
const instance = ownerDocument.createElement('link');
state.preload = instance;
instance.addEventListener('load', () => (state.loading |= Loaded));
instance.addEventListener('error', () => (state.loading |= Errored));
setInitialProperties(instance, 'link', preloadProps);
markNodeAsHoistable(instance);
(ownerDocument.head: any).appendChild(instance);
}
}
function preloadPropsFromStylesheet(
props: StylesheetQualifyingProps,
): PreloadProps {
return {
rel: 'preload',
as: 'style',
href: props.href,
crossOrigin: props.crossOrigin,
integrity: props.integrity,
media: props.media,
hrefLang: props.hrefLang,
referrerPolicy: props.referrerPolicy,
};
}
function getScriptKey(src: string): string {
const limitedEscapedSrc = escapeSelectorAttributeValueInsideDoubleQuotes(src);
return `[src="${limitedEscapedSrc}"]`;
}
function getScriptSelectorFromKey(key: string): string {
return 'script[async]' + key;
}
export function acquireResource(
hoistableRoot: HoistableRoot,
resource: Resource,
props: any,
): null | Instance {
resource.count++;
if (resource.instance === null) {
switch (resource.type) {
case 'style': {
const qualifiedProps: StyleTagQualifyingProps = props;
let instance: null | Instance = hoistableRoot.querySelector(
getStyleTagSelector(qualifiedProps.href),
);
if (instance) {
resource.instance = instance;
markNodeAsHoistable(instance);
return instance;
}
const styleProps = styleTagPropsFromRawProps(props);
const ownerDocument = getDocumentFromRoot(hoistableRoot);
instance = ownerDocument.createElement('style');
markNodeAsHoistable(instance);
setInitialProperties(instance, 'style', styleProps);
insertStylesheet(instance, qualifiedProps.precedence, hoistableRoot);
resource.instance = instance;
return instance;
}
case 'stylesheet': {
const qualifiedProps: StylesheetQualifyingProps = props;
const key = getStyleKey(qualifiedProps.href);
let instance: null | Instance = hoistableRoot.querySelector(
getStylesheetSelectorFromKey(key),
);
if (instance) {
resource.state.loading |= Inserted;
resource.instance = instance;
markNodeAsHoistable(instance);
return instance;
}
const stylesheetProps = stylesheetPropsFromRawProps(props);
const preloadProps = preloadPropsMap.get(key);
if (preloadProps) {
adoptPreloadPropsForStylesheet(stylesheetProps, preloadProps);
}
const ownerDocument = getDocumentFromRoot(hoistableRoot);
instance = ownerDocument.createElement('link');
markNodeAsHoistable(instance);
const linkInstance: HTMLLinkElement = (instance: any);
(linkInstance: any)._p = new Promise((resolve, reject) => {
linkInstance.onload = resolve;
linkInstance.onerror = reject;
});
setInitialProperties(instance, 'link', stylesheetProps);
resource.state.loading |= Inserted;
insertStylesheet(instance, qualifiedProps.precedence, hoistableRoot);
resource.instance = instance;
return instance;
}
case 'script': {
const borrowedScriptProps: ScriptProps = props;
const key = getScriptKey(borrowedScriptProps.src);
let instance: null | Instance = hoistableRoot.querySelector(
getScriptSelectorFromKey(key),
);
if (instance) {
resource.instance = instance;
markNodeAsHoistable(instance);
return instance;
}
let scriptProps = borrowedScriptProps;
const preloadProps = preloadPropsMap.get(key);
if (preloadProps) {
scriptProps = {...borrowedScriptProps};
adoptPreloadPropsForScript(scriptProps, preloadProps);
}
const ownerDocument = getDocumentFromRoot(hoistableRoot);
instance = ownerDocument.createElement('script');
markNodeAsHoistable(instance);
setInitialProperties(instance, 'link', scriptProps);
(ownerDocument.head: any).appendChild(instance);
resource.instance = instance;
return instance;
}
case 'void': {
return null;
}
default: {
throw new Error(
`acquireResource encountered a resource type it did not expect: "${resource.type}". this is a bug in React.`,
);
}
}
} else {
if (
resource.type === 'stylesheet' &&
(resource.state.loading & Inserted) === NotLoaded
) {
const qualifiedProps: StylesheetQualifyingProps = props;
const instance: Instance = resource.instance;
resource.state.loading |= Inserted;
insertStylesheet(instance, qualifiedProps.precedence, hoistableRoot);
}
}
return resource.instance;
}
export function releaseResource(resource: Resource): void {
resource.count--;
}
function insertStylesheet(
instance: Element,
precedence: string,
root: HoistableRoot,
): void {
const nodes = root.querySelectorAll(
'link[rel="stylesheet"][data-precedence],style[data-precedence]',
);
const last = nodes.length ? nodes[nodes.length - 1] : null;
let prior = last;
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
const nodePrecedence = node.dataset.precedence;
if (nodePrecedence === precedence) {
prior = node;
} else if (prior !== last) {
break;
}
}
if (prior) {
((prior.parentNode: any): Node).insertBefore(instance, prior.nextSibling);
} else {
const parent =
root.nodeType === DOCUMENT_NODE
? ((((root: any): Document).head: any): Element)
: ((root: any): ShadowRoot);
parent.insertBefore(instance, parent.firstChild);
}
}
function adoptPreloadPropsForStylesheet(
stylesheetProps: StylesheetProps,
preloadProps: PreloadProps | PreloadModuleProps,
): void {
if (stylesheetProps.crossOrigin == null)
stylesheetProps.crossOrigin = preloadProps.crossOrigin;
if (stylesheetProps.referrerPolicy == null)
stylesheetProps.referrerPolicy = preloadProps.referrerPolicy;
if (stylesheetProps.title == null) stylesheetProps.title = preloadProps.title;
}
function adoptPreloadPropsForScript(
scriptProps: ScriptProps,
preloadProps: PreloadProps | PreloadModuleProps,
): void {
if (scriptProps.crossOrigin == null)
scriptProps.crossOrigin = preloadProps.crossOrigin;
if (scriptProps.referrerPolicy == null)
scriptProps.referrerPolicy = preloadProps.referrerPolicy;
if (scriptProps.integrity == null)
scriptProps.integrity = preloadProps.integrity;
}
type KeyedTagCache = Map<string, Array<Element>>;
type DocumentTagCaches = Map<Document, KeyedTagCache>;
let tagCaches: null | DocumentTagCaches = null;
export function hydrateHoistable(
hoistableRoot: HoistableRoot,
type: HoistableTagType,
props: any,
internalInstanceHandle: Object,
): Instance {
const ownerDocument = getDocumentFromRoot(hoistableRoot);
let instance: ?Instance = null;
getInstance: switch (type) {
case 'title': {
instance = ownerDocument.getElementsByTagName('title')[0];
if (
!instance ||
isOwnedInstance(instance) ||
instance.namespaceURI === SVG_NAMESPACE ||
instance.hasAttribute('itemprop')
) {
instance = ownerDocument.createElement(type);
(ownerDocument.head: any).insertBefore(
instance,
ownerDocument.querySelector('head > title'),
);
}
setInitialProperties(instance, type, props);
precacheFiberNode(internalInstanceHandle, instance);
markNodeAsHoistable(instance);
return instance;
}
case 'link': {
const cache = getHydratableHoistableCache('link', 'href', ownerDocument);
const key = type + (props.href || '');
const maybeNodes = cache.get(key);
if (maybeNodes) {
const nodes = maybeNodes;
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (
node.getAttribute('href') !==
(props.href == null || props.href === '' ? null : props.href) ||
node.getAttribute('rel') !==
(props.rel == null ? null : props.rel) ||
node.getAttribute('title') !==
(props.title == null ? null : props.title) ||
node.getAttribute('crossorigin') !==
(props.crossOrigin == null ? null : props.crossOrigin)
) {
continue;
}
instance = node;
nodes.splice(i, 1);
break getInstance;
}
}
instance = ownerDocument.createElement(type);
setInitialProperties(instance, type, props);
(ownerDocument.head: any).appendChild(instance);
break;
}
case 'meta': {
const cache = getHydratableHoistableCache(
'meta',
'content',
ownerDocument,
);
const key = type + (props.content || '');
const maybeNodes = cache.get(key);
if (maybeNodes) {
const nodes = maybeNodes;
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (__DEV__) {
checkAttributeStringCoercion(props.content, 'content');
}
if (
node.getAttribute('content') !==
(props.content == null ? null : '' + props.content) ||
node.getAttribute('name') !==
(props.name == null ? null : props.name) ||
node.getAttribute('property') !==
(props.property == null ? null : props.property) ||
node.getAttribute('http-equiv') !==
(props.httpEquiv == null ? null : props.httpEquiv) ||
node.getAttribute('charset') !==
(props.charSet == null ? null : props.charSet)
) {
continue;
}
instance = node;
nodes.splice(i, 1);
break getInstance;
}
}
instance = ownerDocument.createElement(type);
setInitialProperties(instance, type, props);
(ownerDocument.head: any).appendChild(instance);
break;
}
default:
throw new Error(
`getNodesForType encountered a type it did not expect: "${type}". This is a bug in React.`,
);
}
precacheFiberNode(internalInstanceHandle, instance);
markNodeAsHoistable(instance);
return instance;
}
function getHydratableHoistableCache(
type: HoistableTagType,
keyAttribute: string,
ownerDocument: Document,
): KeyedTagCache {
let cache: KeyedTagCache;
let caches: DocumentTagCaches;
if (tagCaches === null) {
cache = new Map();
caches = tagCaches = new Map();
caches.set(ownerDocument, cache);
} else {
caches = tagCaches;
const maybeCache = caches.get(ownerDocument);
if (!maybeCache) {
cache = new Map();
caches.set(ownerDocument, cache);
} else {
cache = maybeCache;
}
}
if (cache.has(type)) {
return cache;
}
cache.set(type, (null: any));
const nodes = ownerDocument.getElementsByTagName(type);
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (
!isOwnedInstance(node) &&
(type !== 'link' || node.getAttribute('rel') !== 'stylesheet') &&
node.namespaceURI !== SVG_NAMESPACE
) {
const nodeKey = node.getAttribute(keyAttribute) || '';
const key = type + nodeKey;
const existing = cache.get(key);
if (existing) {
existing.push(node);
} else {
cache.set(key, [node]);
}
}
}
return cache;
}
export function mountHoistable(
hoistableRoot: HoistableRoot,
type: HoistableTagType,
instance: Instance,
): void {
const ownerDocument = getDocumentFromRoot(hoistableRoot);
(ownerDocument.head: any).insertBefore(
instance,
type === 'title' ? ownerDocument.querySelector('head > title') : null,
);
}
export function unmountHoistable(instance: Instance): void {
(instance.parentNode: any).removeChild(instance);
}
export function isHostHoistableType(
type: string,
props: RawProps,
hostContext: HostContext,
): boolean {
let outsideHostContainerContext: boolean;
let hostContextProd: HostContextProd;
if (__DEV__) {
const hostContextDev: HostContextDev = (hostContext: any);
outsideHostContainerContext =
!hostContextDev.ancestorInfo.containerTagInScope;
hostContextProd = hostContextDev.context;
} else {
hostContextProd = (hostContext: any);
}
if (hostContextProd === HostContextNamespaceSvg || props.itemProp != null) {
if (__DEV__) {
if (
outsideHostContainerContext &&
props.itemProp != null &&
(type === 'meta' ||
type === 'title' ||
type === 'style' ||
type === 'link' ||
type === 'script')
) {
console.error(
'Cannot render a <%s> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an' +
' `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <%s> remove the `itemProp` prop.' +
' Otherwise, try moving this tag into the <head> or <body> of the Document.',
type,
type,
);
}
}
return false;
}
switch (type) {
case 'meta':
case 'title': {
return true;
}
case 'style': {
if (
typeof props.precedence !== 'string' ||
typeof props.href !== 'string' ||
props.href === ''
) {
if (__DEV__) {
if (outsideHostContainerContext) {
console.error(
'Cannot render a <style> outside the main document without knowing its precedence and a unique href key.' +
' React can hoist and deduplicate <style> tags if you provide a `precedence` prop along with an `href` prop that' +
' does not conflict with the `href` values used in any other hoisted <style> or <link rel="stylesheet" ...> tags. ' +
' Note that hoisting <style> tags is considered an advanced feature that most will not use directly.' +
' Consider moving the <style> tag to the <head> or consider adding a `precedence="default"` and `href="some unique resource identifier"`.',
);
}
}
return false;
}
return true;
}
case 'link': {
if (
typeof props.rel !== 'string' ||
typeof props.href !== 'string' ||
props.href === '' ||
props.onLoad ||
props.onError
) {
if (__DEV__) {
if (
props.rel === 'stylesheet' &&
typeof props.precedence === 'string'
) {
validateLinkPropsForStyleResource(props);
}
if (outsideHostContainerContext) {
if (
typeof props.rel !== 'string' ||
typeof props.href !== 'string' ||
props.href === ''
) {
console.error(
'Cannot render a <link> outside the main document without a `rel` and `href` prop.' +
' Try adding a `rel` and/or `href` prop to this <link> or moving the link into the <head> tag',
);
} else if (props.onError || props.onLoad) {
console.error(
'Cannot render a <link> with onLoad or onError listeners outside the main document.' +
' Try removing onLoad={...} and onError={...} or moving it into the root <head> tag or' +
' somewhere in the <body>.',
);
}
}
}
return false;
}
switch (props.rel) {
case 'stylesheet': {
const {precedence, disabled} = props;
if (__DEV__) {
if (typeof precedence !== 'string') {
if (outsideHostContainerContext) {
console.error(
'Cannot render a <link rel="stylesheet" /> outside the main document without knowing its precedence.' +
' Consider adding precedence="default" or moving it into the root <head> tag.',
);
}
}
}
return typeof precedence === 'string' && disabled == null;
}
default: {
return true;
}
}
}
case 'script': {
const isAsync =
props.async &&
typeof props.async !== 'function' &&
typeof props.async !== 'symbol';
if (
!isAsync ||
props.onLoad ||
props.onError ||
!props.src ||
typeof props.src !== 'string'
) {
if (__DEV__) {
if (outsideHostContainerContext) {
if (!isAsync) {
console.error(
'Cannot render a sync or defer <script> outside the main document without knowing its order.' +
' Try adding async="" or moving it into the root <head> tag.',
);
} else if (props.onLoad || props.onError) {
console.error(
'Cannot render a <script> with onLoad or onError listeners outside the main document.' +
' Try removing onLoad={...} and onError={...} or moving it into the root <head> tag or' +
' somewhere in the <body>.',
);
} else {
console.error(
'Cannot render a <script> outside the main document without `async={true}` and a non-empty `src` prop.' +
' Ensure there is a valid `src` and either make the script async or move it into the root <head> tag or' +
' somewhere in the <body>.',
);
}
}
}
return false;
}
return true;
}
case 'noscript':
case 'template': {
if (__DEV__) {
if (outsideHostContainerContext) {
console.error(
'Cannot render <%s> outside the main document. Try moving it into the root <head> tag.',
type,
);
}
}
return false;
}
}
return false;
}
export function maySuspendCommit(type: Type, props: Props): boolean {
if (!enableSuspenseyImages && !enableViewTransition) {
return false;
}
return (
type === 'img' &&
props.src != null &&
props.src !== '' &&
props.onLoad == null &&
props.loading !== 'lazy'
);
}
export function maySuspendCommitOnUpdate(
type: Type,
oldProps: Props,
newProps: Props,
): boolean {
return (
maySuspendCommit(type, newProps) &&
(newProps.src !== oldProps.src || newProps.srcSet !== oldProps.srcSet)
);
}
export function maySuspendCommitInSyncRender(
type: Type,
props: Props,
): boolean {
return false;
}
export function mayResourceSuspendCommit(resource: Resource): boolean {
return (
resource.type === 'stylesheet' &&
(resource.state.loading & Inserted) === NotLoaded
);
}
export function preloadInstance(
instance: Instance,
type: Type,
props: Props,
): boolean {
return !!(instance: any).complete;
}
export function preloadResource(resource: Resource): boolean {
if (
resource.type === 'stylesheet' &&
(resource.state.loading & Settled) === NotLoaded
) {
return false;
}
return true;
}
type SuspendedState = {
stylesheets: null | Map<StylesheetResource, HoistableRoot>,
count: number,
unsuspend: null | (() => void),
};
let suspendedState: null | SuspendedState = null;
export function startSuspendingCommit(): void {
suspendedState = {
stylesheets: null,
count: 0,
unsuspend: noop,
};
}
const SUSPENSEY_IMAGE_TIMEOUT = 500;
export function suspendInstance(
instance: Instance,
type: Type,
props: Props,
): void {
if (!enableSuspenseyImages && !enableViewTransition) {
return;
}
if (suspendedState === null) {
throw new Error(
'Internal React Error: suspendedState null when it was expected to exists. Please report this as a React bug.',
);
}
const state = suspendedState;
if (
typeof instance.decode === 'function' &&
typeof setTimeout === 'function'
) {
state.count++;
const ping = onUnsuspend.bind(state);
Promise.race([
instance.decode(),
new Promise(resolve => setTimeout(resolve, SUSPENSEY_IMAGE_TIMEOUT)),
]).then(ping, ping);
}
}
export function suspendResource(
hoistableRoot: HoistableRoot,
resource: Resource,
props: any,
): void {
if (suspendedState === null) {
throw new Error(
'Internal React Error: suspendedState null when it was expected to exists. Please report this as a React bug.',
);
}
const state = suspendedState;
if (resource.type === 'stylesheet') {
if (typeof props.media === 'string') {
if (matchMedia(props.media).matches === false) {
return;
}
}
if ((resource.state.loading & Inserted) === NotLoaded) {
if (resource.instance === null) {
const qualifiedProps: StylesheetQualifyingProps = props;
const key = getStyleKey(qualifiedProps.href);
let instance: null | Instance = hoistableRoot.querySelector(
getStylesheetSelectorFromKey(key),
);
if (instance) {
const maybeLoadingState: ?Promise<mixed> = (instance: any)._p;
if (
maybeLoadingState !== null &&
typeof maybeLoadingState === 'object' &&
typeof maybeLoadingState.then === 'function'
) {
const loadingState = maybeLoadingState;
state.count++;
const ping = onUnsuspend.bind(state);
loadingState.then(ping, ping);
}
resource.state.loading |= Inserted;
resource.instance = instance;
markNodeAsHoistable(instance);
return;
}
const ownerDocument = getDocumentFromRoot(hoistableRoot);
const stylesheetProps = stylesheetPropsFromRawProps(props);
const preloadProps = preloadPropsMap.get(key);
if (preloadProps) {
adoptPreloadPropsForStylesheet(stylesheetProps, preloadProps);
}
instance = ownerDocument.createElement('link');
markNodeAsHoistable(instance);
const linkInstance: HTMLLinkElement = (instance: any);
(linkInstance: any)._p = new Promise((resolve, reject) => {
linkInstance.onload = resolve;
linkInstance.onerror = reject;
});
setInitialProperties(instance, 'link', stylesheetProps);
resource.instance = instance;
}
if (state.stylesheets === null) {
state.stylesheets = new Map();
}
state.stylesheets.set(resource, hoistableRoot);
const preloadEl = resource.state.preload;
if (preloadEl && (resource.state.loading & Settled) === NotLoaded) {
state.count++;
const ping = onUnsuspend.bind(state);
preloadEl.addEventListener('load', ping);
preloadEl.addEventListener('error', ping);
}
}
}
}
export function suspendOnActiveViewTransition(rootContainer: Container): void {
if (suspendedState === null) {
throw new Error(
'Internal React Error: suspendedState null when it was expected to exists. Please report this as a React bug.',
);
}
const state = suspendedState;
const ownerDocument =
rootContainer.nodeType === DOCUMENT_NODE
? rootContainer
: rootContainer.ownerDocument;
const activeViewTransition = ownerDocument.__reactViewTransition;
if (activeViewTransition == null) {
return;
}
state.count++;
const ping = onUnsuspend.bind(state);
activeViewTransition.finished.then(ping, ping);
}
export function waitForCommitToBeReady(): null | ((() => void) => () => void) {
if (suspendedState === null) {
throw new Error(
'Internal React Error: suspendedState null when it was expected to exists. Please report this as a React bug.',
);
}
const state = suspendedState;
if (state.stylesheets && state.count === 0) {
insertSuspendedStylesheets(state, state.stylesheets);
}
if (state.count > 0) {
return commit => {
const stylesheetTimer = setTimeout(() => {
if (state.stylesheets) {
insertSuspendedStylesheets(state, state.stylesheets);
}
if (state.unsuspend) {
const unsuspend = state.unsuspend;
state.unsuspend = null;
unsuspend();
}
}, 60000);
state.unsuspend = commit;
return () => {
state.unsuspend = null;
clearTimeout(stylesheetTimer);
};
};
}
return null;
}
function onUnsuspend(this: SuspendedState) {
this.count--;
if (this.count === 0) {
if (this.stylesheets) {
insertSuspendedStylesheets(this, this.stylesheets);
} else if (this.unsuspend) {
const unsuspend = this.unsuspend;
this.unsuspend = null;
unsuspend();
}
}
}
const LAST_PRECEDENCE = null;
let precedencesByRoot: Map<
HoistableRoot,
Map<string | typeof LAST_PRECEDENCE, Instance>,
> = (null: any);
function insertSuspendedStylesheets(
state: SuspendedState,
resources: Map<StylesheetResource, HoistableRoot>,
): void {
state.stylesheets = null;
if (state.unsuspend === null) {
return;
}
state.count++;
precedencesByRoot = new Map();
resources.forEach(insertStylesheetIntoRoot, state);
precedencesByRoot = (null: any);
onUnsuspend.call(state);
}
function insertStylesheetIntoRoot(
this: SuspendedState,
root: HoistableRoot,
resource: StylesheetResource,
map: Map<StylesheetResource, HoistableRoot>,
) {
if (resource.state.loading & Inserted) {
return;
}
let last;
let precedences = precedencesByRoot.get(root);
if (!precedences) {
precedences = new Map();
precedencesByRoot.set(root, precedences);
const nodes = root.querySelectorAll(
'link[data-precedence],style[data-precedence]',
);
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (
node.nodeName === 'LINK' ||
node.getAttribute('media') !== 'not all'
) {
precedences.set(node.dataset.precedence, node);
last = node;
}
}
if (last) {
precedences.set(LAST_PRECEDENCE, last);
}
} else {
last = precedences.get(LAST_PRECEDENCE);
}
const instance: HTMLLinkElement = (resource.instance: any);
const precedence: string = (instance.getAttribute('data-precedence'): any);
const prior = precedences.get(precedence) || last;
if (prior === last) {
precedences.set(LAST_PRECEDENCE, instance);
}
precedences.set(precedence, instance);
this.count++;
const onComplete = onUnsuspend.bind(this);
instance.addEventListener('load', onComplete);
instance.addEventListener('error', onComplete);
if (prior) {
(prior.parentNode: any).insertBefore(instance, prior.nextSibling);
} else {
const parent =
root.nodeType === DOCUMENT_NODE
? ((((root: any): Document).head: any): Element)
: ((root: any): ShadowRoot);
parent.insertBefore(instance, parent.firstChild);
}
resource.state.loading |= Inserted;
}
export const NotPendingTransition: TransitionStatus = NotPending;
export const HostTransitionContext: ReactContext<TransitionStatus> = {
$$typeof: REACT_CONTEXT_TYPE,
Provider: (null: any),
Consumer: (null: any),
_currentValue: NotPendingTransition,
_currentValue2: NotPendingTransition,
_threadCount: 0,
};
export type FormInstance = HTMLFormElement;
export function resetFormInstance(form: FormInstance): void {
form.reset();
}