1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import type {HostDispatcher} from 'react-dom/src/shared/ReactDOMTypes';
    
  11. import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
    
  12. import type {DOMEventName} from '../events/DOMEventNames';
    
  13. import type {Fiber, FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
    
  14. import type {
    
  15.   BoundingRect,
    
  16.   IntersectionObserverOptions,
    
  17.   ObserveVisibleRectsCallback,
    
  18. } from 'react-reconciler/src/ReactTestSelectors';
    
  19. import type {ReactScopeInstance} from 'shared/ReactTypes';
    
  20. import type {AncestorInfoDev} from './validateDOMNesting';
    
  21. import type {FormStatus} from 'react-dom-bindings/src/shared/ReactDOMFormActions';
    
  22. import type {
    
  23.   CrossOriginEnum,
    
  24.   PreloadImplOptions,
    
  25.   PreloadModuleImplOptions,
    
  26.   PreinitStyleOptions,
    
  27.   PreinitScriptOptions,
    
  28.   PreinitModuleScriptOptions,
    
  29. } from 'react-dom/src/shared/ReactDOMTypes';
    
  30. 
    
  31. import {NotPending} from 'react-dom-bindings/src/shared/ReactDOMFormActions';
    
  32. import {getCurrentRootHostContainer} from 'react-reconciler/src/ReactFiberHostContext';
    
  33. import {DefaultEventPriority} from 'react-reconciler/src/ReactEventPriorities';
    
  34. // TODO: Remove this deep import when we delete the legacy root API
    
  35. import {ConcurrentMode, NoMode} from 'react-reconciler/src/ReactTypeOfMode';
    
  36. 
    
  37. import hasOwnProperty from 'shared/hasOwnProperty';
    
  38. import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion';
    
  39. 
    
  40. import {
    
  41.   precacheFiberNode,
    
  42.   updateFiberProps,
    
  43.   getClosestInstanceFromNode,
    
  44.   getFiberFromScopeInstance,
    
  45.   getInstanceFromNode as getInstanceFromNodeDOMTree,
    
  46.   isContainerMarkedAsRoot,
    
  47.   detachDeletedInstance,
    
  48.   getResourcesFromRoot,
    
  49.   isMarkedHoistable,
    
  50.   markNodeAsHoistable,
    
  51.   isOwnedInstance,
    
  52. } from './ReactDOMComponentTree';
    
  53. export {detachDeletedInstance};
    
  54. import {hasRole} from './DOMAccessibilityRoles';
    
  55. import {
    
  56.   setInitialProperties,
    
  57.   updateProperties,
    
  58.   diffHydratedProperties,
    
  59.   diffHydratedText,
    
  60.   trapClickOnNonInteractiveElement,
    
  61.   checkForUnmatchedText,
    
  62.   warnForDeletedHydratableElement,
    
  63.   warnForDeletedHydratableText,
    
  64.   warnForInsertedHydratedElement,
    
  65.   warnForInsertedHydratedText,
    
  66. } from './ReactDOMComponent';
    
  67. import {getSelectionInformation, restoreSelection} from './ReactInputSelection';
    
  68. import setTextContent from './setTextContent';
    
  69. import {
    
  70.   validateDOMNesting,
    
  71.   validateTextNesting,
    
  72.   updatedAncestorInfoDev,
    
  73. } from './validateDOMNesting';
    
  74. import {
    
  75.   isEnabled as ReactBrowserEventEmitterIsEnabled,
    
  76.   setEnabled as ReactBrowserEventEmitterSetEnabled,
    
  77.   getEventPriority,
    
  78. } from '../events/ReactDOMEventListener';
    
  79. import {SVG_NAMESPACE, MATH_NAMESPACE} from './DOMNamespaces';
    
  80. import {
    
  81.   ELEMENT_NODE,
    
  82.   TEXT_NODE,
    
  83.   COMMENT_NODE,
    
  84.   DOCUMENT_NODE,
    
  85.   DOCUMENT_TYPE_NODE,
    
  86.   DOCUMENT_FRAGMENT_NODE,
    
  87. } from './HTMLNodeType';
    
  88. 
    
  89. import {retryIfBlockedOn} from '../events/ReactDOMEventReplaying';
    
  90. 
    
  91. import {
    
  92.   enableCreateEventHandleAPI,
    
  93.   enableScopeAPI,
    
  94.   enableFloat,
    
  95.   enableHostSingletons,
    
  96.   enableTrustedTypesIntegration,
    
  97.   enableFormActions,
    
  98.   enableAsyncActions,
    
  99. } from 'shared/ReactFeatureFlags';
    
  100. import {
    
  101.   HostComponent,
    
  102.   HostHoistable,
    
  103.   HostText,
    
  104.   HostSingleton,
    
  105. } from 'react-reconciler/src/ReactWorkTags';
    
  106. import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';
    
  107. import {validateLinkPropsForStyleResource} from '../shared/ReactDOMResourceValidation';
    
  108. import escapeSelectorAttributeValueInsideDoubleQuotes from './escapeSelectorAttributeValueInsideDoubleQuotes';
    
  109. 
    
  110. export type Type = string;
    
  111. export type Props = {
    
  112.   autoFocus?: boolean,
    
  113.   children?: mixed,
    
  114.   disabled?: boolean,
    
  115.   hidden?: boolean,
    
  116.   suppressHydrationWarning?: boolean,
    
  117.   dangerouslySetInnerHTML?: mixed,
    
  118.   style?: {display?: string, ...},
    
  119.   bottom?: null | number,
    
  120.   left?: null | number,
    
  121.   right?: null | number,
    
  122.   top?: null | number,
    
  123.   is?: string,
    
  124.   size?: number,
    
  125.   multiple?: boolean,
    
  126.   ...
    
  127. };
    
  128. type RawProps = {
    
  129.   [string]: mixed,
    
  130. };
    
  131. export type EventTargetChildElement = {
    
  132.   type: string,
    
  133.   props: null | {
    
  134.     style?: {
    
  135.       position?: string,
    
  136.       zIndex?: number,
    
  137.       bottom?: string,
    
  138.       left?: string,
    
  139.       right?: string,
    
  140.       top?: string,
    
  141.       ...
    
  142.     },
    
  143.     ...
    
  144.   },
    
  145.   ...
    
  146. };
    
  147. export type Container =
    
  148.   | interface extends Element {_reactRootContainer?: FiberRoot}
    
  149.   | interface extends Document {_reactRootContainer?: FiberRoot}
    
  150.   | interface extends DocumentFragment {_reactRootContainer?: FiberRoot};
    
  151. export type Instance = Element;
    
  152. export type TextInstance = Text;
    
  153. export interface SuspenseInstance extends Comment {
    
  154.   _reactRetry?: () => void;
    
  155. }
    
  156. type FormStateMarkerInstance = Comment;
    
  157. export type HydratableInstance =
    
  158.   | Instance
    
  159.   | TextInstance
    
  160.   | SuspenseInstance
    
  161.   | FormStateMarkerInstance;
    
  162. export type PublicInstance = Element | Text;
    
  163. export type HostContextDev = {
    
  164.   context: HostContextProd,
    
  165.   ancestorInfo: AncestorInfoDev,
    
  166. };
    
  167. type HostContextProd = HostContextNamespace;
    
  168. export type HostContext = HostContextDev | HostContextProd;
    
  169. export type UpdatePayload = Array<mixed>;
    
  170. export type ChildSet = void; // Unused
    
  171. export type TimeoutHandle = TimeoutID;
    
  172. export type NoTimeout = -1;
    
  173. export type RendererInspectionConfig = $ReadOnly<{}>;
    
  174. 
    
  175. export type TransitionStatus = FormStatus;
    
  176. 
    
  177. type SelectionInformation = {
    
  178.   focusedElem: null | HTMLElement,
    
  179.   selectionRange: mixed,
    
  180. };
    
  181. 
    
  182. const SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
    
  183. 
    
  184. const SUSPENSE_START_DATA = '$';
    
  185. const SUSPENSE_END_DATA = '/$';
    
  186. const SUSPENSE_PENDING_START_DATA = '$?';
    
  187. const SUSPENSE_FALLBACK_START_DATA = '$!';
    
  188. const FORM_STATE_IS_MATCHING = 'F!';
    
  189. const FORM_STATE_IS_NOT_MATCHING = 'F';
    
  190. 
    
  191. const STYLE = 'style';
    
  192. 
    
  193. opaque type HostContextNamespace = 0 | 1 | 2;
    
  194. export const HostContextNamespaceNone: HostContextNamespace = 0;
    
  195. const HostContextNamespaceSvg: HostContextNamespace = 1;
    
  196. const HostContextNamespaceMath: HostContextNamespace = 2;
    
  197. 
    
  198. let eventsEnabled: ?boolean = null;
    
  199. let selectionInformation: null | SelectionInformation = null;
    
  200. 
    
  201. export * from 'react-reconciler/src/ReactFiberConfigWithNoPersistence';
    
  202. 
    
  203. function getOwnerDocumentFromRootContainer(
    
  204.   rootContainerElement: Element | Document | DocumentFragment,
    
  205. ): Document {
    
  206.   return rootContainerElement.nodeType === DOCUMENT_NODE
    
  207.     ? (rootContainerElement: any)
    
  208.     : rootContainerElement.ownerDocument;
    
  209. }
    
  210. 
    
  211. export function getRootHostContext(
    
  212.   rootContainerInstance: Container,
    
  213. ): HostContext {
    
  214.   let type;
    
  215.   let context: HostContextProd;
    
  216.   const nodeType = rootContainerInstance.nodeType;
    
  217.   switch (nodeType) {
    
  218.     case DOCUMENT_NODE:
    
  219.     case DOCUMENT_FRAGMENT_NODE: {
    
  220.       type = nodeType === DOCUMENT_NODE ? '#document' : '#fragment';
    
  221.       const root = (rootContainerInstance: any).documentElement;
    
  222.       if (root) {
    
  223.         const namespaceURI = root.namespaceURI;
    
  224.         context = namespaceURI
    
  225.           ? getOwnHostContext(namespaceURI)
    
  226.           : HostContextNamespaceNone;
    
  227.       } else {
    
  228.         context = HostContextNamespaceNone;
    
  229.       }
    
  230.       break;
    
  231.     }
    
  232.     default: {
    
  233.       const container: any =
    
  234.         nodeType === COMMENT_NODE
    
  235.           ? rootContainerInstance.parentNode
    
  236.           : rootContainerInstance;
    
  237.       type = container.tagName;
    
  238.       const namespaceURI = container.namespaceURI;
    
  239.       if (!namespaceURI) {
    
  240.         switch (type) {
    
  241.           case 'svg':
    
  242.             context = HostContextNamespaceSvg;
    
  243.             break;
    
  244.           case 'math':
    
  245.             context = HostContextNamespaceMath;
    
  246.             break;
    
  247.           default:
    
  248.             context = HostContextNamespaceNone;
    
  249.             break;
    
  250.         }
    
  251.       } else {
    
  252.         const ownContext = getOwnHostContext(namespaceURI);
    
  253.         context = getChildHostContextProd(ownContext, type);
    
  254.       }
    
  255.       break;
    
  256.     }
    
  257.   }
    
  258.   if (__DEV__) {
    
  259.     const validatedTag = type.toLowerCase();
    
  260.     const ancestorInfo = updatedAncestorInfoDev(null, validatedTag);
    
  261.     return {context, ancestorInfo};
    
  262.   }
    
  263.   return context;
    
  264. }
    
  265. 
    
  266. function getOwnHostContext(namespaceURI: string): HostContextNamespace {
    
  267.   switch (namespaceURI) {
    
  268.     case SVG_NAMESPACE:
    
  269.       return HostContextNamespaceSvg;
    
  270.     case MATH_NAMESPACE:
    
  271.       return HostContextNamespaceMath;
    
  272.     default:
    
  273.       return HostContextNamespaceNone;
    
  274.   }
    
  275. }
    
  276. 
    
  277. function getChildHostContextProd(
    
  278.   parentNamespace: HostContextNamespace,
    
  279.   type: string,
    
  280. ): HostContextNamespace {
    
  281.   if (parentNamespace === HostContextNamespaceNone) {
    
  282.     // No (or default) parent namespace: potential entry point.
    
  283.     switch (type) {
    
  284.       case 'svg':
    
  285.         return HostContextNamespaceSvg;
    
  286.       case 'math':
    
  287.         return HostContextNamespaceMath;
    
  288.       default:
    
  289.         return HostContextNamespaceNone;
    
  290.     }
    
  291.   }
    
  292.   if (parentNamespace === HostContextNamespaceSvg && type === 'foreignObject') {
    
  293.     // We're leaving SVG.
    
  294.     return HostContextNamespaceNone;
    
  295.   }
    
  296.   // By default, pass namespace below.
    
  297.   return parentNamespace;
    
  298. }
    
  299. 
    
  300. export function getChildHostContext(
    
  301.   parentHostContext: HostContext,
    
  302.   type: string,
    
  303. ): HostContext {
    
  304.   if (__DEV__) {
    
  305.     const parentHostContextDev = ((parentHostContext: any): HostContextDev);
    
  306.     const context = getChildHostContextProd(parentHostContextDev.context, type);
    
  307.     const ancestorInfo = updatedAncestorInfoDev(
    
  308.       parentHostContextDev.ancestorInfo,
    
  309.       type,
    
  310.     );
    
  311.     return {context, ancestorInfo};
    
  312.   }
    
  313.   const parentNamespace = ((parentHostContext: any): HostContextProd);
    
  314.   return getChildHostContextProd(parentNamespace, type);
    
  315. }
    
  316. 
    
  317. export function getPublicInstance(instance: Instance): Instance {
    
  318.   return instance;
    
  319. }
    
  320. 
    
  321. export function prepareForCommit(containerInfo: Container): Object | null {
    
  322.   eventsEnabled = ReactBrowserEventEmitterIsEnabled();
    
  323.   selectionInformation = getSelectionInformation();
    
  324.   let activeInstance = null;
    
  325.   if (enableCreateEventHandleAPI) {
    
  326.     const focusedElem = selectionInformation.focusedElem;
    
  327.     if (focusedElem !== null) {
    
  328.       activeInstance = getClosestInstanceFromNode(focusedElem);
    
  329.     }
    
  330.   }
    
  331.   ReactBrowserEventEmitterSetEnabled(false);
    
  332.   return activeInstance;
    
  333. }
    
  334. 
    
  335. export function beforeActiveInstanceBlur(internalInstanceHandle: Object): void {
    
  336.   if (enableCreateEventHandleAPI) {
    
  337.     ReactBrowserEventEmitterSetEnabled(true);
    
  338.     dispatchBeforeDetachedBlur(
    
  339.       (selectionInformation: any).focusedElem,
    
  340.       internalInstanceHandle,
    
  341.     );
    
  342.     ReactBrowserEventEmitterSetEnabled(false);
    
  343.   }
    
  344. }
    
  345. 
    
  346. export function afterActiveInstanceBlur(): void {
    
  347.   if (enableCreateEventHandleAPI) {
    
  348.     ReactBrowserEventEmitterSetEnabled(true);
    
  349.     dispatchAfterDetachedBlur((selectionInformation: any).focusedElem);
    
  350.     ReactBrowserEventEmitterSetEnabled(false);
    
  351.   }
    
  352. }
    
  353. 
    
  354. export function resetAfterCommit(containerInfo: Container): void {
    
  355.   restoreSelection(selectionInformation);
    
  356.   ReactBrowserEventEmitterSetEnabled(eventsEnabled);
    
  357.   eventsEnabled = null;
    
  358.   selectionInformation = null;
    
  359. }
    
  360. 
    
  361. export function createHoistableInstance(
    
  362.   type: string,
    
  363.   props: Props,
    
  364.   rootContainerInstance: Container,
    
  365.   internalInstanceHandle: Object,
    
  366. ): Instance {
    
  367.   const ownerDocument = getOwnerDocumentFromRootContainer(
    
  368.     rootContainerInstance,
    
  369.   );
    
  370. 
    
  371.   const domElement: Instance = ownerDocument.createElement(type);
    
  372.   precacheFiberNode(internalInstanceHandle, domElement);
    
  373.   updateFiberProps(domElement, props);
    
  374.   setInitialProperties(domElement, type, props);
    
  375.   markNodeAsHoistable(domElement);
    
  376.   return domElement;
    
  377. }
    
  378. 
    
  379. let didWarnScriptTags = false;
    
  380. const warnedUnknownTags: {
    
  381.   [key: string]: boolean,
    
  382. } = {
    
  383.   // There are working polyfills for <dialog>. Let people use it.
    
  384.   dialog: true,
    
  385.   // Electron ships a custom <webview> tag to display external web content in
    
  386.   // an isolated frame and process.
    
  387.   // This tag is not present in non Electron environments such as JSDom which
    
  388.   // is often used for testing purposes.
    
  389.   // @see https://electronjs.org/docs/api/webview-tag
    
  390.   webview: true,
    
  391. };
    
  392. 
    
  393. export function createInstance(
    
  394.   type: string,
    
  395.   props: Props,
    
  396.   rootContainerInstance: Container,
    
  397.   hostContext: HostContext,
    
  398.   internalInstanceHandle: Object,
    
  399. ): Instance {
    
  400.   let hostContextProd: HostContextProd;
    
  401.   if (__DEV__) {
    
  402.     // TODO: take namespace into account when validating.
    
  403.     const hostContextDev: HostContextDev = (hostContext: any);
    
  404.     validateDOMNesting(type, hostContextDev.ancestorInfo);
    
  405.     hostContextProd = hostContextDev.context;
    
  406.   } else {
    
  407.     hostContextProd = (hostContext: any);
    
  408.   }
    
  409. 
    
  410.   const ownerDocument = getOwnerDocumentFromRootContainer(
    
  411.     rootContainerInstance,
    
  412.   );
    
  413. 
    
  414.   let domElement: Instance;
    
  415.   switch (hostContextProd) {
    
  416.     case HostContextNamespaceSvg:
    
  417.       domElement = ownerDocument.createElementNS(SVG_NAMESPACE, type);
    
  418.       break;
    
  419.     case HostContextNamespaceMath:
    
  420.       domElement = ownerDocument.createElementNS(MATH_NAMESPACE, type);
    
  421.       break;
    
  422.     default:
    
  423.       switch (type) {
    
  424.         case 'svg': {
    
  425.           domElement = ownerDocument.createElementNS(SVG_NAMESPACE, type);
    
  426.           break;
    
  427.         }
    
  428.         case 'math': {
    
  429.           domElement = ownerDocument.createElementNS(MATH_NAMESPACE, type);
    
  430.           break;
    
  431.         }
    
  432.         case 'script': {
    
  433.           // Create the script via .innerHTML so its "parser-inserted" flag is
    
  434.           // set to true and it does not execute
    
  435.           const div = ownerDocument.createElement('div');
    
  436.           if (__DEV__) {
    
  437.             if (enableTrustedTypesIntegration && !didWarnScriptTags) {
    
  438.               console.error(
    
  439.                 'Encountered a script tag while rendering React component. ' +
    
  440.                   'Scripts inside React components are never executed when rendering ' +
    
  441.                   'on the client. Consider using template tag instead ' +
    
  442.                   '(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).',
    
  443.               );
    
  444.               didWarnScriptTags = true;
    
  445.             }
    
  446.           }
    
  447.           div.innerHTML = '<script><' + '/script>'; // eslint-disable-line
    
  448.           // This is guaranteed to yield a script element.
    
  449.           const firstChild = ((div.firstChild: any): HTMLScriptElement);
    
  450.           domElement = div.removeChild(firstChild);
    
  451.           break;
    
  452.         }
    
  453.         case 'select': {
    
  454.           if (typeof props.is === 'string') {
    
  455.             domElement = ownerDocument.createElement('select', {is: props.is});
    
  456.           } else {
    
  457.             // Separate else branch instead of using `props.is || undefined` above because of a Firefox bug.
    
  458.             // See discussion in https://github.com/facebook/react/pull/6896
    
  459.             // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240
    
  460.             domElement = ownerDocument.createElement('select');
    
  461.           }
    
  462.           if (props.multiple) {
    
  463.             domElement.multiple = true;
    
  464.           } else if (props.size) {
    
  465.             // Setting a size greater than 1 causes a select to behave like `multiple=true`, where
    
  466.             // it is possible that no option is selected.
    
  467.             //
    
  468.             // This is only necessary when a select in "single selection mode".
    
  469.             domElement.size = props.size;
    
  470.           }
    
  471.           break;
    
  472.         }
    
  473.         default: {
    
  474.           if (typeof props.is === 'string') {
    
  475.             domElement = ownerDocument.createElement(type, {is: props.is});
    
  476.           } else {
    
  477.             // Separate else branch instead of using `props.is || undefined` above because of a Firefox bug.
    
  478.             // See discussion in https://github.com/facebook/react/pull/6896
    
  479.             // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240
    
  480.             domElement = ownerDocument.createElement(type);
    
  481.           }
    
  482. 
    
  483.           if (__DEV__) {
    
  484.             if (type.indexOf('-') === -1) {
    
  485.               // We're not SVG/MathML and we don't have a dash, so we're not a custom element
    
  486.               // Even if you use `is`, these should be of known type and lower case.
    
  487.               if (type !== type.toLowerCase()) {
    
  488.                 console.error(
    
  489.                   '<%s /> is using incorrect casing. ' +
    
  490.                     'Use PascalCase for React components, ' +
    
  491.                     'or lowercase for HTML elements.',
    
  492.                   type,
    
  493.                 );
    
  494.               }
    
  495.               if (
    
  496.                 // $FlowFixMe[method-unbinding]
    
  497.                 Object.prototype.toString.call(domElement) ===
    
  498.                   '[object HTMLUnknownElement]' &&
    
  499.                 !hasOwnProperty.call(warnedUnknownTags, type)
    
  500.               ) {
    
  501.                 warnedUnknownTags[type] = true;
    
  502.                 console.error(
    
  503.                   'The tag <%s> is unrecognized in this browser. ' +
    
  504.                     'If you meant to render a React component, start its name with ' +
    
  505.                     'an uppercase letter.',
    
  506.                   type,
    
  507.                 );
    
  508.               }
    
  509.             }
    
  510.           }
    
  511.         }
    
  512.       }
    
  513.   }
    
  514.   precacheFiberNode(internalInstanceHandle, domElement);
    
  515.   updateFiberProps(domElement, props);
    
  516.   return domElement;
    
  517. }
    
  518. 
    
  519. export function appendInitialChild(
    
  520.   parentInstance: Instance,
    
  521.   child: Instance | TextInstance,
    
  522. ): void {
    
  523.   parentInstance.appendChild(child);
    
  524. }
    
  525. 
    
  526. export function finalizeInitialChildren(
    
  527.   domElement: Instance,
    
  528.   type: string,
    
  529.   props: Props,
    
  530.   hostContext: HostContext,
    
  531. ): boolean {
    
  532.   setInitialProperties(domElement, type, props);
    
  533.   switch (type) {
    
  534.     case 'button':
    
  535.     case 'input':
    
  536.     case 'select':
    
  537.     case 'textarea':
    
  538.       return !!props.autoFocus;
    
  539.     case 'img':
    
  540.       return true;
    
  541.     default:
    
  542.       return false;
    
  543.   }
    
  544. }
    
  545. 
    
  546. export function shouldSetTextContent(type: string, props: Props): boolean {
    
  547.   return (
    
  548.     type === 'textarea' ||
    
  549.     type === 'noscript' ||
    
  550.     typeof props.children === 'string' ||
    
  551.     typeof props.children === 'number' ||
    
  552.     (typeof props.dangerouslySetInnerHTML === 'object' &&
    
  553.       props.dangerouslySetInnerHTML !== null &&
    
  554.       props.dangerouslySetInnerHTML.__html != null)
    
  555.   );
    
  556. }
    
  557. 
    
  558. export function createTextInstance(
    
  559.   text: string,
    
  560.   rootContainerInstance: Container,
    
  561.   hostContext: HostContext,
    
  562.   internalInstanceHandle: Object,
    
  563. ): TextInstance {
    
  564.   if (__DEV__) {
    
  565.     const hostContextDev = ((hostContext: any): HostContextDev);
    
  566.     const ancestor = hostContextDev.ancestorInfo.current;
    
  567.     if (ancestor != null) {
    
  568.       validateTextNesting(text, ancestor.tag);
    
  569.     }
    
  570.   }
    
  571.   const textNode: TextInstance = getOwnerDocumentFromRootContainer(
    
  572.     rootContainerInstance,
    
  573.   ).createTextNode(text);
    
  574.   precacheFiberNode(internalInstanceHandle, textNode);
    
  575.   return textNode;
    
  576. }
    
  577. 
    
  578. export function getCurrentEventPriority(): EventPriority {
    
  579.   const currentEvent = window.event;
    
  580.   if (currentEvent === undefined) {
    
  581.     return DefaultEventPriority;
    
  582.   }
    
  583.   return getEventPriority(currentEvent.type);
    
  584. }
    
  585. 
    
  586. let currentPopstateTransitionEvent: Event | null = null;
    
  587. export function shouldAttemptEagerTransition(): boolean {
    
  588.   const event = window.event;
    
  589.   if (event && event.type === 'popstate') {
    
  590.     // This is a popstate event. Attempt to render any transition during this
    
  591.     // event synchronously. Unless we already attempted during this event.
    
  592.     if (event === currentPopstateTransitionEvent) {
    
  593.       // We already attempted to render this popstate transition synchronously.
    
  594.       // Any subsequent attempts must have happened as the result of a derived
    
  595.       // update, like startTransition inside useEffect, or useDV. Switch back to
    
  596.       // the default behavior for all remaining transitions during the current
    
  597.       // popstate event.
    
  598.       return false;
    
  599.     } else {
    
  600.       // Cache the current event in case a derived transition is scheduled.
    
  601.       // (Refer to previous branch.)
    
  602.       currentPopstateTransitionEvent = event;
    
  603.       return true;
    
  604.     }
    
  605.   }
    
  606.   // We're not inside a popstate event.
    
  607.   currentPopstateTransitionEvent = null;
    
  608.   return false;
    
  609. }
    
  610. 
    
  611. export const isPrimaryRenderer = true;
    
  612. export const warnsIfNotActing = true;
    
  613. // This initialization code may run even on server environments
    
  614. // if a component just imports ReactDOM (e.g. for findDOMNode).
    
  615. // Some environments might not have setTimeout or clearTimeout.
    
  616. export const scheduleTimeout: any =
    
  617.   typeof setTimeout === 'function' ? setTimeout : (undefined: any);
    
  618. export const cancelTimeout: any =
    
  619.   typeof clearTimeout === 'function' ? clearTimeout : (undefined: any);
    
  620. export const noTimeout = -1;
    
  621. const localPromise = typeof Promise === 'function' ? Promise : undefined;
    
  622. const localRequestAnimationFrame =
    
  623.   typeof requestAnimationFrame === 'function'
    
  624.     ? requestAnimationFrame
    
  625.     : scheduleTimeout;
    
  626. 
    
  627. export function getInstanceFromNode(node: HTMLElement): null | Object {
    
  628.   return getClosestInstanceFromNode(node) || null;
    
  629. }
    
  630. 
    
  631. export function preparePortalMount(portalInstance: Instance): void {
    
  632.   listenToAllSupportedEvents(portalInstance);
    
  633. }
    
  634. 
    
  635. export function prepareScopeUpdate(
    
  636.   scopeInstance: ReactScopeInstance,
    
  637.   internalInstanceHandle: Object,
    
  638. ): void {
    
  639.   if (enableScopeAPI) {
    
  640.     precacheFiberNode(internalInstanceHandle, scopeInstance);
    
  641.   }
    
  642. }
    
  643. 
    
  644. export function getInstanceFromScope(
    
  645.   scopeInstance: ReactScopeInstance,
    
  646. ): null | Object {
    
  647.   if (enableScopeAPI) {
    
  648.     return getFiberFromScopeInstance(scopeInstance);
    
  649.   }
    
  650.   return null;
    
  651. }
    
  652. 
    
  653. // -------------------
    
  654. //     Microtasks
    
  655. // -------------------
    
  656. export const supportsMicrotasks = true;
    
  657. export const scheduleMicrotask: any =
    
  658.   typeof queueMicrotask === 'function'
    
  659.     ? queueMicrotask
    
  660.     : typeof localPromise !== 'undefined'
    
  661.     ? callback =>
    
  662.         localPromise.resolve(null).then(callback).catch(handleErrorInNextTick)
    
  663.     : scheduleTimeout; // TODO: Determine the best fallback here.
    
  664. 
    
  665. function handleErrorInNextTick(error: any) {
    
  666.   setTimeout(() => {
    
  667.     throw error;
    
  668.   });
    
  669. }
    
  670. 
    
  671. // -------------------
    
  672. //     Mutation
    
  673. // -------------------
    
  674. 
    
  675. export const supportsMutation = true;
    
  676. 
    
  677. export function commitMount(
    
  678.   domElement: Instance,
    
  679.   type: string,
    
  680.   newProps: Props,
    
  681.   internalInstanceHandle: Object,
    
  682. ): void {
    
  683.   // Despite the naming that might imply otherwise, this method only
    
  684.   // fires if there is an `Update` effect scheduled during mounting.
    
  685.   // This happens if `finalizeInitialChildren` returns `true` (which it
    
  686.   // does to implement the `autoFocus` attribute on the client). But
    
  687.   // there are also other cases when this might happen (such as patching
    
  688.   // up text content during hydration mismatch). So we'll check this again.
    
  689.   switch (type) {
    
  690.     case 'button':
    
  691.     case 'input':
    
  692.     case 'select':
    
  693.     case 'textarea':
    
  694.       if (newProps.autoFocus) {
    
  695.         ((domElement: any):
    
  696.           | HTMLButtonElement
    
  697.           | HTMLInputElement
    
  698.           | HTMLSelectElement
    
  699.           | HTMLTextAreaElement).focus();
    
  700.       }
    
  701.       return;
    
  702.     case 'img': {
    
  703.       if ((newProps: any).src) {
    
  704.         ((domElement: any): HTMLImageElement).src = (newProps: any).src;
    
  705.       }
    
  706.       return;
    
  707.     }
    
  708.   }
    
  709. }
    
  710. 
    
  711. export function commitUpdate(
    
  712.   domElement: Instance,
    
  713.   updatePayload: any,
    
  714.   type: string,
    
  715.   oldProps: Props,
    
  716.   newProps: Props,
    
  717.   internalInstanceHandle: Object,
    
  718. ): void {
    
  719.   // Diff and update the properties.
    
  720.   updateProperties(domElement, type, oldProps, newProps);
    
  721. 
    
  722.   // Update the props handle so that we know which props are the ones with
    
  723.   // with current event handlers.
    
  724.   updateFiberProps(domElement, newProps);
    
  725. }
    
  726. 
    
  727. export function resetTextContent(domElement: Instance): void {
    
  728.   setTextContent(domElement, '');
    
  729. }
    
  730. 
    
  731. export function commitTextUpdate(
    
  732.   textInstance: TextInstance,
    
  733.   oldText: string,
    
  734.   newText: string,
    
  735. ): void {
    
  736.   textInstance.nodeValue = newText;
    
  737. }
    
  738. 
    
  739. export function appendChild(
    
  740.   parentInstance: Instance,
    
  741.   child: Instance | TextInstance,
    
  742. ): void {
    
  743.   parentInstance.appendChild(child);
    
  744. }
    
  745. 
    
  746. export function appendChildToContainer(
    
  747.   container: Container,
    
  748.   child: Instance | TextInstance,
    
  749. ): void {
    
  750.   let parentNode;
    
  751.   if (container.nodeType === COMMENT_NODE) {
    
  752.     parentNode = (container.parentNode: any);
    
  753.     parentNode.insertBefore(child, container);
    
  754.   } else {
    
  755.     parentNode = container;
    
  756.     parentNode.appendChild(child);
    
  757.   }
    
  758.   // This container might be used for a portal.
    
  759.   // If something inside a portal is clicked, that click should bubble
    
  760.   // through the React tree. However, on Mobile Safari the click would
    
  761.   // never bubble through the *DOM* tree unless an ancestor with onclick
    
  762.   // event exists. So we wouldn't see it and dispatch it.
    
  763.   // This is why we ensure that non React root containers have inline onclick
    
  764.   // defined.
    
  765.   // https://github.com/facebook/react/issues/11918
    
  766.   const reactRootContainer = container._reactRootContainer;
    
  767.   if (
    
  768.     (reactRootContainer === null || reactRootContainer === undefined) &&
    
  769.     parentNode.onclick === null
    
  770.   ) {
    
  771.     // TODO: This cast may not be sound for SVG, MathML or custom elements.
    
  772.     trapClickOnNonInteractiveElement(((parentNode: any): HTMLElement));
    
  773.   }
    
  774. }
    
  775. 
    
  776. export function insertBefore(
    
  777.   parentInstance: Instance,
    
  778.   child: Instance | TextInstance,
    
  779.   beforeChild: Instance | TextInstance | SuspenseInstance,
    
  780. ): void {
    
  781.   parentInstance.insertBefore(child, beforeChild);
    
  782. }
    
  783. 
    
  784. export function insertInContainerBefore(
    
  785.   container: Container,
    
  786.   child: Instance | TextInstance,
    
  787.   beforeChild: Instance | TextInstance | SuspenseInstance,
    
  788. ): void {
    
  789.   if (container.nodeType === COMMENT_NODE) {
    
  790.     (container.parentNode: any).insertBefore(child, beforeChild);
    
  791.   } else {
    
  792.     container.insertBefore(child, beforeChild);
    
  793.   }
    
  794. }
    
  795. 
    
  796. function createEvent(type: DOMEventName, bubbles: boolean): Event {
    
  797.   const event = document.createEvent('Event');
    
  798.   event.initEvent(((type: any): string), bubbles, false);
    
  799.   return event;
    
  800. }
    
  801. 
    
  802. function dispatchBeforeDetachedBlur(
    
  803.   target: HTMLElement,
    
  804.   internalInstanceHandle: Object,
    
  805. ): void {
    
  806.   if (enableCreateEventHandleAPI) {
    
  807.     const event = createEvent('beforeblur', true);
    
  808.     // Dispatch "beforeblur" directly on the target,
    
  809.     // so it gets picked up by the event system and
    
  810.     // can propagate through the React internal tree.
    
  811.     // $FlowFixMe[prop-missing]: internal field
    
  812.     event._detachedInterceptFiber = internalInstanceHandle;
    
  813.     target.dispatchEvent(event);
    
  814.   }
    
  815. }
    
  816. 
    
  817. function dispatchAfterDetachedBlur(target: HTMLElement): void {
    
  818.   if (enableCreateEventHandleAPI) {
    
  819.     const event = createEvent('afterblur', false);
    
  820.     // So we know what was detached, make the relatedTarget the
    
  821.     // detached target on the "afterblur" event.
    
  822.     (event: any).relatedTarget = target;
    
  823.     // Dispatch the event on the document.
    
  824.     document.dispatchEvent(event);
    
  825.   }
    
  826. }
    
  827. 
    
  828. export function removeChild(
    
  829.   parentInstance: Instance,
    
  830.   child: Instance | TextInstance | SuspenseInstance,
    
  831. ): void {
    
  832.   parentInstance.removeChild(child);
    
  833. }
    
  834. 
    
  835. export function removeChildFromContainer(
    
  836.   container: Container,
    
  837.   child: Instance | TextInstance | SuspenseInstance,
    
  838. ): void {
    
  839.   if (container.nodeType === COMMENT_NODE) {
    
  840.     (container.parentNode: any).removeChild(child);
    
  841.   } else {
    
  842.     container.removeChild(child);
    
  843.   }
    
  844. }
    
  845. 
    
  846. export function clearSuspenseBoundary(
    
  847.   parentInstance: Instance,
    
  848.   suspenseInstance: SuspenseInstance,
    
  849. ): void {
    
  850.   let node: Node = suspenseInstance;
    
  851.   // Delete all nodes within this suspense boundary.
    
  852.   // There might be nested nodes so we need to keep track of how
    
  853.   // deep we are and only break out when we're back on top.
    
  854.   let depth = 0;
    
  855.   do {
    
  856.     const nextNode = node.nextSibling;
    
  857.     parentInstance.removeChild(node);
    
  858.     if (nextNode && nextNode.nodeType === COMMENT_NODE) {
    
  859.       const data = ((nextNode: any).data: string);
    
  860.       if (data === SUSPENSE_END_DATA) {
    
  861.         if (depth === 0) {
    
  862.           parentInstance.removeChild(nextNode);
    
  863.           // Retry if any event replaying was blocked on this.
    
  864.           retryIfBlockedOn(suspenseInstance);
    
  865.           return;
    
  866.         } else {
    
  867.           depth--;
    
  868.         }
    
  869.       } else if (
    
  870.         data === SUSPENSE_START_DATA ||
    
  871.         data === SUSPENSE_PENDING_START_DATA ||
    
  872.         data === SUSPENSE_FALLBACK_START_DATA
    
  873.       ) {
    
  874.         depth++;
    
  875.       }
    
  876.     }
    
  877.     // $FlowFixMe[incompatible-type] we bail out when we get a null
    
  878.     node = nextNode;
    
  879.   } while (node);
    
  880.   // TODO: Warn, we didn't find the end comment boundary.
    
  881.   // Retry if any event replaying was blocked on this.
    
  882.   retryIfBlockedOn(suspenseInstance);
    
  883. }
    
  884. 
    
  885. export function clearSuspenseBoundaryFromContainer(
    
  886.   container: Container,
    
  887.   suspenseInstance: SuspenseInstance,
    
  888. ): void {
    
  889.   if (container.nodeType === COMMENT_NODE) {
    
  890.     clearSuspenseBoundary((container.parentNode: any), suspenseInstance);
    
  891.   } else if (container.nodeType === ELEMENT_NODE) {
    
  892.     clearSuspenseBoundary((container: any), suspenseInstance);
    
  893.   } else {
    
  894.     // Document nodes should never contain suspense boundaries.
    
  895.   }
    
  896.   // Retry if any event replaying was blocked on this.
    
  897.   retryIfBlockedOn(container);
    
  898. }
    
  899. 
    
  900. export function hideInstance(instance: Instance): void {
    
  901.   // TODO: Does this work for all element types? What about MathML? Should we
    
  902.   // pass host context to this method?
    
  903.   instance = ((instance: any): HTMLElement);
    
  904.   const style = instance.style;
    
  905.   // $FlowFixMe[method-unbinding]
    
  906.   if (typeof style.setProperty === 'function') {
    
  907.     style.setProperty('display', 'none', 'important');
    
  908.   } else {
    
  909.     style.display = 'none';
    
  910.   }
    
  911. }
    
  912. 
    
  913. export function hideTextInstance(textInstance: TextInstance): void {
    
  914.   textInstance.nodeValue = '';
    
  915. }
    
  916. 
    
  917. export function unhideInstance(instance: Instance, props: Props): void {
    
  918.   instance = ((instance: any): HTMLElement);
    
  919.   const styleProp = props[STYLE];
    
  920.   const display =
    
  921.     styleProp !== undefined &&
    
  922.     styleProp !== null &&
    
  923.     styleProp.hasOwnProperty('display')
    
  924.       ? styleProp.display
    
  925.       : null;
    
  926.   instance.style.display =
    
  927.     display == null || typeof display === 'boolean'
    
  928.       ? ''
    
  929.       : // The value would've errored already if it wasn't safe.
    
  930.         // eslint-disable-next-line react-internal/safe-string-coercion
    
  931.         ('' + display).trim();
    
  932. }
    
  933. 
    
  934. export function unhideTextInstance(
    
  935.   textInstance: TextInstance,
    
  936.   text: string,
    
  937. ): void {
    
  938.   textInstance.nodeValue = text;
    
  939. }
    
  940. 
    
  941. export function clearContainer(container: Container): void {
    
  942.   if (enableHostSingletons) {
    
  943.     const nodeType = container.nodeType;
    
  944.     if (nodeType === DOCUMENT_NODE) {
    
  945.       clearContainerSparingly(container);
    
  946.     } else if (nodeType === ELEMENT_NODE) {
    
  947.       switch (container.nodeName) {
    
  948.         case 'HEAD':
    
  949.         case 'HTML':
    
  950.         case 'BODY':
    
  951.           clearContainerSparingly(container);
    
  952.           return;
    
  953.         default: {
    
  954.           container.textContent = '';
    
  955.         }
    
  956.       }
    
  957.     }
    
  958.   } else {
    
  959.     if (container.nodeType === ELEMENT_NODE) {
    
  960.       // We have refined the container to Element type
    
  961.       const element: Element = (container: any);
    
  962.       element.textContent = '';
    
  963.     } else if (container.nodeType === DOCUMENT_NODE) {
    
  964.       // We have refined the container to Document type
    
  965.       const doc: Document = (container: any);
    
  966.       if (doc.documentElement) {
    
  967.         doc.removeChild(doc.documentElement);
    
  968.       }
    
  969.     }
    
  970.   }
    
  971. }
    
  972. 
    
  973. function clearContainerSparingly(container: Node) {
    
  974.   let node;
    
  975.   let nextNode: ?Node = container.firstChild;
    
  976.   if (nextNode && nextNode.nodeType === DOCUMENT_TYPE_NODE) {
    
  977.     nextNode = nextNode.nextSibling;
    
  978.   }
    
  979.   while (nextNode) {
    
  980.     node = nextNode;
    
  981.     nextNode = nextNode.nextSibling;
    
  982.     switch (node.nodeName) {
    
  983.       case 'HTML':
    
  984.       case 'HEAD':
    
  985.       case 'BODY': {
    
  986.         const element: Element = (node: any);
    
  987.         clearContainerSparingly(element);
    
  988.         // If these singleton instances had previously been rendered with React they
    
  989.         // may still hold on to references to the previous fiber tree. We detatch them
    
  990.         // prospectively to reset them to a baseline starting state since we cannot create
    
  991.         // new instances.
    
  992.         detachDeletedInstance(element);
    
  993.         continue;
    
  994.       }
    
  995.       // Script tags are retained to avoid an edge case bug. Normally scripts will execute if they
    
  996.       // are ever inserted into the DOM. However when streaming if a script tag is opened but not
    
  997.       // yet closed some browsers create and insert the script DOM Node but the script cannot execute
    
  998.       // yet until the closing tag is parsed. If something causes React to call clearContainer while
    
  999.       // this DOM node is in the document but not yet executable the DOM node will be removed from the
    
  1000.       // document and when the script closing tag comes in the script will not end up running. This seems
    
  1001.       // to happen in Chrome/Firefox but not Safari at the moment though this is not necessarily specified
    
  1002.       // behavior so it could change in future versions of browsers. While leaving all scripts is broader
    
  1003.       // than strictly necessary this is the least amount of additional code to avoid this breaking
    
  1004.       // edge case.
    
  1005.       //
    
  1006.       // Style tags are retained because they may likely come from 3rd party scripts and extensions
    
  1007.       case 'SCRIPT':
    
  1008.       case 'STYLE': {
    
  1009.         continue;
    
  1010.       }
    
  1011.       // Stylesheet tags are retained because tehy may likely come from 3rd party scripts and extensions
    
  1012.       case 'LINK': {
    
  1013.         if (((node: any): HTMLLinkElement).rel.toLowerCase() === 'stylesheet') {
    
  1014.           continue;
    
  1015.         }
    
  1016.       }
    
  1017.     }
    
  1018.     container.removeChild(node);
    
  1019.   }
    
  1020.   return;
    
  1021. }
    
  1022. 
    
  1023. // Making this so we can eventually move all of the instance caching to the commit phase.
    
  1024. // Currently this is only used to associate fiber and props to instances for hydrating
    
  1025. // HostSingletons. The reason we need it here is we only want to make this binding on commit
    
  1026. // because only one fiber can own the instance at a time and render can fail/restart
    
  1027. export function bindInstance(
    
  1028.   instance: Instance,
    
  1029.   props: Props,
    
  1030.   internalInstanceHandle: mixed,
    
  1031. ) {
    
  1032.   precacheFiberNode((internalInstanceHandle: any), instance);
    
  1033.   updateFiberProps(instance, props);
    
  1034. }
    
  1035. 
    
  1036. // -------------------
    
  1037. //     Hydration
    
  1038. // -------------------
    
  1039. 
    
  1040. export const supportsHydration = true;
    
  1041. 
    
  1042. export function isHydratableText(text: string): boolean {
    
  1043.   return text !== '';
    
  1044. }
    
  1045. 
    
  1046. export function canHydrateInstance(
    
  1047.   instance: HydratableInstance,
    
  1048.   type: string,
    
  1049.   props: Props,
    
  1050.   inRootOrSingleton: boolean,
    
  1051. ): null | Instance {
    
  1052.   while (instance.nodeType === ELEMENT_NODE) {
    
  1053.     const element: Element = (instance: any);
    
  1054.     const anyProps = (props: any);
    
  1055.     if (element.nodeName.toLowerCase() !== type.toLowerCase()) {
    
  1056.       if (!inRootOrSingleton || !enableHostSingletons) {
    
  1057.         // Usually we error for mismatched tags.
    
  1058.         if (
    
  1059.           enableFormActions &&
    
  1060.           element.nodeName === 'INPUT' &&
    
  1061.           (element: any).type === 'hidden'
    
  1062.         ) {
    
  1063.           // If we have extra hidden inputs, we don't mismatch. This allows us to embed
    
  1064.           // extra form data in the original form.
    
  1065.         } else {
    
  1066.           return null;
    
  1067.         }
    
  1068.       }
    
  1069.       // In root or singleton parents we skip past mismatched instances.
    
  1070.     } else if (!inRootOrSingleton || !enableHostSingletons) {
    
  1071.       // Match
    
  1072.       if (
    
  1073.         enableFormActions &&
    
  1074.         type === 'input' &&
    
  1075.         (element: any).type === 'hidden'
    
  1076.       ) {
    
  1077.         if (__DEV__) {
    
  1078.           checkAttributeStringCoercion(anyProps.name, 'name');
    
  1079.         }
    
  1080.         const name = anyProps.name == null ? null : '' + anyProps.name;
    
  1081.         if (
    
  1082.           anyProps.type !== 'hidden' ||
    
  1083.           element.getAttribute('name') !== name
    
  1084.         ) {
    
  1085.           // Skip past hidden inputs unless that's what we're looking for. This allows us
    
  1086.           // embed extra form data in the original form.
    
  1087.         } else {
    
  1088.           return element;
    
  1089.         }
    
  1090.       } else {
    
  1091.         return element;
    
  1092.       }
    
  1093.     } else if (isMarkedHoistable(element)) {
    
  1094.       // We've already claimed this as a hoistable which isn't hydrated this way so we skip past it.
    
  1095.     } else {
    
  1096.       // We have an Element with the right type.
    
  1097. 
    
  1098.       // We are going to try to exclude it if we can definitely identify it as a hoisted Node or if
    
  1099.       // we can guess that the node is likely hoisted or was inserted by a 3rd party script or browser extension
    
  1100.       // using high entropy attributes for certain types. This technique will fail for strange insertions like
    
  1101.       // extension prepending <div> in the <body> but that already breaks before and that is an edge case.
    
  1102.       switch (type) {
    
  1103.         // case 'title':
    
  1104.         //We assume all titles are matchable. You should only have one in the Document, at least in a hoistable scope
    
  1105.         // and if you are a HostComponent with type title we must either be in an <svg> context or this title must have an `itemProp` prop.
    
  1106.         case 'meta': {
    
  1107.           // The only way to opt out of hoisting meta tags is to give it an itemprop attribute. We assume there will be
    
  1108.           // not 3rd party meta tags that are prepended, accepting the cases where this isn't true because meta tags
    
  1109.           // are usually only functional for SSR so even in a rare case where we did bind to an injected tag the runtime
    
  1110.           // implications are minimal
    
  1111.           if (!element.hasAttribute('itemprop')) {
    
  1112.             // This is a Hoistable
    
  1113.             break;
    
  1114.           }
    
  1115.           return element;
    
  1116.         }
    
  1117.         case 'link': {
    
  1118.           // Links come in many forms and we do expect 3rd parties to inject them into <head> / <body>. We exclude known resources
    
  1119.           // and then use high-entroy attributes like href which are almost always used and almost always unique to filter out unlikely
    
  1120.           // matches.
    
  1121.           const rel = element.getAttribute('rel');
    
  1122.           if (rel === 'stylesheet' && element.hasAttribute('data-precedence')) {
    
  1123.             // This is a stylesheet resource
    
  1124.             break;
    
  1125.           } else if (
    
  1126.             rel !== anyProps.rel ||
    
  1127.             element.getAttribute('href') !==
    
  1128.               (anyProps.href == null ? null : anyProps.href) ||
    
  1129.             element.getAttribute('crossorigin') !==
    
  1130.               (anyProps.crossOrigin == null ? null : anyProps.crossOrigin) ||
    
  1131.             element.getAttribute('title') !==
    
  1132.               (anyProps.title == null ? null : anyProps.title)
    
  1133.           ) {
    
  1134.             // rel + href should usually be enough to uniquely identify a link however crossOrigin can vary for rel preconnect
    
  1135.             // and title could vary for rel alternate
    
  1136.             break;
    
  1137.           }
    
  1138.           return element;
    
  1139.         }
    
  1140.         case 'style': {
    
  1141.           // Styles are hard to match correctly. We can exclude known resources but otherwise we accept the fact that a non-hoisted style tags
    
  1142.           // in <head> or <body> are likely never going to be unmounted given their position in the document and the fact they likely hold global styles
    
  1143.           if (element.hasAttribute('data-precedence')) {
    
  1144.             // This is a style resource
    
  1145.             break;
    
  1146.           }
    
  1147.           return element;
    
  1148.         }
    
  1149.         case 'script': {
    
  1150.           // Scripts are a little tricky, we exclude known resources and then similar to links try to use high-entropy attributes
    
  1151.           // to reject poor matches. One challenge with scripts are inline scripts. We don't attempt to check text content which could
    
  1152.           // in theory lead to a hydration error later if a 3rd party injected an inline script before the React rendered nodes.
    
  1153.           // Falling back to client rendering if this happens should be seemless though so we will try this hueristic and revisit later
    
  1154.           // if we learn it is problematic
    
  1155.           const srcAttr = element.getAttribute('src');
    
  1156.           if (
    
  1157.             srcAttr !== (anyProps.src == null ? null : anyProps.src) ||
    
  1158.             element.getAttribute('type') !==
    
  1159.               (anyProps.type == null ? null : anyProps.type) ||
    
  1160.             element.getAttribute('crossorigin') !==
    
  1161.               (anyProps.crossOrigin == null ? null : anyProps.crossOrigin)
    
  1162.           ) {
    
  1163.             // This script is for a different src/type/crossOrigin. It may be a script resource
    
  1164.             // or it may just be a mistmatch
    
  1165.             if (
    
  1166.               srcAttr &&
    
  1167.               element.hasAttribute('async') &&
    
  1168.               !element.hasAttribute('itemprop')
    
  1169.             ) {
    
  1170.               // This is an async script resource
    
  1171.               break;
    
  1172.             }
    
  1173.           }
    
  1174.           return element;
    
  1175.         }
    
  1176.         default: {
    
  1177.           // We have excluded the most likely cases of mismatch between hoistable tags, 3rd party script inserted tags,
    
  1178.           // and browser extension inserted tags. While it is possible this is not the right match it is a decent hueristic
    
  1179.           // that should work in the vast majority of cases.
    
  1180.           return element;
    
  1181.         }
    
  1182.       }
    
  1183.     }
    
  1184.     const nextInstance = getNextHydratableSibling(element);
    
  1185.     if (nextInstance === null) {
    
  1186.       break;
    
  1187.     }
    
  1188.     instance = nextInstance;
    
  1189.   }
    
  1190.   // This is a suspense boundary or Text node or we got the end.
    
  1191.   // Suspense Boundaries are never expected to be injected by 3rd parties. If we see one it should be matched
    
  1192.   // and this is a hydration error.
    
  1193.   // Text Nodes are also not expected to be injected by 3rd parties. This is less of a guarantee for <body>
    
  1194.   // but it seems reasonable and conservative to reject this as a hydration error as well
    
  1195.   return null;
    
  1196. }
    
  1197. 
    
  1198. export function canHydrateTextInstance(
    
  1199.   instance: HydratableInstance,
    
  1200.   text: string,
    
  1201.   inRootOrSingleton: boolean,
    
  1202. ): null | TextInstance {
    
  1203.   // Empty strings are not parsed by HTML so there won't be a correct match here.
    
  1204.   if (text === '') return null;
    
  1205. 
    
  1206.   while (instance.nodeType !== TEXT_NODE) {
    
  1207.     if (
    
  1208.       enableFormActions &&
    
  1209.       instance.nodeType === ELEMENT_NODE &&
    
  1210.       instance.nodeName === 'INPUT' &&
    
  1211.       (instance: any).type === 'hidden'
    
  1212.     ) {
    
  1213.       // If we have extra hidden inputs, we don't mismatch. This allows us to
    
  1214.       // embed extra form data in the original form.
    
  1215.     } else if (!inRootOrSingleton || !enableHostSingletons) {
    
  1216.       return null;
    
  1217.     }
    
  1218.     const nextInstance = getNextHydratableSibling(instance);
    
  1219.     if (nextInstance === null) {
    
  1220.       return null;
    
  1221.     }
    
  1222.     instance = nextInstance;
    
  1223.   }
    
  1224.   // This has now been refined to a text node.
    
  1225.   return ((instance: any): TextInstance);
    
  1226. }
    
  1227. 
    
  1228. export function canHydrateSuspenseInstance(
    
  1229.   instance: HydratableInstance,
    
  1230.   inRootOrSingleton: boolean,
    
  1231. ): null | SuspenseInstance {
    
  1232.   while (instance.nodeType !== COMMENT_NODE) {
    
  1233.     if (!inRootOrSingleton || !enableHostSingletons) {
    
  1234.       return null;
    
  1235.     }
    
  1236.     const nextInstance = getNextHydratableSibling(instance);
    
  1237.     if (nextInstance === null) {
    
  1238.       return null;
    
  1239.     }
    
  1240.     instance = nextInstance;
    
  1241.   }
    
  1242.   // This has now been refined to a suspense node.
    
  1243.   return ((instance: any): SuspenseInstance);
    
  1244. }
    
  1245. 
    
  1246. export function isSuspenseInstancePending(instance: SuspenseInstance): boolean {
    
  1247.   return instance.data === SUSPENSE_PENDING_START_DATA;
    
  1248. }
    
  1249. 
    
  1250. export function isSuspenseInstanceFallback(
    
  1251.   instance: SuspenseInstance,
    
  1252. ): boolean {
    
  1253.   return instance.data === SUSPENSE_FALLBACK_START_DATA;
    
  1254. }
    
  1255. 
    
  1256. export function getSuspenseInstanceFallbackErrorDetails(
    
  1257.   instance: SuspenseInstance,
    
  1258. ): {digest: ?string, message?: string, stack?: string} {
    
  1259.   const dataset =
    
  1260.     instance.nextSibling && ((instance.nextSibling: any): HTMLElement).dataset;
    
  1261.   let digest, message, stack;
    
  1262.   if (dataset) {
    
  1263.     digest = dataset.dgst;
    
  1264.     if (__DEV__) {
    
  1265.       message = dataset.msg;
    
  1266.       stack = dataset.stck;
    
  1267.     }
    
  1268.   }
    
  1269.   if (__DEV__) {
    
  1270.     return {
    
  1271.       message,
    
  1272.       digest,
    
  1273.       stack,
    
  1274.     };
    
  1275.   } else {
    
  1276.     // Object gets DCE'd if constructed in tail position and matches callsite destructuring
    
  1277.     return {
    
  1278.       digest,
    
  1279.     };
    
  1280.   }
    
  1281. }
    
  1282. 
    
  1283. export function registerSuspenseInstanceRetry(
    
  1284.   instance: SuspenseInstance,
    
  1285.   callback: () => void,
    
  1286. ) {
    
  1287.   instance._reactRetry = callback;
    
  1288. }
    
  1289. 
    
  1290. export function canHydrateFormStateMarker(
    
  1291.   instance: HydratableInstance,
    
  1292.   inRootOrSingleton: boolean,
    
  1293. ): null | FormStateMarkerInstance {
    
  1294.   while (instance.nodeType !== COMMENT_NODE) {
    
  1295.     if (!inRootOrSingleton || !enableHostSingletons) {
    
  1296.       return null;
    
  1297.     }
    
  1298.     const nextInstance = getNextHydratableSibling(instance);
    
  1299.     if (nextInstance === null) {
    
  1300.       return null;
    
  1301.     }
    
  1302.     instance = nextInstance;
    
  1303.   }
    
  1304.   const nodeData = (instance: any).data;
    
  1305.   if (
    
  1306.     nodeData === FORM_STATE_IS_MATCHING ||
    
  1307.     nodeData === FORM_STATE_IS_NOT_MATCHING
    
  1308.   ) {
    
  1309.     const markerInstance: FormStateMarkerInstance = (instance: any);
    
  1310.     return markerInstance;
    
  1311.   }
    
  1312.   return null;
    
  1313. }
    
  1314. 
    
  1315. export function isFormStateMarkerMatching(
    
  1316.   markerInstance: FormStateMarkerInstance,
    
  1317. ): boolean {
    
  1318.   return markerInstance.data === FORM_STATE_IS_MATCHING;
    
  1319. }
    
  1320. 
    
  1321. function getNextHydratable(node: ?Node) {
    
  1322.   // Skip non-hydratable nodes.
    
  1323.   for (; node != null; node = ((node: any): Node).nextSibling) {
    
  1324.     const nodeType = node.nodeType;
    
  1325.     if (nodeType === ELEMENT_NODE || nodeType === TEXT_NODE) {
    
  1326.       break;
    
  1327.     }
    
  1328.     if (nodeType === COMMENT_NODE) {
    
  1329.       const nodeData = (node: any).data;
    
  1330.       if (
    
  1331.         nodeData === SUSPENSE_START_DATA ||
    
  1332.         nodeData === SUSPENSE_FALLBACK_START_DATA ||
    
  1333.         nodeData === SUSPENSE_PENDING_START_DATA ||
    
  1334.         (enableFormActions &&
    
  1335.           enableAsyncActions &&
    
  1336.           (nodeData === FORM_STATE_IS_MATCHING ||
    
  1337.             nodeData === FORM_STATE_IS_NOT_MATCHING))
    
  1338.       ) {
    
  1339.         break;
    
  1340.       }
    
  1341.       if (nodeData === SUSPENSE_END_DATA) {
    
  1342.         return null;
    
  1343.       }
    
  1344.     }
    
  1345.   }
    
  1346.   return (node: any);
    
  1347. }
    
  1348. 
    
  1349. export function getNextHydratableSibling(
    
  1350.   instance: HydratableInstance,
    
  1351. ): null | HydratableInstance {
    
  1352.   return getNextHydratable(instance.nextSibling);
    
  1353. }
    
  1354. 
    
  1355. export function getFirstHydratableChild(
    
  1356.   parentInstance: Instance,
    
  1357. ): null | HydratableInstance {
    
  1358.   return getNextHydratable(parentInstance.firstChild);
    
  1359. }
    
  1360. 
    
  1361. export function getFirstHydratableChildWithinContainer(
    
  1362.   parentContainer: Container,
    
  1363. ): null | HydratableInstance {
    
  1364.   return getNextHydratable(parentContainer.firstChild);
    
  1365. }
    
  1366. 
    
  1367. export function getFirstHydratableChildWithinSuspenseInstance(
    
  1368.   parentInstance: SuspenseInstance,
    
  1369. ): null | HydratableInstance {
    
  1370.   return getNextHydratable(parentInstance.nextSibling);
    
  1371. }
    
  1372. 
    
  1373. export function hydrateInstance(
    
  1374.   instance: Instance,
    
  1375.   type: string,
    
  1376.   props: Props,
    
  1377.   hostContext: HostContext,
    
  1378.   internalInstanceHandle: Object,
    
  1379.   shouldWarnDev: boolean,
    
  1380. ): void {
    
  1381.   precacheFiberNode(internalInstanceHandle, instance);
    
  1382.   // TODO: Possibly defer this until the commit phase where all the events
    
  1383.   // get attached.
    
  1384.   updateFiberProps(instance, props);
    
  1385. 
    
  1386.   // TODO: Temporary hack to check if we're in a concurrent root. We can delete
    
  1387.   // when the legacy root API is removed.
    
  1388.   const isConcurrentMode =
    
  1389.     ((internalInstanceHandle: Fiber).mode & ConcurrentMode) !== NoMode;
    
  1390. 
    
  1391.   diffHydratedProperties(
    
  1392.     instance,
    
  1393.     type,
    
  1394.     props,
    
  1395.     isConcurrentMode,
    
  1396.     shouldWarnDev,
    
  1397.     hostContext,
    
  1398.   );
    
  1399. }
    
  1400. 
    
  1401. export function hydrateTextInstance(
    
  1402.   textInstance: TextInstance,
    
  1403.   text: string,
    
  1404.   internalInstanceHandle: Object,
    
  1405.   shouldWarnDev: boolean,
    
  1406. ): boolean {
    
  1407.   precacheFiberNode(internalInstanceHandle, textInstance);
    
  1408. 
    
  1409.   // TODO: Temporary hack to check if we're in a concurrent root. We can delete
    
  1410.   // when the legacy root API is removed.
    
  1411.   const isConcurrentMode =
    
  1412.     ((internalInstanceHandle: Fiber).mode & ConcurrentMode) !== NoMode;
    
  1413. 
    
  1414.   return diffHydratedText(textInstance, text, isConcurrentMode);
    
  1415. }
    
  1416. 
    
  1417. export function hydrateSuspenseInstance(
    
  1418.   suspenseInstance: SuspenseInstance,
    
  1419.   internalInstanceHandle: Object,
    
  1420. ) {
    
  1421.   precacheFiberNode(internalInstanceHandle, suspenseInstance);
    
  1422. }
    
  1423. 
    
  1424. export function getNextHydratableInstanceAfterSuspenseInstance(
    
  1425.   suspenseInstance: SuspenseInstance,
    
  1426. ): null | HydratableInstance {
    
  1427.   let node = suspenseInstance.nextSibling;
    
  1428.   // Skip past all nodes within this suspense boundary.
    
  1429.   // There might be nested nodes so we need to keep track of how
    
  1430.   // deep we are and only break out when we're back on top.
    
  1431.   let depth = 0;
    
  1432.   while (node) {
    
  1433.     if (node.nodeType === COMMENT_NODE) {
    
  1434.       const data = ((node: any).data: string);
    
  1435.       if (data === SUSPENSE_END_DATA) {
    
  1436.         if (depth === 0) {
    
  1437.           return getNextHydratableSibling((node: any));
    
  1438.         } else {
    
  1439.           depth--;
    
  1440.         }
    
  1441.       } else if (
    
  1442.         data === SUSPENSE_START_DATA ||
    
  1443.         data === SUSPENSE_FALLBACK_START_DATA ||
    
  1444.         data === SUSPENSE_PENDING_START_DATA
    
  1445.       ) {
    
  1446.         depth++;
    
  1447.       }
    
  1448.     }
    
  1449.     node = node.nextSibling;
    
  1450.   }
    
  1451.   // TODO: Warn, we didn't find the end comment boundary.
    
  1452.   return null;
    
  1453. }
    
  1454. 
    
  1455. // Returns the SuspenseInstance if this node is a direct child of a
    
  1456. // SuspenseInstance. I.e. if its previous sibling is a Comment with
    
  1457. // SUSPENSE_x_START_DATA. Otherwise, null.
    
  1458. export function getParentSuspenseInstance(
    
  1459.   targetInstance: Node,
    
  1460. ): null | SuspenseInstance {
    
  1461.   let node = targetInstance.previousSibling;
    
  1462.   // Skip past all nodes within this suspense boundary.
    
  1463.   // There might be nested nodes so we need to keep track of how
    
  1464.   // deep we are and only break out when we're back on top.
    
  1465.   let depth = 0;
    
  1466.   while (node) {
    
  1467.     if (node.nodeType === COMMENT_NODE) {
    
  1468.       const data = ((node: any).data: string);
    
  1469.       if (
    
  1470.         data === SUSPENSE_START_DATA ||
    
  1471.         data === SUSPENSE_FALLBACK_START_DATA ||
    
  1472.         data === SUSPENSE_PENDING_START_DATA
    
  1473.       ) {
    
  1474.         if (depth === 0) {
    
  1475.           return ((node: any): SuspenseInstance);
    
  1476.         } else {
    
  1477.           depth--;
    
  1478.         }
    
  1479.       } else if (data === SUSPENSE_END_DATA) {
    
  1480.         depth++;
    
  1481.       }
    
  1482.     }
    
  1483.     node = node.previousSibling;
    
  1484.   }
    
  1485.   return null;
    
  1486. }
    
  1487. 
    
  1488. export function commitHydratedContainer(container: Container): void {
    
  1489.   // Retry if any event replaying was blocked on this.
    
  1490.   retryIfBlockedOn(container);
    
  1491. }
    
  1492. 
    
  1493. export function commitHydratedSuspenseInstance(
    
  1494.   suspenseInstance: SuspenseInstance,
    
  1495. ): void {
    
  1496.   // Retry if any event replaying was blocked on this.
    
  1497.   retryIfBlockedOn(suspenseInstance);
    
  1498. }
    
  1499. 
    
  1500. export function shouldDeleteUnhydratedTailInstances(
    
  1501.   parentType: string,
    
  1502. ): boolean {
    
  1503.   return (
    
  1504.     (enableHostSingletons ||
    
  1505.       (parentType !== 'head' && parentType !== 'body')) &&
    
  1506.     (!enableFormActions || (parentType !== 'form' && parentType !== 'button'))
    
  1507.   );
    
  1508. }
    
  1509. 
    
  1510. export function didNotMatchHydratedContainerTextInstance(
    
  1511.   parentContainer: Container,
    
  1512.   textInstance: TextInstance,
    
  1513.   text: string,
    
  1514.   isConcurrentMode: boolean,
    
  1515.   shouldWarnDev: boolean,
    
  1516. ) {
    
  1517.   checkForUnmatchedText(
    
  1518.     textInstance.nodeValue,
    
  1519.     text,
    
  1520.     isConcurrentMode,
    
  1521.     shouldWarnDev,
    
  1522.   );
    
  1523. }
    
  1524. 
    
  1525. export function didNotMatchHydratedTextInstance(
    
  1526.   parentType: string,
    
  1527.   parentProps: Props,
    
  1528.   parentInstance: Instance,
    
  1529.   textInstance: TextInstance,
    
  1530.   text: string,
    
  1531.   isConcurrentMode: boolean,
    
  1532.   shouldWarnDev: boolean,
    
  1533. ) {
    
  1534.   if (parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
    
  1535.     checkForUnmatchedText(
    
  1536.       textInstance.nodeValue,
    
  1537.       text,
    
  1538.       isConcurrentMode,
    
  1539.       shouldWarnDev,
    
  1540.     );
    
  1541.   }
    
  1542. }
    
  1543. 
    
  1544. export function didNotHydrateInstanceWithinContainer(
    
  1545.   parentContainer: Container,
    
  1546.   instance: HydratableInstance,
    
  1547. ) {
    
  1548.   if (__DEV__) {
    
  1549.     if (instance.nodeType === ELEMENT_NODE) {
    
  1550.       warnForDeletedHydratableElement(parentContainer, (instance: any));
    
  1551.     } else if (instance.nodeType === COMMENT_NODE) {
    
  1552.       // TODO: warnForDeletedHydratableSuspenseBoundary
    
  1553.     } else {
    
  1554.       warnForDeletedHydratableText(parentContainer, (instance: any));
    
  1555.     }
    
  1556.   }
    
  1557. }
    
  1558. 
    
  1559. export function didNotHydrateInstanceWithinSuspenseInstance(
    
  1560.   parentInstance: SuspenseInstance,
    
  1561.   instance: HydratableInstance,
    
  1562. ) {
    
  1563.   if (__DEV__) {
    
  1564.     // $FlowFixMe[incompatible-type]: Only Element or Document can be parent nodes.
    
  1565.     const parentNode: Element | Document | null = parentInstance.parentNode;
    
  1566.     if (parentNode !== null) {
    
  1567.       if (instance.nodeType === ELEMENT_NODE) {
    
  1568.         warnForDeletedHydratableElement(parentNode, (instance: any));
    
  1569.       } else if (instance.nodeType === COMMENT_NODE) {
    
  1570.         // TODO: warnForDeletedHydratableSuspenseBoundary
    
  1571.       } else {
    
  1572.         warnForDeletedHydratableText(parentNode, (instance: any));
    
  1573.       }
    
  1574.     }
    
  1575.   }
    
  1576. }
    
  1577. 
    
  1578. export function didNotHydrateInstance(
    
  1579.   parentType: string,
    
  1580.   parentProps: Props,
    
  1581.   parentInstance: Instance,
    
  1582.   instance: HydratableInstance,
    
  1583.   isConcurrentMode: boolean,
    
  1584. ) {
    
  1585.   if (__DEV__) {
    
  1586.     if (isConcurrentMode || parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
    
  1587.       if (instance.nodeType === ELEMENT_NODE) {
    
  1588.         warnForDeletedHydratableElement(parentInstance, (instance: any));
    
  1589.       } else if (instance.nodeType === COMMENT_NODE) {
    
  1590.         // TODO: warnForDeletedHydratableSuspenseBoundary
    
  1591.       } else {
    
  1592.         warnForDeletedHydratableText(parentInstance, (instance: any));
    
  1593.       }
    
  1594.     }
    
  1595.   }
    
  1596. }
    
  1597. 
    
  1598. export function didNotFindHydratableInstanceWithinContainer(
    
  1599.   parentContainer: Container,
    
  1600.   type: string,
    
  1601.   props: Props,
    
  1602. ) {
    
  1603.   if (__DEV__) {
    
  1604.     warnForInsertedHydratedElement(parentContainer, type, props);
    
  1605.   }
    
  1606. }
    
  1607. 
    
  1608. export function didNotFindHydratableTextInstanceWithinContainer(
    
  1609.   parentContainer: Container,
    
  1610.   text: string,
    
  1611. ) {
    
  1612.   if (__DEV__) {
    
  1613.     warnForInsertedHydratedText(parentContainer, text);
    
  1614.   }
    
  1615. }
    
  1616. 
    
  1617. export function didNotFindHydratableSuspenseInstanceWithinContainer(
    
  1618.   parentContainer: Container,
    
  1619. ) {
    
  1620.   if (__DEV__) {
    
  1621.     // TODO: warnForInsertedHydratedSuspense(parentContainer);
    
  1622.   }
    
  1623. }
    
  1624. 
    
  1625. export function didNotFindHydratableInstanceWithinSuspenseInstance(
    
  1626.   parentInstance: SuspenseInstance,
    
  1627.   type: string,
    
  1628.   props: Props,
    
  1629. ) {
    
  1630.   if (__DEV__) {
    
  1631.     // $FlowFixMe[incompatible-type]: Only Element or Document can be parent nodes.
    
  1632.     const parentNode: Element | Document | null = parentInstance.parentNode;
    
  1633.     if (parentNode !== null)
    
  1634.       warnForInsertedHydratedElement(parentNode, type, props);
    
  1635.   }
    
  1636. }
    
  1637. 
    
  1638. export function didNotFindHydratableTextInstanceWithinSuspenseInstance(
    
  1639.   parentInstance: SuspenseInstance,
    
  1640.   text: string,
    
  1641. ) {
    
  1642.   if (__DEV__) {
    
  1643.     // $FlowFixMe[incompatible-type]: Only Element or Document can be parent nodes.
    
  1644.     const parentNode: Element | Document | null = parentInstance.parentNode;
    
  1645.     if (parentNode !== null) warnForInsertedHydratedText(parentNode, text);
    
  1646.   }
    
  1647. }
    
  1648. 
    
  1649. export function didNotFindHydratableSuspenseInstanceWithinSuspenseInstance(
    
  1650.   parentInstance: SuspenseInstance,
    
  1651. ) {
    
  1652.   if (__DEV__) {
    
  1653.     // const parentNode: Element | Document | null = parentInstance.parentNode;
    
  1654.     // TODO: warnForInsertedHydratedSuspense(parentNode);
    
  1655.   }
    
  1656. }
    
  1657. 
    
  1658. export function didNotFindHydratableInstance(
    
  1659.   parentType: string,
    
  1660.   parentProps: Props,
    
  1661.   parentInstance: Instance,
    
  1662.   type: string,
    
  1663.   props: Props,
    
  1664.   isConcurrentMode: boolean,
    
  1665. ) {
    
  1666.   if (__DEV__) {
    
  1667.     if (isConcurrentMode || parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
    
  1668.       warnForInsertedHydratedElement(parentInstance, type, props);
    
  1669.     }
    
  1670.   }
    
  1671. }
    
  1672. 
    
  1673. export function didNotFindHydratableTextInstance(
    
  1674.   parentType: string,
    
  1675.   parentProps: Props,
    
  1676.   parentInstance: Instance,
    
  1677.   text: string,
    
  1678.   isConcurrentMode: boolean,
    
  1679. ) {
    
  1680.   if (__DEV__) {
    
  1681.     if (isConcurrentMode || parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
    
  1682.       warnForInsertedHydratedText(parentInstance, text);
    
  1683.     }
    
  1684.   }
    
  1685. }
    
  1686. 
    
  1687. export function didNotFindHydratableSuspenseInstance(
    
  1688.   parentType: string,
    
  1689.   parentProps: Props,
    
  1690.   parentInstance: Instance,
    
  1691. ) {
    
  1692.   if (__DEV__) {
    
  1693.     // TODO: warnForInsertedHydratedSuspense(parentInstance);
    
  1694.   }
    
  1695. }
    
  1696. 
    
  1697. export function errorHydratingContainer(parentContainer: Container): void {
    
  1698.   if (__DEV__) {
    
  1699.     // TODO: This gets logged by onRecoverableError, too, so we should be
    
  1700.     // able to remove it.
    
  1701.     console.error(
    
  1702.       'An error occurred during hydration. The server HTML was replaced with client content in <%s>.',
    
  1703.       parentContainer.nodeName.toLowerCase(),
    
  1704.     );
    
  1705.   }
    
  1706. }
    
  1707. 
    
  1708. // -------------------
    
  1709. //     Test Selectors
    
  1710. // -------------------
    
  1711. 
    
  1712. export const supportsTestSelectors = true;
    
  1713. 
    
  1714. export function findFiberRoot(node: Instance): null | FiberRoot {
    
  1715.   const stack = [node];
    
  1716.   let index = 0;
    
  1717.   while (index < stack.length) {
    
  1718.     const current = stack[index++];
    
  1719.     if (isContainerMarkedAsRoot(current)) {
    
  1720.       return ((getInstanceFromNodeDOMTree(current): any): FiberRoot);
    
  1721.     }
    
  1722.     stack.push(...current.children);
    
  1723.   }
    
  1724.   return null;
    
  1725. }
    
  1726. 
    
  1727. export function getBoundingRect(node: Instance): BoundingRect {
    
  1728.   const rect = node.getBoundingClientRect();
    
  1729.   return {
    
  1730.     x: rect.left,
    
  1731.     y: rect.top,
    
  1732.     width: rect.width,
    
  1733.     height: rect.height,
    
  1734.   };
    
  1735. }
    
  1736. 
    
  1737. export function matchAccessibilityRole(node: Instance, role: string): boolean {
    
  1738.   if (hasRole(node, role)) {
    
  1739.     return true;
    
  1740.   }
    
  1741. 
    
  1742.   return false;
    
  1743. }
    
  1744. 
    
  1745. export function getTextContent(fiber: Fiber): string | null {
    
  1746.   switch (fiber.tag) {
    
  1747.     case HostHoistable:
    
  1748.     case HostSingleton:
    
  1749.     case HostComponent:
    
  1750.       let textContent = '';
    
  1751.       const childNodes = fiber.stateNode.childNodes;
    
  1752.       for (let i = 0; i < childNodes.length; i++) {
    
  1753.         const childNode = childNodes[i];
    
  1754.         if (childNode.nodeType === Node.TEXT_NODE) {
    
  1755.           textContent += childNode.textContent;
    
  1756.         }
    
  1757.       }
    
  1758.       return textContent;
    
  1759.     case HostText:
    
  1760.       return fiber.stateNode.textContent;
    
  1761.   }
    
  1762. 
    
  1763.   return null;
    
  1764. }
    
  1765. 
    
  1766. export function isHiddenSubtree(fiber: Fiber): boolean {
    
  1767.   return fiber.tag === HostComponent && fiber.memoizedProps.hidden === true;
    
  1768. }
    
  1769. 
    
  1770. export function setFocusIfFocusable(node: Instance): boolean {
    
  1771.   // The logic for determining if an element is focusable is kind of complex,
    
  1772.   // and since we want to actually change focus anyway- we can just skip it.
    
  1773.   // Instead we'll just listen for a "focus" event to verify that focus was set.
    
  1774.   //
    
  1775.   // We could compare the node to document.activeElement after focus,
    
  1776.   // but this would not handle the case where application code managed focus to automatically blur.
    
  1777.   let didFocus = false;
    
  1778.   const handleFocus = () => {
    
  1779.     didFocus = true;
    
  1780.   };
    
  1781. 
    
  1782.   const element = ((node: any): HTMLElement);
    
  1783.   try {
    
  1784.     element.addEventListener('focus', handleFocus);
    
  1785.     // $FlowFixMe[method-unbinding]
    
  1786.     (element.focus || HTMLElement.prototype.focus).call(element);
    
  1787.   } finally {
    
  1788.     element.removeEventListener('focus', handleFocus);
    
  1789.   }
    
  1790. 
    
  1791.   return didFocus;
    
  1792. }
    
  1793. 
    
  1794. type RectRatio = {
    
  1795.   ratio: number,
    
  1796.   rect: BoundingRect,
    
  1797. };
    
  1798. 
    
  1799. export function setupIntersectionObserver(
    
  1800.   targets: Array<Instance>,
    
  1801.   callback: ObserveVisibleRectsCallback,
    
  1802.   options?: IntersectionObserverOptions,
    
  1803. ): {
    
  1804.   disconnect: () => void,
    
  1805.   observe: (instance: Instance) => void,
    
  1806.   unobserve: (instance: Instance) => void,
    
  1807. } {
    
  1808.   const rectRatioCache: Map<Instance, RectRatio> = new Map();
    
  1809.   targets.forEach(target => {
    
  1810.     rectRatioCache.set(target, {
    
  1811.       rect: getBoundingRect(target),
    
  1812.       ratio: 0,
    
  1813.     });
    
  1814.   });
    
  1815. 
    
  1816.   const handleIntersection = (entries: Array<IntersectionObserverEntry>) => {
    
  1817.     entries.forEach(entry => {
    
  1818.       const {boundingClientRect, intersectionRatio, target} = entry;
    
  1819.       rectRatioCache.set(target, {
    
  1820.         rect: {
    
  1821.           x: boundingClientRect.left,
    
  1822.           y: boundingClientRect.top,
    
  1823.           width: boundingClientRect.width,
    
  1824.           height: boundingClientRect.height,
    
  1825.         },
    
  1826.         ratio: intersectionRatio,
    
  1827.       });
    
  1828.     });
    
  1829. 
    
  1830.     callback(Array.from(rectRatioCache.values()));
    
  1831.   };
    
  1832. 
    
  1833.   const observer = new IntersectionObserver(handleIntersection, options);
    
  1834.   targets.forEach(target => {
    
  1835.     observer.observe((target: any));
    
  1836.   });
    
  1837. 
    
  1838.   return {
    
  1839.     disconnect: () => observer.disconnect(),
    
  1840.     observe: target => {
    
  1841.       rectRatioCache.set(target, {
    
  1842.         rect: getBoundingRect(target),
    
  1843.         ratio: 0,
    
  1844.       });
    
  1845.       observer.observe((target: any));
    
  1846.     },
    
  1847.     unobserve: target => {
    
  1848.       rectRatioCache.delete(target);
    
  1849.       observer.unobserve((target: any));
    
  1850.     },
    
  1851.   };
    
  1852. }
    
  1853. 
    
  1854. export function requestPostPaintCallback(callback: (time: number) => void) {
    
  1855.   localRequestAnimationFrame(() => {
    
  1856.     localRequestAnimationFrame(time => callback(time));
    
  1857.   });
    
  1858. }
    
  1859. 
    
  1860. // -------------------
    
  1861. //     Singletons
    
  1862. // -------------------
    
  1863. 
    
  1864. export const supportsSingletons = true;
    
  1865. 
    
  1866. export function isHostSingletonType(type: string): boolean {
    
  1867.   return type === 'html' || type === 'head' || type === 'body';
    
  1868. }
    
  1869. 
    
  1870. export function resolveSingletonInstance(
    
  1871.   type: string,
    
  1872.   props: Props,
    
  1873.   rootContainerInstance: Container,
    
  1874.   hostContext: HostContext,
    
  1875.   validateDOMNestingDev: boolean,
    
  1876. ): Instance {
    
  1877.   if (__DEV__) {
    
  1878.     const hostContextDev = ((hostContext: any): HostContextDev);
    
  1879.     if (validateDOMNestingDev) {
    
  1880.       validateDOMNesting(type, hostContextDev.ancestorInfo);
    
  1881.     }
    
  1882.   }
    
  1883.   const ownerDocument = getOwnerDocumentFromRootContainer(
    
  1884.     rootContainerInstance,
    
  1885.   );
    
  1886.   switch (type) {
    
  1887.     case 'html': {
    
  1888.       const documentElement = ownerDocument.documentElement;
    
  1889.       if (!documentElement) {
    
  1890.         throw new Error(
    
  1891.           'React expected an <html> element (document.documentElement) to exist in the Document but one was' +
    
  1892.             ' not found. React never removes the documentElement for any Document it renders into so' +
    
  1893.             ' the cause is likely in some other script running on this page.',
    
  1894.         );
    
  1895.       }
    
  1896.       return documentElement;
    
  1897.     }
    
  1898.     case 'head': {
    
  1899.       const head = ownerDocument.head;
    
  1900.       if (!head) {
    
  1901.         throw new Error(
    
  1902.           'React expected a <head> element (document.head) to exist in the Document but one was' +
    
  1903.             ' not found. React never removes the head for any Document it renders into so' +
    
  1904.             ' the cause is likely in some other script running on this page.',
    
  1905.         );
    
  1906.       }
    
  1907.       return head;
    
  1908.     }
    
  1909.     case 'body': {
    
  1910.       const body = ownerDocument.body;
    
  1911.       if (!body) {
    
  1912.         throw new Error(
    
  1913.           'React expected a <body> element (document.body) to exist in the Document but one was' +
    
  1914.             ' not found. React never removes the body for any Document it renders into so' +
    
  1915.             ' the cause is likely in some other script running on this page.',
    
  1916.         );
    
  1917.       }
    
  1918.       return body;
    
  1919.     }
    
  1920.     default: {
    
  1921.       throw new Error(
    
  1922.         'resolveSingletonInstance was called with an element type that is not supported. This is a bug in React.',
    
  1923.       );
    
  1924.     }
    
  1925.   }
    
  1926. }
    
  1927. 
    
  1928. export function acquireSingletonInstance(
    
  1929.   type: string,
    
  1930.   props: Props,
    
  1931.   instance: Instance,
    
  1932.   internalInstanceHandle: Object,
    
  1933. ): void {
    
  1934.   if (__DEV__) {
    
  1935.     const currentInstanceHandle = getInstanceFromNodeDOMTree(instance);
    
  1936.     if (currentInstanceHandle) {
    
  1937.       const tagName = instance.tagName.toLowerCase();
    
  1938.       console.error(
    
  1939.         'You are mounting a new %s component when a previous one has not first unmounted. It is an' +
    
  1940.           ' error to render more than one %s component at a time and attributes and children of these' +
    
  1941.           ' components will likely fail in unpredictable ways. Please only render a single instance of' +
    
  1942.           ' <%s> and if you need to mount a new one, ensure any previous ones have unmounted first.',
    
  1943.         tagName,
    
  1944.         tagName,
    
  1945.         tagName,
    
  1946.       );
    
  1947.     }
    
  1948.     switch (type) {
    
  1949.       case 'html':
    
  1950.       case 'head':
    
  1951.       case 'body': {
    
  1952.         break;
    
  1953.       }
    
  1954.       default: {
    
  1955.         console.error(
    
  1956.           'acquireSingletonInstance was called with an element type that is not supported. This is a bug in React.',
    
  1957.         );
    
  1958.       }
    
  1959.     }
    
  1960.   }
    
  1961. 
    
  1962.   const attributes = instance.attributes;
    
  1963.   while (attributes.length) {
    
  1964.     instance.removeAttributeNode(attributes[0]);
    
  1965.   }
    
  1966. 
    
  1967.   setInitialProperties(instance, type, props);
    
  1968.   precacheFiberNode(internalInstanceHandle, instance);
    
  1969.   updateFiberProps(instance, props);
    
  1970. }
    
  1971. 
    
  1972. export function releaseSingletonInstance(instance: Instance): void {
    
  1973.   const attributes = instance.attributes;
    
  1974.   while (attributes.length) {
    
  1975.     instance.removeAttributeNode(attributes[0]);
    
  1976.   }
    
  1977.   detachDeletedInstance(instance);
    
  1978. }
    
  1979. 
    
  1980. export function clearSingleton(instance: Instance): void {
    
  1981.   const element: Element = (instance: any);
    
  1982.   let node = element.firstChild;
    
  1983.   while (node) {
    
  1984.     const nextNode = node.nextSibling;
    
  1985.     const nodeName = node.nodeName;
    
  1986.     if (
    
  1987.       isMarkedHoistable(node) ||
    
  1988.       nodeName === 'HEAD' ||
    
  1989.       nodeName === 'BODY' ||
    
  1990.       nodeName === 'SCRIPT' ||
    
  1991.       nodeName === 'STYLE' ||
    
  1992.       (nodeName === 'LINK' &&
    
  1993.         ((node: any): HTMLLinkElement).rel.toLowerCase() === 'stylesheet')
    
  1994.     ) {
    
  1995.       // retain these nodes
    
  1996.     } else {
    
  1997.       element.removeChild(node);
    
  1998.     }
    
  1999.     node = nextNode;
    
  2000.   }
    
  2001.   return;
    
  2002. }
    
  2003. 
    
  2004. // -------------------
    
  2005. //     Resources
    
  2006. // -------------------
    
  2007. 
    
  2008. export const supportsResources = true;
    
  2009. 
    
  2010. type HoistableTagType = 'link' | 'meta' | 'title';
    
  2011. type TResource<
    
  2012.   T: 'stylesheet' | 'style' | 'script' | 'void',
    
  2013.   S: null | {...},
    
  2014. > = {
    
  2015.   type: T,
    
  2016.   instance: null | Instance,
    
  2017.   count: number,
    
  2018.   state: S,
    
  2019. };
    
  2020. type StylesheetResource = TResource<'stylesheet', StylesheetState>;
    
  2021. type StyleTagResource = TResource<'style', null>;
    
  2022. type StyleResource = StyleTagResource | StylesheetResource;
    
  2023. type ScriptResource = TResource<'script', null>;
    
  2024. type VoidResource = TResource<'void', null>;
    
  2025. export type Resource = StyleResource | ScriptResource | VoidResource;
    
  2026. 
    
  2027. type LoadingState = number;
    
  2028. const NotLoaded = /*       */ 0b000;
    
  2029. const Loaded = /*          */ 0b001;
    
  2030. const Errored = /*         */ 0b010;
    
  2031. const Settled = /*         */ 0b011;
    
  2032. const Inserted = /*        */ 0b100;
    
  2033. 
    
  2034. type StylesheetState = {
    
  2035.   loading: LoadingState,
    
  2036.   preload: null | HTMLLinkElement,
    
  2037. };
    
  2038. 
    
  2039. type StyleTagProps = {
    
  2040.   'data-href': string,
    
  2041.   'data-precedence': string,
    
  2042.   [string]: mixed,
    
  2043. };
    
  2044. type StylesheetProps = {
    
  2045.   rel: 'stylesheet',
    
  2046.   href: string,
    
  2047.   'data-precedence': string,
    
  2048.   [string]: mixed,
    
  2049. };
    
  2050. 
    
  2051. type ScriptProps = {
    
  2052.   src: string,
    
  2053.   async: true,
    
  2054.   [string]: mixed,
    
  2055. };
    
  2056. 
    
  2057. type PreloadProps = {
    
  2058.   rel: 'preload',
    
  2059.   href: ?string,
    
  2060.   [string]: mixed,
    
  2061. };
    
  2062. type PreloadModuleProps = {
    
  2063.   rel: 'modulepreload',
    
  2064.   href: string,
    
  2065.   [string]: mixed,
    
  2066. };
    
  2067. 
    
  2068. export type RootResources = {
    
  2069.   hoistableStyles: Map<string, StyleResource>,
    
  2070.   hoistableScripts: Map<string, ScriptResource>,
    
  2071. };
    
  2072. 
    
  2073. export function prepareToCommitHoistables() {
    
  2074.   tagCaches = null;
    
  2075. }
    
  2076. 
    
  2077. // global collections of Resources
    
  2078. const preloadPropsMap: Map<string, PreloadProps | PreloadModuleProps> =
    
  2079.   new Map();
    
  2080. const preconnectsSet: Set<string> = new Set();
    
  2081. 
    
  2082. export type HoistableRoot = Document | ShadowRoot;
    
  2083. 
    
  2084. // getRootNode is missing from IE and old jsdom versions
    
  2085. export function getHoistableRoot(container: Container): HoistableRoot {
    
  2086.   // $FlowFixMe[method-unbinding]
    
  2087.   return typeof container.getRootNode === 'function'
    
  2088.     ? /* $FlowFixMe[incompatible-return] Flow types this as returning a `Node`,
    
  2089.        * but it's either a `Document` or `ShadowRoot`. */
    
  2090.       container.getRootNode()
    
  2091.     : container.ownerDocument;
    
  2092. }
    
  2093. 
    
  2094. function getCurrentResourceRoot(): null | HoistableRoot {
    
  2095.   const currentContainer = getCurrentRootHostContainer();
    
  2096.   return currentContainer ? getHoistableRoot(currentContainer) : null;
    
  2097. }
    
  2098. 
    
  2099. function getDocumentFromRoot(root: HoistableRoot): Document {
    
  2100.   return root.ownerDocument || root;
    
  2101. }
    
  2102. 
    
  2103. // We want this to be the default dispatcher on ReactDOMSharedInternals but we don't want to mutate
    
  2104. // internals in Module scope. Instead we export it and Internals will import it. There is already a cycle
    
  2105. // from Internals -> ReactDOM -> HostConfig -> Internals so this doesn't introduce a new one.
    
  2106. export const ReactDOMClientDispatcher: HostDispatcher = {
    
  2107.   prefetchDNS,
    
  2108.   preconnect,
    
  2109.   preload,
    
  2110.   preloadModule,
    
  2111.   preinitStyle,
    
  2112.   preinitScript,
    
  2113.   preinitModuleScript,
    
  2114. };
    
  2115. 
    
  2116. // We expect this to get inlined. It is a function mostly to communicate the special nature of
    
  2117. // how we resolve the HoistableRoot for ReactDOM.pre*() methods. Because we support calling
    
  2118. // these methods outside of render there is no way to know which Document or ShadowRoot is 'scoped'
    
  2119. // and so we have to fall back to something universal. Currently we just refer to the global document.
    
  2120. // This is notable because nowhere else in ReactDOM do we actually reference the global document or window
    
  2121. // because we may be rendering inside an iframe.
    
  2122. function getDocumentForImperativeFloatMethods(): Document {
    
  2123.   return document;
    
  2124. }
    
  2125. 
    
  2126. function preconnectAs(
    
  2127.   rel: 'preconnect' | 'dns-prefetch',
    
  2128.   href: string,
    
  2129.   crossOrigin: ?CrossOriginEnum,
    
  2130. ) {
    
  2131.   const ownerDocument = getDocumentForImperativeFloatMethods();
    
  2132.   if (typeof href === 'string' && href) {
    
  2133.     const limitedEscapedHref =
    
  2134.       escapeSelectorAttributeValueInsideDoubleQuotes(href);
    
  2135.     let key = `link[rel="${rel}"][href="${limitedEscapedHref}"]`;
    
  2136.     if (typeof crossOrigin === 'string') {
    
  2137.       key += `[crossorigin="${crossOrigin}"]`;
    
  2138.     }
    
  2139.     if (!preconnectsSet.has(key)) {
    
  2140.       preconnectsSet.add(key);
    
  2141. 
    
  2142.       const preconnectProps = {rel, crossOrigin, href};
    
  2143.       if (null === ownerDocument.querySelector(key)) {
    
  2144.         const instance = ownerDocument.createElement('link');
    
  2145.         setInitialProperties(instance, 'link', preconnectProps);
    
  2146.         markNodeAsHoistable(instance);
    
  2147.         (ownerDocument.head: any).appendChild(instance);
    
  2148.       }
    
  2149.     }
    
  2150.   }
    
  2151. }
    
  2152. 
    
  2153. function prefetchDNS(href: string) {
    
  2154.   if (!enableFloat) {
    
  2155.     return;
    
  2156.   }
    
  2157.   preconnectAs('dns-prefetch', href, null);
    
  2158. }
    
  2159. 
    
  2160. function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) {
    
  2161.   if (!enableFloat) {
    
  2162.     return;
    
  2163.   }
    
  2164.   preconnectAs('preconnect', href, crossOrigin);
    
  2165. }
    
  2166. 
    
  2167. function preload(href: string, as: string, options?: ?PreloadImplOptions) {
    
  2168.   if (!enableFloat) {
    
  2169.     return;
    
  2170.   }
    
  2171.   const ownerDocument = getDocumentForImperativeFloatMethods();
    
  2172.   if (href && as && ownerDocument) {
    
  2173.     let preloadSelector = `link[rel="preload"][as="${escapeSelectorAttributeValueInsideDoubleQuotes(
    
  2174.       as,
    
  2175.     )}"]`;
    
  2176.     if (as === 'image') {
    
  2177.       if (options && options.imageSrcSet) {
    
  2178.         preloadSelector += `[imagesrcset="${escapeSelectorAttributeValueInsideDoubleQuotes(
    
  2179.           options.imageSrcSet,
    
  2180.         )}"]`;
    
  2181.         if (typeof options.imageSizes === 'string') {
    
  2182.           preloadSelector += `[imagesizes="${escapeSelectorAttributeValueInsideDoubleQuotes(
    
  2183.             options.imageSizes,
    
  2184.           )}"]`;
    
  2185.         }
    
  2186.       } else {
    
  2187.         preloadSelector += `[href="${escapeSelectorAttributeValueInsideDoubleQuotes(
    
  2188.           href,
    
  2189.         )}"]`;
    
  2190.       }
    
  2191.     } else {
    
  2192.       preloadSelector += `[href="${escapeSelectorAttributeValueInsideDoubleQuotes(
    
  2193.         href,
    
  2194.       )}"]`;
    
  2195.     }
    
  2196.     // Some preloads are keyed under their selector. This happens when the preload is for
    
  2197.     // an arbitrary type. Other preloads are keyed under the resource key they represent a preload for.
    
  2198.     // Here we figure out which key to use to determine if we have a preload already.
    
  2199.     let key = preloadSelector;
    
  2200.     switch (as) {
    
  2201.       case 'style':
    
  2202.         key = getStyleKey(href);
    
  2203.         break;
    
  2204.       case 'script':
    
  2205.         key = getScriptKey(href);
    
  2206.         break;
    
  2207.     }
    
  2208.     if (!preloadPropsMap.has(key)) {
    
  2209.       const preloadProps = Object.assign(
    
  2210.         ({
    
  2211.           rel: 'preload',
    
  2212.           // There is a bug in Safari where imageSrcSet is not respected on preload links
    
  2213.           // so we omit the href here if we have imageSrcSet b/c safari will load the wrong image.
    
  2214.           // This harms older browers that do not support imageSrcSet by making their preloads not work
    
  2215.           // but this population is shrinking fast and is already small so we accept this tradeoff.
    
  2216.           href:
    
  2217.             as === 'image' && options && options.imageSrcSet ? undefined : href,
    
  2218.           as,
    
  2219.         }: PreloadProps),
    
  2220.         options,
    
  2221.       );
    
  2222.       preloadPropsMap.set(key, preloadProps);
    
  2223. 
    
  2224.       if (null === ownerDocument.querySelector(preloadSelector)) {
    
  2225.         if (
    
  2226.           as === 'style' &&
    
  2227.           ownerDocument.querySelector(getStylesheetSelectorFromKey(key))
    
  2228.         ) {
    
  2229.           // We already have a stylesheet for this key. We don't need to preload it.
    
  2230.           return;
    
  2231.         } else if (
    
  2232.           as === 'script' &&
    
  2233.           ownerDocument.querySelector(getScriptSelectorFromKey(key))
    
  2234.         ) {
    
  2235.           // We already have a stylesheet for this key. We don't need to preload it.
    
  2236.           return;
    
  2237.         }
    
  2238.         const instance = ownerDocument.createElement('link');
    
  2239.         setInitialProperties(instance, 'link', preloadProps);
    
  2240.         markNodeAsHoistable(instance);
    
  2241.         (ownerDocument.head: any).appendChild(instance);
    
  2242.       }
    
  2243.     }
    
  2244.   }
    
  2245. }
    
  2246. 
    
  2247. function preloadModule(href: string, options?: ?PreloadModuleImplOptions) {
    
  2248.   if (!enableFloat) {
    
  2249.     return;
    
  2250.   }
    
  2251.   const ownerDocument = getDocumentForImperativeFloatMethods();
    
  2252.   if (href) {
    
  2253.     const as =
    
  2254.       options && typeof options.as === 'string' ? options.as : 'script';
    
  2255.     const preloadSelector = `link[rel="modulepreload"][as="${escapeSelectorAttributeValueInsideDoubleQuotes(
    
  2256.       as,
    
  2257.     )}"][href="${escapeSelectorAttributeValueInsideDoubleQuotes(href)}"]`;
    
  2258.     // Some preloads are keyed under their selector. This happens when the preload is for
    
  2259.     // an arbitrary type. Other preloads are keyed under the resource key they represent a preload for.
    
  2260.     // Here we figure out which key to use to determine if we have a preload already.
    
  2261.     let key = preloadSelector;
    
  2262.     switch (as) {
    
  2263.       case 'audioworklet':
    
  2264.       case 'paintworklet':
    
  2265.       case 'serviceworker':
    
  2266.       case 'sharedworker':
    
  2267.       case 'worker':
    
  2268.       case 'script': {
    
  2269.         key = getScriptKey(href);
    
  2270.         break;
    
  2271.       }
    
  2272.     }
    
  2273. 
    
  2274.     if (!preloadPropsMap.has(key)) {
    
  2275.       const props: PreloadModuleProps = Object.assign(
    
  2276.         ({
    
  2277.           rel: 'modulepreload',
    
  2278.           href,
    
  2279.         }: PreloadModuleProps),
    
  2280.         options,
    
  2281.       );
    
  2282.       preloadPropsMap.set(key, props);
    
  2283. 
    
  2284.       if (null === ownerDocument.querySelector(preloadSelector)) {
    
  2285.         switch (as) {
    
  2286.           case 'audioworklet':
    
  2287.           case 'paintworklet':
    
  2288.           case 'serviceworker':
    
  2289.           case 'sharedworker':
    
  2290.           case 'worker':
    
  2291.           case 'script': {
    
  2292.             if (ownerDocument.querySelector(getScriptSelectorFromKey(key))) {
    
  2293.               return;
    
  2294.             }
    
  2295.           }
    
  2296.         }
    
  2297.         const instance = ownerDocument.createElement('link');
    
  2298.         setInitialProperties(instance, 'link', props);
    
  2299.         markNodeAsHoistable(instance);
    
  2300.         (ownerDocument.head: any).appendChild(instance);
    
  2301.       }
    
  2302.     }
    
  2303.   }
    
  2304. }
    
  2305. 
    
  2306. function preinitStyle(
    
  2307.   href: string,
    
  2308.   precedence: ?string,
    
  2309.   options?: ?PreinitStyleOptions,
    
  2310. ) {
    
  2311.   if (!enableFloat) {
    
  2312.     return;
    
  2313.   }
    
  2314.   const ownerDocument = getDocumentForImperativeFloatMethods();
    
  2315. 
    
  2316.   if (href) {
    
  2317.     const styles = getResourcesFromRoot(ownerDocument).hoistableStyles;
    
  2318. 
    
  2319.     const key = getStyleKey(href);
    
  2320.     precedence = precedence || 'default';
    
  2321. 
    
  2322.     // Check if this resource already exists
    
  2323.     let resource = styles.get(key);
    
  2324.     if (resource) {
    
  2325.       // We can early return. The resource exists and there is nothing
    
  2326.       // more to do
    
  2327.       return;
    
  2328.     }
    
  2329. 
    
  2330.     const state = {
    
  2331.       loading: NotLoaded,
    
  2332.       preload: null,
    
  2333.     };
    
  2334. 
    
  2335.     // Attempt to hydrate instance from DOM
    
  2336.     let instance: null | Instance = ownerDocument.querySelector(
    
  2337.       getStylesheetSelectorFromKey(key),
    
  2338.     );
    
  2339.     if (instance) {
    
  2340.       state.loading = Loaded | Inserted;
    
  2341.     } else {
    
  2342.       // Construct a new instance and insert it
    
  2343.       const stylesheetProps = Object.assign(
    
  2344.         ({
    
  2345.           rel: 'stylesheet',
    
  2346.           href,
    
  2347.           'data-precedence': precedence,
    
  2348.         }: StylesheetProps),
    
  2349.         options,
    
  2350.       );
    
  2351.       const preloadProps = preloadPropsMap.get(key);
    
  2352.       if (preloadProps) {
    
  2353.         adoptPreloadPropsForStylesheet(stylesheetProps, preloadProps);
    
  2354.       }
    
  2355.       const link = (instance = ownerDocument.createElement('link'));
    
  2356.       markNodeAsHoistable(link);
    
  2357.       setInitialProperties(link, 'link', stylesheetProps);
    
  2358. 
    
  2359.       (link: any)._p = new Promise((resolve, reject) => {
    
  2360.         link.onload = resolve;
    
  2361.         link.onerror = reject;
    
  2362.       });
    
  2363.       link.addEventListener('load', () => {
    
  2364.         state.loading |= Loaded;
    
  2365.       });
    
  2366.       link.addEventListener('error', () => {
    
  2367.         state.loading |= Errored;
    
  2368.       });
    
  2369. 
    
  2370.       state.loading |= Inserted;
    
  2371.       insertStylesheet(instance, precedence, ownerDocument);
    
  2372.     }
    
  2373. 
    
  2374.     // Construct a Resource and cache it
    
  2375.     resource = {
    
  2376.       type: 'stylesheet',
    
  2377.       instance,
    
  2378.       count: 1,
    
  2379.       state,
    
  2380.     };
    
  2381.     styles.set(key, resource);
    
  2382.     return;
    
  2383.   }
    
  2384. }
    
  2385. 
    
  2386. function preinitScript(src: string, options?: ?PreinitScriptOptions) {
    
  2387.   if (!enableFloat) {
    
  2388.     return;
    
  2389.   }
    
  2390.   const ownerDocument = getDocumentForImperativeFloatMethods();
    
  2391. 
    
  2392.   if (src) {
    
  2393.     const scripts = getResourcesFromRoot(ownerDocument).hoistableScripts;
    
  2394. 
    
  2395.     const key = getScriptKey(src);
    
  2396. 
    
  2397.     // Check if this resource already exists
    
  2398.     let resource = scripts.get(key);
    
  2399.     if (resource) {
    
  2400.       // We can early return. The resource exists and there is nothing
    
  2401.       // more to do
    
  2402.       return;
    
  2403.     }
    
  2404. 
    
  2405.     // Attempt to hydrate instance from DOM
    
  2406.     let instance: null | Instance = ownerDocument.querySelector(
    
  2407.       getScriptSelectorFromKey(key),
    
  2408.     );
    
  2409.     if (!instance) {
    
  2410.       // Construct a new instance and insert it
    
  2411.       const scriptProps = Object.assign(
    
  2412.         ({
    
  2413.           src,
    
  2414.           async: true,
    
  2415.         }: ScriptProps),
    
  2416.         options,
    
  2417.       );
    
  2418.       // Adopt certain preload props
    
  2419.       const preloadProps = preloadPropsMap.get(key);
    
  2420.       if (preloadProps) {
    
  2421.         adoptPreloadPropsForScript(scriptProps, preloadProps);
    
  2422.       }
    
  2423.       instance = ownerDocument.createElement('script');
    
  2424.       markNodeAsHoistable(instance);
    
  2425.       setInitialProperties(instance, 'link', scriptProps);
    
  2426.       (ownerDocument.head: any).appendChild(instance);
    
  2427.     }
    
  2428. 
    
  2429.     // Construct a Resource and cache it
    
  2430.     resource = {
    
  2431.       type: 'script',
    
  2432.       instance,
    
  2433.       count: 1,
    
  2434.       state: null,
    
  2435.     };
    
  2436.     scripts.set(key, resource);
    
  2437.     return;
    
  2438.   }
    
  2439. }
    
  2440. 
    
  2441. function preinitModuleScript(
    
  2442.   src: string,
    
  2443.   options?: ?PreinitModuleScriptOptions,
    
  2444. ) {
    
  2445.   if (!enableFloat) {
    
  2446.     return;
    
  2447.   }
    
  2448.   const ownerDocument = getDocumentForImperativeFloatMethods();
    
  2449. 
    
  2450.   if (src) {
    
  2451.     const scripts = getResourcesFromRoot(ownerDocument).hoistableScripts;
    
  2452. 
    
  2453.     const key = getScriptKey(src);
    
  2454. 
    
  2455.     // Check if this resource already exists
    
  2456.     let resource = scripts.get(key);
    
  2457.     if (resource) {
    
  2458.       // We can early return. The resource exists and there is nothing
    
  2459.       // more to do
    
  2460.       return;
    
  2461.     }
    
  2462. 
    
  2463.     // Attempt to hydrate instance from DOM
    
  2464.     let instance: null | Instance = ownerDocument.querySelector(
    
  2465.       getScriptSelectorFromKey(key),
    
  2466.     );
    
  2467.     if (!instance) {
    
  2468.       // Construct a new instance and insert it
    
  2469.       const scriptProps = Object.assign(
    
  2470.         ({
    
  2471.           src,
    
  2472.           async: true,
    
  2473.           type: 'module',
    
  2474.         }: ScriptProps),
    
  2475.         options,
    
  2476.       );
    
  2477.       // Adopt certain preload props
    
  2478.       const preloadProps = preloadPropsMap.get(key);
    
  2479.       if (preloadProps) {
    
  2480.         adoptPreloadPropsForScript(scriptProps, preloadProps);
    
  2481.       }
    
  2482.       instance = ownerDocument.createElement('script');
    
  2483.       markNodeAsHoistable(instance);
    
  2484.       setInitialProperties(instance, 'link', scriptProps);
    
  2485.       (ownerDocument.head: any).appendChild(instance);
    
  2486.     }
    
  2487. 
    
  2488.     // Construct a Resource and cache it
    
  2489.     resource = {
    
  2490.       type: 'script',
    
  2491.       instance,
    
  2492.       count: 1,
    
  2493.       state: null,
    
  2494.     };
    
  2495.     scripts.set(key, resource);
    
  2496.     return;
    
  2497.   }
    
  2498. }
    
  2499. 
    
  2500. type StyleTagQualifyingProps = {
    
  2501.   href: string,
    
  2502.   precedence: string,
    
  2503.   [string]: mixed,
    
  2504. };
    
  2505. 
    
  2506. type StylesheetQualifyingProps = {
    
  2507.   rel: 'stylesheet',
    
  2508.   href: string,
    
  2509.   precedence: string,
    
  2510.   [string]: mixed,
    
  2511. };
    
  2512. 
    
  2513. // This function is called in begin work and we should always have a currentDocument set
    
  2514. export function getResource(
    
  2515.   type: string,
    
  2516.   currentProps: any,
    
  2517.   pendingProps: any,
    
  2518. ): null | Resource {
    
  2519.   const resourceRoot = getCurrentResourceRoot();
    
  2520.   if (!resourceRoot) {
    
  2521.     throw new Error(
    
  2522.       '"resourceRoot" was expected to exist. This is a bug in React.',
    
  2523.     );
    
  2524.   }
    
  2525.   switch (type) {
    
  2526.     case 'meta':
    
  2527.     case 'title': {
    
  2528.       return null;
    
  2529.     }
    
  2530.     case 'style': {
    
  2531.       if (
    
  2532.         typeof pendingProps.precedence === 'string' &&
    
  2533.         typeof pendingProps.href === 'string'
    
  2534.       ) {
    
  2535.         const key = getStyleKey(pendingProps.href);
    
  2536.         const styles = getResourcesFromRoot(resourceRoot).hoistableStyles;
    
  2537.         let resource = styles.get(key);
    
  2538.         if (!resource) {
    
  2539.           resource = {
    
  2540.             type: 'style',
    
  2541.             instance: null,
    
  2542.             count: 0,
    
  2543.             state: null,
    
  2544.           };
    
  2545.           styles.set(key, resource);
    
  2546.         }
    
  2547.         return resource;
    
  2548.       }
    
  2549.       return {
    
  2550.         type: 'void',
    
  2551.         instance: null,
    
  2552.         count: 0,
    
  2553.         state: null,
    
  2554.       };
    
  2555.     }
    
  2556.     case 'link': {
    
  2557.       if (
    
  2558.         pendingProps.rel === 'stylesheet' &&
    
  2559.         typeof pendingProps.href === 'string' &&
    
  2560.         typeof pendingProps.precedence === 'string'
    
  2561.       ) {
    
  2562.         const qualifiedProps: StylesheetQualifyingProps = pendingProps;
    
  2563.         const key = getStyleKey(qualifiedProps.href);
    
  2564. 
    
  2565.         const styles = getResourcesFromRoot(resourceRoot).hoistableStyles;
    
  2566. 
    
  2567.         let resource = styles.get(key);
    
  2568.         if (!resource) {
    
  2569.           // We asserted this above but Flow can't figure out that the type satisfies
    
  2570.           const ownerDocument = getDocumentFromRoot(resourceRoot);
    
  2571.           resource = {
    
  2572.             type: 'stylesheet',
    
  2573.             instance: null,
    
  2574.             count: 0,
    
  2575.             state: {
    
  2576.               loading: NotLoaded,
    
  2577.               preload: null,
    
  2578.             },
    
  2579.           };
    
  2580.           styles.set(key, resource);
    
  2581.           if (!preloadPropsMap.has(key)) {
    
  2582.             preloadStylesheet(
    
  2583.               ownerDocument,
    
  2584.               key,
    
  2585.               preloadPropsFromStylesheet(qualifiedProps),
    
  2586.               resource.state,
    
  2587.             );
    
  2588.           }
    
  2589.         }
    
  2590.         return resource;
    
  2591.       }
    
  2592.       return null;
    
  2593.     }
    
  2594.     case 'script': {
    
  2595.       if (typeof pendingProps.src === 'string' && pendingProps.async === true) {
    
  2596.         const scriptProps: ScriptProps = pendingProps;
    
  2597.         const key = getScriptKey(scriptProps.src);
    
  2598.         const scripts = getResourcesFromRoot(resourceRoot).hoistableScripts;
    
  2599. 
    
  2600.         let resource = scripts.get(key);
    
  2601.         if (!resource) {
    
  2602.           resource = {
    
  2603.             type: 'script',
    
  2604.             instance: null,
    
  2605.             count: 0,
    
  2606.             state: null,
    
  2607.           };
    
  2608.           scripts.set(key, resource);
    
  2609.         }
    
  2610.         return resource;
    
  2611.       }
    
  2612.       return {
    
  2613.         type: 'void',
    
  2614.         instance: null,
    
  2615.         count: 0,
    
  2616.         state: null,
    
  2617.       };
    
  2618.     }
    
  2619.     default: {
    
  2620.       throw new Error(
    
  2621.         `getResource encountered a type it did not expect: "${type}". this is a bug in React.`,
    
  2622.       );
    
  2623.     }
    
  2624.   }
    
  2625. }
    
  2626. 
    
  2627. function styleTagPropsFromRawProps(
    
  2628.   rawProps: StyleTagQualifyingProps,
    
  2629. ): StyleTagProps {
    
  2630.   return {
    
  2631.     ...rawProps,
    
  2632.     'data-href': rawProps.href,
    
  2633.     'data-precedence': rawProps.precedence,
    
  2634.     href: null,
    
  2635.     precedence: null,
    
  2636.   };
    
  2637. }
    
  2638. 
    
  2639. function getStyleKey(href: string) {
    
  2640.   const limitedEscapedHref =
    
  2641.     escapeSelectorAttributeValueInsideDoubleQuotes(href);
    
  2642.   return `href="${limitedEscapedHref}"`;
    
  2643. }
    
  2644. 
    
  2645. function getStyleTagSelector(href: string) {
    
  2646.   const limitedEscapedHref =
    
  2647.     escapeSelectorAttributeValueInsideDoubleQuotes(href);
    
  2648.   return `style[data-href~="${limitedEscapedHref}"]`;
    
  2649. }
    
  2650. 
    
  2651. function getStylesheetSelectorFromKey(key: string) {
    
  2652.   return `link[rel="stylesheet"][${key}]`;
    
  2653. }
    
  2654. 
    
  2655. function getPreloadStylesheetSelectorFromKey(key: string) {
    
  2656.   return `link[rel="preload"][as="style"][${key}]`;
    
  2657. }
    
  2658. 
    
  2659. function stylesheetPropsFromRawProps(
    
  2660.   rawProps: StylesheetQualifyingProps,
    
  2661. ): StylesheetProps {
    
  2662.   return {
    
  2663.     ...rawProps,
    
  2664.     'data-precedence': rawProps.precedence,
    
  2665.     precedence: null,
    
  2666.   };
    
  2667. }
    
  2668. function preloadStylesheet(
    
  2669.   ownerDocument: Document,
    
  2670.   key: string,
    
  2671.   preloadProps: PreloadProps,
    
  2672.   state: StylesheetState,
    
  2673. ) {
    
  2674.   preloadPropsMap.set(key, preloadProps);
    
  2675. 
    
  2676.   if (!ownerDocument.querySelector(getStylesheetSelectorFromKey(key))) {
    
  2677.     // There is no matching stylesheet instance in the Document.
    
  2678.     // We will insert a preload now to kick off loading because
    
  2679.     // we expect this stylesheet to commit
    
  2680.     const preloadEl = ownerDocument.querySelector(
    
  2681.       getPreloadStylesheetSelectorFromKey(key),
    
  2682.     );
    
  2683.     if (preloadEl) {
    
  2684.       // If we find a preload already it was SSR'd and we won't have an actual
    
  2685.       // loading state to track. For now we will just assume it is loaded
    
  2686.       state.loading = Loaded;
    
  2687.     } else {
    
  2688.       const instance = ownerDocument.createElement('link');
    
  2689.       state.preload = instance;
    
  2690.       instance.addEventListener('load', () => (state.loading |= Loaded));
    
  2691.       instance.addEventListener('error', () => (state.loading |= Errored));
    
  2692.       setInitialProperties(instance, 'link', preloadProps);
    
  2693.       markNodeAsHoistable(instance);
    
  2694.       (ownerDocument.head: any).appendChild(instance);
    
  2695.     }
    
  2696.   }
    
  2697. }
    
  2698. 
    
  2699. function preloadPropsFromStylesheet(
    
  2700.   props: StylesheetQualifyingProps,
    
  2701. ): PreloadProps {
    
  2702.   return {
    
  2703.     rel: 'preload',
    
  2704.     as: 'style',
    
  2705.     href: props.href,
    
  2706.     crossOrigin: props.crossOrigin,
    
  2707.     integrity: props.integrity,
    
  2708.     media: props.media,
    
  2709.     hrefLang: props.hrefLang,
    
  2710.     referrerPolicy: props.referrerPolicy,
    
  2711.   };
    
  2712. }
    
  2713. 
    
  2714. function getScriptKey(src: string): string {
    
  2715.   const limitedEscapedSrc = escapeSelectorAttributeValueInsideDoubleQuotes(src);
    
  2716.   return `[src="${limitedEscapedSrc}"]`;
    
  2717. }
    
  2718. 
    
  2719. function getScriptSelectorFromKey(key: string): string {
    
  2720.   return 'script[async]' + key;
    
  2721. }
    
  2722. 
    
  2723. export function acquireResource(
    
  2724.   hoistableRoot: HoistableRoot,
    
  2725.   resource: Resource,
    
  2726.   props: any,
    
  2727. ): null | Instance {
    
  2728.   resource.count++;
    
  2729.   if (resource.instance === null) {
    
  2730.     switch (resource.type) {
    
  2731.       case 'style': {
    
  2732.         const qualifiedProps: StyleTagQualifyingProps = props;
    
  2733. 
    
  2734.         // Attempt to hydrate instance from DOM
    
  2735.         let instance: null | Instance = hoistableRoot.querySelector(
    
  2736.           getStyleTagSelector(qualifiedProps.href),
    
  2737.         );
    
  2738.         if (instance) {
    
  2739.           resource.instance = instance;
    
  2740.           markNodeAsHoistable(instance);
    
  2741.           return instance;
    
  2742.         }
    
  2743. 
    
  2744.         const styleProps = styleTagPropsFromRawProps(props);
    
  2745.         const ownerDocument = getDocumentFromRoot(hoistableRoot);
    
  2746.         instance = ownerDocument.createElement('style');
    
  2747. 
    
  2748.         markNodeAsHoistable(instance);
    
  2749.         setInitialProperties(instance, 'style', styleProps);
    
  2750. 
    
  2751.         // TODO: `style` does not have loading state for tracking insertions. I
    
  2752.         // guess because these aren't suspensey? Not sure whether this is a
    
  2753.         // factoring smell.
    
  2754.         // resource.state.loading |= Inserted;
    
  2755.         insertStylesheet(instance, qualifiedProps.precedence, hoistableRoot);
    
  2756.         resource.instance = instance;
    
  2757. 
    
  2758.         return instance;
    
  2759.       }
    
  2760.       case 'stylesheet': {
    
  2761.         // This typing is enforce by `getResource`. If we change the logic
    
  2762.         // there for what qualifies as a stylesheet resource we need to ensure
    
  2763.         // this cast still makes sense;
    
  2764.         const qualifiedProps: StylesheetQualifyingProps = props;
    
  2765.         const key = getStyleKey(qualifiedProps.href);
    
  2766. 
    
  2767.         // Attempt to hydrate instance from DOM
    
  2768.         let instance: null | Instance = hoistableRoot.querySelector(
    
  2769.           getStylesheetSelectorFromKey(key),
    
  2770.         );
    
  2771.         if (instance) {
    
  2772.           resource.state.loading |= Inserted;
    
  2773.           resource.instance = instance;
    
  2774.           markNodeAsHoistable(instance);
    
  2775.           return instance;
    
  2776.         }
    
  2777. 
    
  2778.         const stylesheetProps = stylesheetPropsFromRawProps(props);
    
  2779.         const preloadProps = preloadPropsMap.get(key);
    
  2780.         if (preloadProps) {
    
  2781.           adoptPreloadPropsForStylesheet(stylesheetProps, preloadProps);
    
  2782.         }
    
  2783. 
    
  2784.         // Construct and insert a new instance
    
  2785.         const ownerDocument = getDocumentFromRoot(hoistableRoot);
    
  2786.         instance = ownerDocument.createElement('link');
    
  2787.         markNodeAsHoistable(instance);
    
  2788.         const linkInstance: HTMLLinkElement = (instance: any);
    
  2789.         (linkInstance: any)._p = new Promise((resolve, reject) => {
    
  2790.           linkInstance.onload = resolve;
    
  2791.           linkInstance.onerror = reject;
    
  2792.         });
    
  2793.         setInitialProperties(instance, 'link', stylesheetProps);
    
  2794.         resource.state.loading |= Inserted;
    
  2795.         insertStylesheet(instance, qualifiedProps.precedence, hoistableRoot);
    
  2796.         resource.instance = instance;
    
  2797. 
    
  2798.         return instance;
    
  2799.       }
    
  2800.       case 'script': {
    
  2801.         // This typing is enforce by `getResource`. If we change the logic
    
  2802.         // there for what qualifies as a stylesheet resource we need to ensure
    
  2803.         // this cast still makes sense;
    
  2804.         const borrowedScriptProps: ScriptProps = props;
    
  2805.         const key = getScriptKey(borrowedScriptProps.src);
    
  2806. 
    
  2807.         // Attempt to hydrate instance from DOM
    
  2808.         let instance: null | Instance = hoistableRoot.querySelector(
    
  2809.           getScriptSelectorFromKey(key),
    
  2810.         );
    
  2811.         if (instance) {
    
  2812.           resource.instance = instance;
    
  2813.           markNodeAsHoistable(instance);
    
  2814.           return instance;
    
  2815.         }
    
  2816. 
    
  2817.         let scriptProps = borrowedScriptProps;
    
  2818.         const preloadProps = preloadPropsMap.get(key);
    
  2819.         if (preloadProps) {
    
  2820.           scriptProps = {...borrowedScriptProps};
    
  2821.           adoptPreloadPropsForScript(scriptProps, preloadProps);
    
  2822.         }
    
  2823. 
    
  2824.         // Construct and insert a new instance
    
  2825.         const ownerDocument = getDocumentFromRoot(hoistableRoot);
    
  2826.         instance = ownerDocument.createElement('script');
    
  2827.         markNodeAsHoistable(instance);
    
  2828.         setInitialProperties(instance, 'link', scriptProps);
    
  2829.         (ownerDocument.head: any).appendChild(instance);
    
  2830.         resource.instance = instance;
    
  2831. 
    
  2832.         return instance;
    
  2833.       }
    
  2834.       case 'void': {
    
  2835.         return null;
    
  2836.       }
    
  2837.       default: {
    
  2838.         throw new Error(
    
  2839.           `acquireResource encountered a resource type it did not expect: "${resource.type}". this is a bug in React.`,
    
  2840.         );
    
  2841.       }
    
  2842.     }
    
  2843.   } else {
    
  2844.     // In the case of stylesheets, they might have already been assigned an
    
  2845.     // instance during `suspendResource`. But that doesn't mean they were
    
  2846.     // inserted, because the commit might have been interrupted. So we need to
    
  2847.     // check now.
    
  2848.     //
    
  2849.     // The other resource types are unaffected because they are not
    
  2850.     // yet suspensey.
    
  2851.     //
    
  2852.     // TODO: This is a bit of a code smell. Consider refactoring how
    
  2853.     // `suspendResource` and `acquireResource` work together. The idea is that
    
  2854.     // `suspendResource` does all the same stuff as `acquireResource` except
    
  2855.     // for the insertion.
    
  2856.     if (
    
  2857.       resource.type === 'stylesheet' &&
    
  2858.       (resource.state.loading & Inserted) === NotLoaded
    
  2859.     ) {
    
  2860.       const qualifiedProps: StylesheetQualifyingProps = props;
    
  2861.       const instance: Instance = resource.instance;
    
  2862.       resource.state.loading |= Inserted;
    
  2863.       insertStylesheet(instance, qualifiedProps.precedence, hoistableRoot);
    
  2864.     }
    
  2865.   }
    
  2866.   return resource.instance;
    
  2867. }
    
  2868. 
    
  2869. export function releaseResource(resource: Resource): void {
    
  2870.   resource.count--;
    
  2871. }
    
  2872. 
    
  2873. function insertStylesheet(
    
  2874.   instance: Element,
    
  2875.   precedence: string,
    
  2876.   root: HoistableRoot,
    
  2877. ): void {
    
  2878.   const nodes = root.querySelectorAll(
    
  2879.     'link[rel="stylesheet"][data-precedence],style[data-precedence]',
    
  2880.   );
    
  2881.   const last = nodes.length ? nodes[nodes.length - 1] : null;
    
  2882.   let prior = last;
    
  2883.   for (let i = 0; i < nodes.length; i++) {
    
  2884.     const node = nodes[i];
    
  2885.     const nodePrecedence = node.dataset.precedence;
    
  2886.     if (nodePrecedence === precedence) {
    
  2887.       prior = node;
    
  2888.     } else if (prior !== last) {
    
  2889.       break;
    
  2890.     }
    
  2891.   }
    
  2892. 
    
  2893.   if (prior) {
    
  2894.     // We get the prior from the document so we know it is in the tree.
    
  2895.     // We also know that links can't be the topmost Node so the parentNode
    
  2896.     // must exist.
    
  2897.     ((prior.parentNode: any): Node).insertBefore(instance, prior.nextSibling);
    
  2898.   } else {
    
  2899.     const parent =
    
  2900.       root.nodeType === DOCUMENT_NODE
    
  2901.         ? ((((root: any): Document).head: any): Element)
    
  2902.         : ((root: any): ShadowRoot);
    
  2903.     parent.insertBefore(instance, parent.firstChild);
    
  2904.   }
    
  2905. }
    
  2906. 
    
  2907. function adoptPreloadPropsForStylesheet(
    
  2908.   stylesheetProps: StylesheetProps,
    
  2909.   preloadProps: PreloadProps | PreloadModuleProps,
    
  2910. ): void {
    
  2911.   if (stylesheetProps.crossOrigin == null)
    
  2912.     stylesheetProps.crossOrigin = preloadProps.crossOrigin;
    
  2913.   if (stylesheetProps.referrerPolicy == null)
    
  2914.     stylesheetProps.referrerPolicy = preloadProps.referrerPolicy;
    
  2915.   if (stylesheetProps.title == null) stylesheetProps.title = preloadProps.title;
    
  2916. }
    
  2917. 
    
  2918. function adoptPreloadPropsForScript(
    
  2919.   scriptProps: ScriptProps,
    
  2920.   preloadProps: PreloadProps | PreloadModuleProps,
    
  2921. ): void {
    
  2922.   if (scriptProps.crossOrigin == null)
    
  2923.     scriptProps.crossOrigin = preloadProps.crossOrigin;
    
  2924.   if (scriptProps.referrerPolicy == null)
    
  2925.     scriptProps.referrerPolicy = preloadProps.referrerPolicy;
    
  2926.   if (scriptProps.integrity == null)
    
  2927.     scriptProps.integrity = preloadProps.integrity;
    
  2928. }
    
  2929. 
    
  2930. type KeyedTagCache = Map<string, Array<Element>>;
    
  2931. type DocumentTagCaches = Map<Document, KeyedTagCache>;
    
  2932. let tagCaches: null | DocumentTagCaches = null;
    
  2933. 
    
  2934. export function hydrateHoistable(
    
  2935.   hoistableRoot: HoistableRoot,
    
  2936.   type: HoistableTagType,
    
  2937.   props: any,
    
  2938.   internalInstanceHandle: Object,
    
  2939. ): Instance {
    
  2940.   const ownerDocument = getDocumentFromRoot(hoistableRoot);
    
  2941. 
    
  2942.   let instance: ?Instance = null;
    
  2943.   getInstance: switch (type) {
    
  2944.     case 'title': {
    
  2945.       instance = ownerDocument.getElementsByTagName('title')[0];
    
  2946.       if (
    
  2947.         !instance ||
    
  2948.         isOwnedInstance(instance) ||
    
  2949.         instance.namespaceURI === SVG_NAMESPACE ||
    
  2950.         instance.hasAttribute('itemprop')
    
  2951.       ) {
    
  2952.         instance = ownerDocument.createElement(type);
    
  2953.         (ownerDocument.head: any).insertBefore(
    
  2954.           instance,
    
  2955.           ownerDocument.querySelector('head > title'),
    
  2956.         );
    
  2957.       }
    
  2958.       setInitialProperties(instance, type, props);
    
  2959.       precacheFiberNode(internalInstanceHandle, instance);
    
  2960.       markNodeAsHoistable(instance);
    
  2961.       return instance;
    
  2962.     }
    
  2963.     case 'link': {
    
  2964.       const cache = getHydratableHoistableCache('link', 'href', ownerDocument);
    
  2965.       const key = type + (props.href || '');
    
  2966.       const maybeNodes = cache.get(key);
    
  2967.       if (maybeNodes) {
    
  2968.         const nodes = maybeNodes;
    
  2969.         for (let i = 0; i < nodes.length; i++) {
    
  2970.           const node = nodes[i];
    
  2971.           if (
    
  2972.             node.getAttribute('href') !==
    
  2973.               (props.href == null ? null : props.href) ||
    
  2974.             node.getAttribute('rel') !==
    
  2975.               (props.rel == null ? null : props.rel) ||
    
  2976.             node.getAttribute('title') !==
    
  2977.               (props.title == null ? null : props.title) ||
    
  2978.             node.getAttribute('crossorigin') !==
    
  2979.               (props.crossOrigin == null ? null : props.crossOrigin)
    
  2980.           ) {
    
  2981.             // mismatch, try the next node;
    
  2982.             continue;
    
  2983.           }
    
  2984.           instance = node;
    
  2985.           nodes.splice(i, 1);
    
  2986.           break getInstance;
    
  2987.         }
    
  2988.       }
    
  2989.       instance = ownerDocument.createElement(type);
    
  2990.       setInitialProperties(instance, type, props);
    
  2991.       (ownerDocument.head: any).appendChild(instance);
    
  2992.       break;
    
  2993.     }
    
  2994.     case 'meta': {
    
  2995.       const cache = getHydratableHoistableCache(
    
  2996.         'meta',
    
  2997.         'content',
    
  2998.         ownerDocument,
    
  2999.       );
    
  3000.       const key = type + (props.content || '');
    
  3001.       const maybeNodes = cache.get(key);
    
  3002.       if (maybeNodes) {
    
  3003.         const nodes = maybeNodes;
    
  3004.         for (let i = 0; i < nodes.length; i++) {
    
  3005.           const node = nodes[i];
    
  3006. 
    
  3007.           // We coerce content to string because it is the most likely one to
    
  3008.           // use a `toString` capable value. For the rest we just do identity match
    
  3009.           // passing non-strings here is not really valid anyway.
    
  3010.           if (__DEV__) {
    
  3011.             checkAttributeStringCoercion(props.content, 'content');
    
  3012.           }
    
  3013.           if (
    
  3014.             node.getAttribute('content') !==
    
  3015.               (props.content == null ? null : '' + props.content) ||
    
  3016.             node.getAttribute('name') !==
    
  3017.               (props.name == null ? null : props.name) ||
    
  3018.             node.getAttribute('property') !==
    
  3019.               (props.property == null ? null : props.property) ||
    
  3020.             node.getAttribute('http-equiv') !==
    
  3021.               (props.httpEquiv == null ? null : props.httpEquiv) ||
    
  3022.             node.getAttribute('charset') !==
    
  3023.               (props.charSet == null ? null : props.charSet)
    
  3024.           ) {
    
  3025.             // mismatch, try the next node;
    
  3026.             continue;
    
  3027.           }
    
  3028.           instance = node;
    
  3029.           nodes.splice(i, 1);
    
  3030.           break getInstance;
    
  3031.         }
    
  3032.       }
    
  3033.       instance = ownerDocument.createElement(type);
    
  3034.       setInitialProperties(instance, type, props);
    
  3035.       (ownerDocument.head: any).appendChild(instance);
    
  3036.       break;
    
  3037.     }
    
  3038.     default:
    
  3039.       throw new Error(
    
  3040.         `getNodesForType encountered a type it did not expect: "${type}". This is a bug in React.`,
    
  3041.       );
    
  3042.   }
    
  3043. 
    
  3044.   // This node is a match
    
  3045.   precacheFiberNode(internalInstanceHandle, instance);
    
  3046.   markNodeAsHoistable(instance);
    
  3047.   return instance;
    
  3048. }
    
  3049. 
    
  3050. function getHydratableHoistableCache(
    
  3051.   type: HoistableTagType,
    
  3052.   keyAttribute: string,
    
  3053.   ownerDocument: Document,
    
  3054. ): KeyedTagCache {
    
  3055.   let cache: KeyedTagCache;
    
  3056.   let caches: DocumentTagCaches;
    
  3057.   if (tagCaches === null) {
    
  3058.     cache = new Map();
    
  3059.     caches = tagCaches = new Map();
    
  3060.     caches.set(ownerDocument, cache);
    
  3061.   } else {
    
  3062.     caches = tagCaches;
    
  3063.     const maybeCache = caches.get(ownerDocument);
    
  3064.     if (!maybeCache) {
    
  3065.       cache = new Map();
    
  3066.       caches.set(ownerDocument, cache);
    
  3067.     } else {
    
  3068.       cache = maybeCache;
    
  3069.     }
    
  3070.   }
    
  3071. 
    
  3072.   if (cache.has(type)) {
    
  3073.     // We use type as a special key that signals that this cache has been seeded for this type
    
  3074.     return cache;
    
  3075.   }
    
  3076. 
    
  3077.   // Mark this cache as seeded for this type
    
  3078.   cache.set(type, (null: any));
    
  3079. 
    
  3080.   const nodes = ownerDocument.getElementsByTagName(type);
    
  3081.   for (let i = 0; i < nodes.length; i++) {
    
  3082.     const node = nodes[i];
    
  3083.     if (
    
  3084.       !isOwnedInstance(node) &&
    
  3085.       (type !== 'link' || node.getAttribute('rel') !== 'stylesheet') &&
    
  3086.       node.namespaceURI !== SVG_NAMESPACE
    
  3087.     ) {
    
  3088.       const nodeKey = node.getAttribute(keyAttribute) || '';
    
  3089.       const key = type + nodeKey;
    
  3090.       const existing = cache.get(key);
    
  3091.       if (existing) {
    
  3092.         existing.push(node);
    
  3093.       } else {
    
  3094.         cache.set(key, [node]);
    
  3095.       }
    
  3096.     }
    
  3097.   }
    
  3098. 
    
  3099.   return cache;
    
  3100. }
    
  3101. 
    
  3102. export function mountHoistable(
    
  3103.   hoistableRoot: HoistableRoot,
    
  3104.   type: HoistableTagType,
    
  3105.   instance: Instance,
    
  3106. ): void {
    
  3107.   const ownerDocument = getDocumentFromRoot(hoistableRoot);
    
  3108.   (ownerDocument.head: any).insertBefore(
    
  3109.     instance,
    
  3110.     type === 'title' ? ownerDocument.querySelector('head > title') : null,
    
  3111.   );
    
  3112. }
    
  3113. 
    
  3114. export function unmountHoistable(instance: Instance): void {
    
  3115.   (instance.parentNode: any).removeChild(instance);
    
  3116. }
    
  3117. 
    
  3118. export function isHostHoistableType(
    
  3119.   type: string,
    
  3120.   props: RawProps,
    
  3121.   hostContext: HostContext,
    
  3122. ): boolean {
    
  3123.   let outsideHostContainerContext: boolean;
    
  3124.   let hostContextProd: HostContextProd;
    
  3125.   if (__DEV__) {
    
  3126.     const hostContextDev: HostContextDev = (hostContext: any);
    
  3127.     // We can only render resources when we are not within the host container context
    
  3128.     outsideHostContainerContext =
    
  3129.       !hostContextDev.ancestorInfo.containerTagInScope;
    
  3130.     hostContextProd = hostContextDev.context;
    
  3131.   } else {
    
  3132.     hostContextProd = (hostContext: any);
    
  3133.   }
    
  3134. 
    
  3135.   // Global opt out of hoisting for anything in SVG Namespace or anything with an itemProp inside an itemScope
    
  3136.   if (hostContextProd === HostContextNamespaceSvg || props.itemProp != null) {
    
  3137.     if (__DEV__) {
    
  3138.       if (
    
  3139.         outsideHostContainerContext &&
    
  3140.         props.itemProp != null &&
    
  3141.         (type === 'meta' ||
    
  3142.           type === 'title' ||
    
  3143.           type === 'style' ||
    
  3144.           type === 'link' ||
    
  3145.           type === 'script')
    
  3146.       ) {
    
  3147.         console.error(
    
  3148.           'Cannot render a <%s> outside the main document if it has an `itemProp` prop. `itemProp` suggests the tag belongs to an' +
    
  3149.             ' `itemScope` which can appear anywhere in the DOM. If you were intending for React to hoist this <%s> remove the `itemProp` prop.' +
    
  3150.             ' Otherwise, try moving this tag into the <head> or <body> of the Document.',
    
  3151.           type,
    
  3152.           type,
    
  3153.         );
    
  3154.       }
    
  3155.     }
    
  3156.     return false;
    
  3157.   }
    
  3158. 
    
  3159.   switch (type) {
    
  3160.     case 'meta':
    
  3161.     case 'title': {
    
  3162.       return true;
    
  3163.     }
    
  3164.     case 'style': {
    
  3165.       if (
    
  3166.         typeof props.precedence !== 'string' ||
    
  3167.         typeof props.href !== 'string' ||
    
  3168.         props.href === ''
    
  3169.       ) {
    
  3170.         if (__DEV__) {
    
  3171.           if (outsideHostContainerContext) {
    
  3172.             console.error(
    
  3173.               'Cannot render a <style> outside the main document without knowing its precedence and a unique href key.' +
    
  3174.                 ' React can hoist and deduplicate <style> tags if you provide a `precedence` prop along with an `href` prop that' +
    
  3175.                 ' does not conflic with the `href` values used in any other hoisted <style> or <link rel="stylesheet" ...> tags. ' +
    
  3176.                 ' Note that hoisting <style> tags is considered an advanced feature that most will not use directly.' +
    
  3177.                 ' Consider moving the <style> tag to the <head> or consider adding a `precedence="default"` and `href="some unique resource identifier"`, or move the <style>' +
    
  3178.                 ' to the <style> tag.',
    
  3179.             );
    
  3180.           }
    
  3181.         }
    
  3182.         return false;
    
  3183.       }
    
  3184.       return true;
    
  3185.     }
    
  3186.     case 'link': {
    
  3187.       if (
    
  3188.         typeof props.rel !== 'string' ||
    
  3189.         typeof props.href !== 'string' ||
    
  3190.         props.href === '' ||
    
  3191.         props.onLoad ||
    
  3192.         props.onError
    
  3193.       ) {
    
  3194.         if (__DEV__) {
    
  3195.           if (
    
  3196.             props.rel === 'stylesheet' &&
    
  3197.             typeof props.precedence === 'string'
    
  3198.           ) {
    
  3199.             validateLinkPropsForStyleResource(props);
    
  3200.           }
    
  3201.           if (outsideHostContainerContext) {
    
  3202.             if (
    
  3203.               typeof props.rel !== 'string' ||
    
  3204.               typeof props.href !== 'string' ||
    
  3205.               props.href === ''
    
  3206.             ) {
    
  3207.               console.error(
    
  3208.                 'Cannot render a <link> outside the main document without a `rel` and `href` prop.' +
    
  3209.                   ' Try adding a `rel` and/or `href` prop to this <link> or moving the link into the <head> tag',
    
  3210.               );
    
  3211.             } else if (props.onError || props.onLoad) {
    
  3212.               console.error(
    
  3213.                 'Cannot render a <link> with onLoad or onError listeners outside the main document.' +
    
  3214.                   ' Try removing onLoad={...} and onError={...} or moving it into the root <head> tag or' +
    
  3215.                   ' somewhere in the <body>.',
    
  3216.               );
    
  3217.             }
    
  3218.           }
    
  3219.         }
    
  3220.         return false;
    
  3221.       }
    
  3222.       switch (props.rel) {
    
  3223.         case 'stylesheet': {
    
  3224.           const {precedence, disabled} = props;
    
  3225.           if (__DEV__) {
    
  3226.             if (typeof precedence !== 'string') {
    
  3227.               if (outsideHostContainerContext) {
    
  3228.                 console.error(
    
  3229.                   'Cannot render a <link rel="stylesheet" /> outside the main document without knowing its precedence.' +
    
  3230.                     ' Consider adding precedence="default" or moving it into the root <head> tag.',
    
  3231.                 );
    
  3232.               }
    
  3233.             }
    
  3234.           }
    
  3235.           return typeof precedence === 'string' && disabled == null;
    
  3236.         }
    
  3237.         default: {
    
  3238.           return true;
    
  3239.         }
    
  3240.       }
    
  3241.     }
    
  3242.     case 'script': {
    
  3243.       if (
    
  3244.         props.async !== true ||
    
  3245.         props.onLoad ||
    
  3246.         props.onError ||
    
  3247.         typeof props.src !== 'string' ||
    
  3248.         !props.src
    
  3249.       ) {
    
  3250.         if (__DEV__) {
    
  3251.           if (outsideHostContainerContext) {
    
  3252.             if (props.async !== true) {
    
  3253.               console.error(
    
  3254.                 'Cannot render a sync or defer <script> outside the main document without knowing its order.' +
    
  3255.                   ' Try adding async="" or moving it into the root <head> tag.',
    
  3256.               );
    
  3257.             } else if (props.onLoad || props.onError) {
    
  3258.               console.error(
    
  3259.                 'Cannot render a <script> with onLoad or onError listeners outside the main document.' +
    
  3260.                   ' Try removing onLoad={...} and onError={...} or moving it into the root <head> tag or' +
    
  3261.                   ' somewhere in the <body>.',
    
  3262.               );
    
  3263.             } else {
    
  3264.               console.error(
    
  3265.                 'Cannot render a <script> outside the main document without `async={true}` and a non-empty `src` prop.' +
    
  3266.                   ' Ensure there is a valid `src` and either make the script async or move it into the root <head> tag or' +
    
  3267.                   ' somewhere in the <body>.',
    
  3268.               );
    
  3269.             }
    
  3270.           }
    
  3271.         }
    
  3272.         return false;
    
  3273.       }
    
  3274.       return true;
    
  3275.     }
    
  3276.     case 'noscript':
    
  3277.     case 'template': {
    
  3278.       if (__DEV__) {
    
  3279.         if (outsideHostContainerContext) {
    
  3280.           console.error(
    
  3281.             'Cannot render <%s> outside the main document. Try moving it into the root <head> tag.',
    
  3282.             type,
    
  3283.           );
    
  3284.         }
    
  3285.       }
    
  3286.       return false;
    
  3287.     }
    
  3288.   }
    
  3289.   return false;
    
  3290. }
    
  3291. 
    
  3292. export function maySuspendCommit(type: Type, props: Props): boolean {
    
  3293.   return false;
    
  3294. }
    
  3295. 
    
  3296. export function mayResourceSuspendCommit(resource: Resource): boolean {
    
  3297.   return (
    
  3298.     resource.type === 'stylesheet' &&
    
  3299.     (resource.state.loading & Inserted) === NotLoaded
    
  3300.   );
    
  3301. }
    
  3302. 
    
  3303. export function preloadInstance(type: Type, props: Props): boolean {
    
  3304.   // Return true to indicate it's already loaded
    
  3305.   return true;
    
  3306. }
    
  3307. 
    
  3308. export function preloadResource(resource: Resource): boolean {
    
  3309.   if (
    
  3310.     resource.type === 'stylesheet' &&
    
  3311.     (resource.state.loading & Settled) === NotLoaded
    
  3312.   ) {
    
  3313.     // we have not finished loading the underlying stylesheet yet.
    
  3314.     return false;
    
  3315.   }
    
  3316.   // Return true to indicate it's already loaded
    
  3317.   return true;
    
  3318. }
    
  3319. 
    
  3320. type SuspendedState = {
    
  3321.   stylesheets: null | Map<StylesheetResource, HoistableRoot>,
    
  3322.   count: number,
    
  3323.   unsuspend: null | (() => void),
    
  3324. };
    
  3325. let suspendedState: null | SuspendedState = null;
    
  3326. 
    
  3327. // We use a noop function when we begin suspending because if possible we want the
    
  3328. // waitfor step to finish synchronously. If it doesn't we'll return a function to
    
  3329. // provide the actual unsuspend function and that will get completed when the count
    
  3330. // hits zero or it will get cancelled if the root starts new work.
    
  3331. function noop() {}
    
  3332. 
    
  3333. export function startSuspendingCommit(): void {
    
  3334.   suspendedState = {
    
  3335.     stylesheets: null,
    
  3336.     count: 0,
    
  3337.     unsuspend: noop,
    
  3338.   };
    
  3339. }
    
  3340. 
    
  3341. export function suspendInstance(type: Type, props: Props): void {
    
  3342.   return;
    
  3343. }
    
  3344. 
    
  3345. export function suspendResource(
    
  3346.   hoistableRoot: HoistableRoot,
    
  3347.   resource: Resource,
    
  3348.   props: any,
    
  3349. ): void {
    
  3350.   if (suspendedState === null) {
    
  3351.     throw new Error(
    
  3352.       'Internal React Error: suspendedState null when it was expected to exists. Please report this as a React bug.',
    
  3353.     );
    
  3354.   }
    
  3355.   const state = suspendedState;
    
  3356.   if (resource.type === 'stylesheet') {
    
  3357.     if (typeof props.media === 'string') {
    
  3358.       // If we don't currently match media we avoid suspending on this resource
    
  3359.       // and let it insert on the mutation path
    
  3360.       if (matchMedia(props.media).matches === false) {
    
  3361.         return;
    
  3362.       }
    
  3363.     }
    
  3364.     if ((resource.state.loading & Inserted) === NotLoaded) {
    
  3365.       if (resource.instance === null) {
    
  3366.         const qualifiedProps: StylesheetQualifyingProps = props;
    
  3367.         const key = getStyleKey(qualifiedProps.href);
    
  3368. 
    
  3369.         // Attempt to hydrate instance from DOM
    
  3370.         let instance: null | Instance = hoistableRoot.querySelector(
    
  3371.           getStylesheetSelectorFromKey(key),
    
  3372.         );
    
  3373.         if (instance) {
    
  3374.           // If this instance has a loading state it came from the Fizz runtime.
    
  3375.           // If there is not loading state it is assumed to have been server rendered
    
  3376.           // as part of the preamble and therefore synchronously loaded. It could have
    
  3377.           // errored however which we still do not yet have a means to detect. For now
    
  3378.           // we assume it is loaded.
    
  3379.           const maybeLoadingState: ?Promise<mixed> = (instance: any)._p;
    
  3380.           if (
    
  3381.             maybeLoadingState !== null &&
    
  3382.             typeof maybeLoadingState === 'object' &&
    
  3383.             // $FlowFixMe[method-unbinding]
    
  3384.             typeof maybeLoadingState.then === 'function'
    
  3385.           ) {
    
  3386.             const loadingState = maybeLoadingState;
    
  3387.             state.count++;
    
  3388.             const ping = onUnsuspend.bind(state);
    
  3389.             loadingState.then(ping, ping);
    
  3390.           }
    
  3391.           resource.state.loading |= Inserted;
    
  3392.           resource.instance = instance;
    
  3393.           markNodeAsHoistable(instance);
    
  3394.           return;
    
  3395.         }
    
  3396. 
    
  3397.         const ownerDocument = getDocumentFromRoot(hoistableRoot);
    
  3398. 
    
  3399.         const stylesheetProps = stylesheetPropsFromRawProps(props);
    
  3400.         const preloadProps = preloadPropsMap.get(key);
    
  3401.         if (preloadProps) {
    
  3402.           adoptPreloadPropsForStylesheet(stylesheetProps, preloadProps);
    
  3403.         }
    
  3404. 
    
  3405.         // Construct and insert a new instance
    
  3406.         instance = ownerDocument.createElement('link');
    
  3407.         markNodeAsHoistable(instance);
    
  3408.         const linkInstance: HTMLLinkElement = (instance: any);
    
  3409.         // This Promise is a loading state used by the Fizz runtime. We need this incase there is a race
    
  3410.         // between this resource being rendered on the client and being rendered with a late completed boundary.
    
  3411.         (linkInstance: any)._p = new Promise((resolve, reject) => {
    
  3412.           linkInstance.onload = resolve;
    
  3413.           linkInstance.onerror = reject;
    
  3414.         });
    
  3415.         setInitialProperties(instance, 'link', stylesheetProps);
    
  3416.         resource.instance = instance;
    
  3417.       }
    
  3418. 
    
  3419.       if (state.stylesheets === null) {
    
  3420.         state.stylesheets = new Map();
    
  3421.       }
    
  3422.       state.stylesheets.set(resource, hoistableRoot);
    
  3423. 
    
  3424.       const preloadEl = resource.state.preload;
    
  3425.       if (preloadEl && (resource.state.loading & Settled) === NotLoaded) {
    
  3426.         state.count++;
    
  3427.         const ping = onUnsuspend.bind(state);
    
  3428.         preloadEl.addEventListener('load', ping);
    
  3429.         preloadEl.addEventListener('error', ping);
    
  3430.       }
    
  3431.     }
    
  3432.   }
    
  3433. }
    
  3434. 
    
  3435. export function waitForCommitToBeReady(): null | (Function => Function) {
    
  3436.   if (suspendedState === null) {
    
  3437.     throw new Error(
    
  3438.       'Internal React Error: suspendedState null when it was expected to exists. Please report this as a React bug.',
    
  3439.     );
    
  3440.   }
    
  3441. 
    
  3442.   const state = suspendedState;
    
  3443. 
    
  3444.   if (state.stylesheets && state.count === 0) {
    
  3445.     // We are not currently blocked but we have not inserted all stylesheets.
    
  3446.     // If this insertion happens and loads or errors synchronously then we can
    
  3447.     // avoid suspending the commit. To do this we check the count again immediately after
    
  3448.     insertSuspendedStylesheets(state, state.stylesheets);
    
  3449.   }
    
  3450. 
    
  3451.   // We need to check the count again because the inserted stylesheets may have led to new
    
  3452.   // tasks to wait on.
    
  3453.   if (state.count > 0) {
    
  3454.     return commit => {
    
  3455.       // We almost never want to show content before its styles have loaded. But
    
  3456.       // eventually we will give up and allow unstyled content. So this number is
    
  3457.       // somewhat arbitrary — big enough that you'd only reach it under
    
  3458.       // extreme circumstances.
    
  3459.       // TODO: Figure out what the browser engines do during initial page load and
    
  3460.       // consider aligning our behavior with that.
    
  3461.       const stylesheetTimer = setTimeout(() => {
    
  3462.         if (state.stylesheets) {
    
  3463.           insertSuspendedStylesheets(state, state.stylesheets);
    
  3464.         }
    
  3465.         if (state.unsuspend) {
    
  3466.           const unsuspend = state.unsuspend;
    
  3467.           state.unsuspend = null;
    
  3468.           unsuspend();
    
  3469.         }
    
  3470.       }, 60000); // one minute
    
  3471. 
    
  3472.       state.unsuspend = commit;
    
  3473. 
    
  3474.       return () => {
    
  3475.         state.unsuspend = null;
    
  3476.         clearTimeout(stylesheetTimer);
    
  3477.       };
    
  3478.     };
    
  3479.   }
    
  3480.   return null;
    
  3481. }
    
  3482. 
    
  3483. function onUnsuspend(this: SuspendedState) {
    
  3484.   this.count--;
    
  3485.   if (this.count === 0) {
    
  3486.     if (this.stylesheets) {
    
  3487.       // If we haven't actually inserted the stylesheets yet we need to do so now before starting the commit.
    
  3488.       // The reason we do this after everything else has finished is because we want to have all the stylesheets
    
  3489.       // load synchronously right before mutating. Ideally the new styles will cause a single recalc only on the
    
  3490.       // new tree. When we filled up stylesheets we only inlcuded stylesheets with matching media attributes so we
    
  3491.       // wait for them to load before actually continuing. We expect this to increase the count above zero
    
  3492.       insertSuspendedStylesheets(this, this.stylesheets);
    
  3493.     } else if (this.unsuspend) {
    
  3494.       const unsuspend = this.unsuspend;
    
  3495.       this.unsuspend = null;
    
  3496.       unsuspend();
    
  3497.     }
    
  3498.   }
    
  3499. }
    
  3500. 
    
  3501. // This is typecast to non-null because it will always be set before read.
    
  3502. // it is important that this not be used except when the stack guarantees it exists.
    
  3503. // Currentlyt his is only during insertSuspendedStylesheet.
    
  3504. let precedencesByRoot: Map<HoistableRoot, Map<string, Instance>> = (null: any);
    
  3505. 
    
  3506. function insertSuspendedStylesheets(
    
  3507.   state: SuspendedState,
    
  3508.   resources: Map<StylesheetResource, HoistableRoot>,
    
  3509. ): void {
    
  3510.   // We need to clear this out so we don't try to reinsert after the stylesheets have loaded
    
  3511.   state.stylesheets = null;
    
  3512. 
    
  3513.   if (state.unsuspend === null) {
    
  3514.     // The suspended commit was cancelled. We don't need to insert any stylesheets.
    
  3515.     return;
    
  3516.   }
    
  3517. 
    
  3518.   // Temporarily increment count. we don't want any synchronously loaded stylesheets to try to unsuspend
    
  3519.   // before we finish inserting all stylesheets.
    
  3520.   state.count++;
    
  3521. 
    
  3522.   precedencesByRoot = new Map();
    
  3523.   resources.forEach(insertStylesheetIntoRoot, state);
    
  3524.   precedencesByRoot = (null: any);
    
  3525. 
    
  3526.   // We can remove our temporary count and if we're still at zero we can unsuspend.
    
  3527.   // If we are in the synchronous phase before deciding if the commit should suspend and this
    
  3528.   // ends up hitting the unsuspend path it will just invoke the noop unsuspend.
    
  3529.   onUnsuspend.call(state);
    
  3530. }
    
  3531. 
    
  3532. function insertStylesheetIntoRoot(
    
  3533.   this: SuspendedState,
    
  3534.   root: HoistableRoot,
    
  3535.   resource: StylesheetResource,
    
  3536.   map: Map<StylesheetResource, HoistableRoot>,
    
  3537. ) {
    
  3538.   if (resource.state.loading & Inserted) {
    
  3539.     // This resource was inserted by another root committing. we don't need to insert it again
    
  3540.     return;
    
  3541.   }
    
  3542. 
    
  3543.   let last;
    
  3544.   let precedences = precedencesByRoot.get(root);
    
  3545.   if (!precedences) {
    
  3546.     precedences = new Map();
    
  3547.     precedencesByRoot.set(root, precedences);
    
  3548.     const nodes = root.querySelectorAll(
    
  3549.       'link[data-precedence],style[data-precedence]',
    
  3550.     );
    
  3551.     for (let i = 0; i < nodes.length; i++) {
    
  3552.       const node = nodes[i];
    
  3553.       if (
    
  3554.         node.nodeName === 'link' ||
    
  3555.         // We omit style tags with media="not all" because they are not in the right position
    
  3556.         // and will be hoisted by the Fizz runtime imminently.
    
  3557.         node.getAttribute('media') !== 'not all'
    
  3558.       ) {
    
  3559.         precedences.set('p' + node.dataset.precedence, node);
    
  3560.         last = node;
    
  3561.       }
    
  3562.     }
    
  3563.     if (last) {
    
  3564.       precedences.set('last', last);
    
  3565.     }
    
  3566.   } else {
    
  3567.     last = precedences.get('last');
    
  3568.   }
    
  3569. 
    
  3570.   // We only call this after we have constructed an instance so we assume it here
    
  3571.   const instance: HTMLLinkElement = (resource.instance: any);
    
  3572.   // We will always have a precedence for stylesheet instances
    
  3573.   const precedence: string = (instance.getAttribute('data-precedence'): any);
    
  3574. 
    
  3575.   const prior = precedences.get('p' + precedence) || last;
    
  3576.   if (prior === last) {
    
  3577.     precedences.set('last', instance);
    
  3578.   }
    
  3579.   precedences.set(precedence, instance);
    
  3580. 
    
  3581.   this.count++;
    
  3582.   const onComplete = onUnsuspend.bind(this);
    
  3583.   instance.addEventListener('load', onComplete);
    
  3584.   instance.addEventListener('error', onComplete);
    
  3585. 
    
  3586.   if (prior) {
    
  3587.     (prior.parentNode: any).insertBefore(instance, prior.nextSibling);
    
  3588.   } else {
    
  3589.     const parent =
    
  3590.       root.nodeType === DOCUMENT_NODE
    
  3591.         ? ((((root: any): Document).head: any): Element)
    
  3592.         : ((root: any): ShadowRoot);
    
  3593.     parent.insertBefore(instance, parent.firstChild);
    
  3594.   }
    
  3595.   resource.state.loading |= Inserted;
    
  3596. }
    
  3597. 
    
  3598. export const NotPendingTransition: TransitionStatus = NotPending;