packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js JAVASCRIPT 6,892 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 6,892.
1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */910import type {DOMEventName} from '../events/DOMEventNames';11import type {Fiber, FiberRoot} from 'react-reconciler/src/ReactInternalTypes';12import type {13  BoundingRect,14  IntersectionObserverOptions,15  ObserveVisibleRectsCallback,16} from 'react-reconciler/src/ReactTestSelectors';17import type {ReactContext, ReactScopeInstance} from 'shared/ReactTypes';18import type {AncestorInfoDev} from './validateDOMNesting';19import type {FormStatus} from 'react-dom-bindings/src/shared/ReactDOMFormActions';20import type {21  CrossOriginEnum,22  PreloadImplOptions,23  PreloadModuleImplOptions,24  PreinitStyleOptions,25  PreinitScriptOptions,26  PreinitModuleScriptOptions,27} from 'react-dom/src/shared/ReactDOMTypes';28import type {TransitionTypes} from 'react/src/ReactTransitionType';2930import {NotPending} from '../shared/ReactDOMFormActions';3132import {setSrcObject} from './ReactDOMSrcObject';3334import {getCurrentRootHostContainer} from 'react-reconciler/src/ReactFiberHostContext';35import {runWithFiberInDEV} from 'react-reconciler/src/ReactCurrentFiber';3637import hasOwnProperty from 'shared/hasOwnProperty';38import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion';39import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';4041export {42  setCurrentUpdatePriority,43  getCurrentUpdatePriority,44  resolveUpdatePriority,45} from './ReactDOMUpdatePriority';46import {47  precacheFiberNode,48  updateFiberProps,49  getFiberCurrentPropsFromNode,50  getInstanceFromNode,51  getClosestInstanceFromNode,52  getFiberFromScopeInstance,53  getInstanceFromNode as getInstanceFromNodeDOMTree,54  isContainerMarkedAsRoot,55  detachDeletedInstance,56  getResourcesFromRoot,57  isMarkedHoistable,58  markNodeAsHoistable,59  markNodeAsPendingLoad,60  clearPendingLoadOnNode,61  isNodePendingLoad,62  isOwnedInstance,63} from './ReactDOMComponentTree';64import {65  traverseFragmentInstancesAndTextInstances,66  getFragmentParentInstanceOrContainerFiber,67  getInstanceFromHostFiber,68  isFiberFollowing,69  isFiberPreceding,70  getFragmentInstanceOrTextInstanceSiblings,71  traverseFragmentInstancesAndTextInstancesDeeply,72  fiberIsPortaledIntoHost,73  getFragmentPortalContainerInfo,74  isFiberContainedByFragment,75  isFragmentContainedByFiber,76} from 'react-reconciler/src/ReactFiberTreeReflection';77import {compareDocumentPositionForEmptyFragment} from 'shared/ReactDOMFragmentRefShared';7879export {detachDeletedInstance};80import {hasRole} from './DOMAccessibilityRoles';81import type {SingletonType} from './ReactDOMComponent';82import {83  setInitialProperties,84  updateProperties,85  clearSingletonProperties,86  hydrateProperties,87  hydrateText,88  diffHydratedProperties,89  getPropsFromElement,90  diffHydratedText,91  trapClickOnNonInteractiveElement,92  clearClickListener,93} from './ReactDOMComponent';94import {hydrateInput} from './ReactDOMInput';95import {hydrateTextarea} from './ReactDOMTextarea';96import {hydrateSelect} from './ReactDOMSelect';97import {getSelectionInformation, restoreSelection} from './ReactInputSelection';98import setTextContent from './setTextContent';99import {100  validateDOMNesting,101  validateTextNesting,102  updatedAncestorInfoDev,103} from './validateDOMNesting';104import {105  isEnabled as ReactBrowserEventEmitterIsEnabled,106  setEnabled as ReactBrowserEventEmitterSetEnabled,107} from '../events/ReactDOMEventListener';108import {SVG_NAMESPACE, MATH_NAMESPACE} from './DOMNamespaces';109import {110  ELEMENT_NODE,111  TEXT_NODE,112  COMMENT_NODE,113  DOCUMENT_NODE,114  DOCUMENT_TYPE_NODE,115  DOCUMENT_FRAGMENT_NODE,116} from './HTMLNodeType';117118import {119  flushEventReplaying,120  retryIfBlockedOn,121} from '../events/ReactDOMEventReplaying';122123import {124  enableCreateEventHandleAPI,125  enableScopeAPI,126  enableTrustedTypesIntegration,127  disableLegacyMode,128  enableMoveBefore,129  disableCommentsAsDOMContainers,130  enableSuspenseyImages,131  enableSrcObject,132  enableViewTransition,133  enableHydrationChangeEvent,134  enableFragmentRefsScrollIntoView,135  enableProfilerTimer,136  enableFragmentRefsInstanceHandles,137  enableFragmentRefsTextNodes,138} from 'shared/ReactFeatureFlags';139import {140  HostComponent,141  HostHoistable,142  HostText,143  HostSingleton,144} from 'react-reconciler/src/ReactWorkTags';145import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';146import {validateLinkPropsForStyleResource} from '../shared/ReactDOMResourceValidation';147import escapeSelectorAttributeValueInsideDoubleQuotes from './escapeSelectorAttributeValueInsideDoubleQuotes';148import {flushSyncWork as flushSyncWorkOnAllRoots} from 'react-reconciler/src/ReactFiberWorkLoop';149import {requestFormReset as requestFormResetOnFiber} from 'react-reconciler/src/ReactFiberHooks';150151import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';152153export {default as rendererVersion} from 'shared/ReactVersion';154155import noop from 'shared/noop';156import estimateBandwidth from './estimateBandwidth';157158export const rendererPackageName = 'react-dom';159export const extraDevToolsConfig = null;160161export type Type = string;162export type Props = {163  autoFocus?: boolean,164  children?: mixed,165  disabled?: boolean,166  hidden?: boolean,167  suppressHydrationWarning?: boolean,168  dangerouslySetInnerHTML?: mixed,169  style?: {170    display?: string,171    viewTransitionName?: string,172    'view-transition-name'?: string,173    viewTransitionClass?: string,174    'view-transition-class'?: string,175    margin?: string,176    marginTop?: string,177    'margin-top'?: string,178    marginBottom?: string,179    'margin-bottom'?: string,180    ...181  },182  bottom?: null | number,183  left?: null | number,184  right?: null | number,185  top?: null | number,186  is?: string,187  size?: number,188  value?: string,189  defaultValue?: string,190  checked?: boolean,191  defaultChecked?: boolean,192  multiple?: boolean,193  type?: string,194  src?: string | Blob | MediaSource | MediaStream, // TODO: Response195  srcSet?: string,196  loading?: 'eager' | 'lazy',197  onLoad?: (event: any) => void,198  ...199};200type RawProps = {201  [string]: mixed,202};203export type EventTargetChildElement = {204  type: string,205  props: null | {206    style?: {207      position?: string,208      zIndex?: number,209      bottom?: string,210      left?: string,211      right?: string,212      top?: string,213      ...214    },215    ...216  },217  ...218};219220export type Container =221  | interface extends Element {_reactRootContainer?: FiberRoot}222  | interface extends Document {_reactRootContainer?: FiberRoot}223  | interface extends DocumentFragment {_reactRootContainer?: FiberRoot};224export type Instance = Element;225export type TextInstance = Text;226227type InstanceWithFragmentHandles = Instance & {228  reactFragments?: Set<FragmentInstanceType>,229};230type HostNodeWithFragmentHandles = (Instance | TextInstance) & {231  reactFragments?: Set<FragmentInstanceType>,232};233234declare class ActivityInterface extends Comment {}235declare class SuspenseInterface extends Comment {236  _reactRetry: void | (() => void);237}238239export type ActivityInstance = ActivityInterface;240export type SuspenseInstance = SuspenseInterface;241242type FormStateMarkerInstance = Comment;243export type HydratableInstance =244  | Instance245  | TextInstance246  | ActivityInstance247  | SuspenseInstance248  | FormStateMarkerInstance;249export type PublicInstance = Element | Text;250export type HostContextDev = {251  context: HostContextProd,252  ancestorInfo: AncestorInfoDev,253};254type HostContextProd = HostContextNamespace;255export type HostContext = HostContextDev | HostContextProd;256export type UpdatePayload = Array<mixed>;257export type ChildSet = void; // Unused258export type TimeoutHandle = TimeoutID;259export type NoTimeout = -1;260export type RendererInspectionConfig = $ReadOnly<{}>;261262export type TransitionStatus = FormStatus;263264export type ViewTransitionInstance = {265  name: string,266  group: mixin$Animatable,267  imagePair: mixin$Animatable,268  old: mixin$Animatable,269  new: mixin$Animatable,270};271272type SelectionInformation = {273  focusedElem: null | HTMLElement,274  selectionRange: mixed,275};276277const SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';278279const ACTIVITY_START_DATA = '&';280const ACTIVITY_END_DATA = '/&';281const SUSPENSE_START_DATA = '$';282const SUSPENSE_END_DATA = '/$';283const SUSPENSE_PENDING_START_DATA = '$?';284const SUSPENSE_QUEUED_START_DATA = '$~';285const SUSPENSE_FALLBACK_START_DATA = '$!';286const PREAMBLE_CONTRIBUTION_HTML = 'html';287const PREAMBLE_CONTRIBUTION_BODY = 'body';288const PREAMBLE_CONTRIBUTION_HEAD = 'head';289const FORM_STATE_IS_MATCHING = 'F!';290const FORM_STATE_IS_NOT_MATCHING = 'F';291292const DOCUMENT_READY_STATE_LOADING = 'loading';293294const STYLE = 'style';295296opaque type HostContextNamespace = 0 | 1 | 2;297export const HostContextNamespaceNone: HostContextNamespace = 0;298const HostContextNamespaceSvg: HostContextNamespace = 1;299const HostContextNamespaceMath: HostContextNamespace = 2;300301let eventsEnabled: ?boolean = null;302let selectionInformation: null | SelectionInformation = null;303304export * from 'react-reconciler/src/ReactFiberConfigWithNoPersistence';305306function getOwnerDocumentFromRootContainer(307  rootContainerElement: Element | Document | DocumentFragment,308): Document {309  return rootContainerElement.nodeType === DOCUMENT_NODE310    ? (rootContainerElement as any)311    : rootContainerElement.ownerDocument;312}313314export function getRootHostContext(315  rootContainerInstance: Container,316): HostContext {317  let type;318  let context: HostContextProd;319  const nodeType = rootContainerInstance.nodeType;320  switch (nodeType) {321    case DOCUMENT_NODE:322    case DOCUMENT_FRAGMENT_NODE: {323      type = nodeType === DOCUMENT_NODE ? '#document' : '#fragment';324      const root = (rootContainerInstance as any).documentElement;325      if (root) {326        const namespaceURI = root.namespaceURI;327        context = namespaceURI328          ? getOwnHostContext(namespaceURI)329          : HostContextNamespaceNone;330      } else {331        context = HostContextNamespaceNone;332      }333      break;334    }335    default: {336      const container: any =337        !disableCommentsAsDOMContainers && nodeType === COMMENT_NODE338          ? rootContainerInstance.parentNode339          : rootContainerInstance;340      type = container.tagName;341      const namespaceURI = container.namespaceURI;342      if (!namespaceURI) {343        switch (type) {344          case 'svg':345            context = HostContextNamespaceSvg;346            break;347          case 'math':348            context = HostContextNamespaceMath;349            break;350          default:351            context = HostContextNamespaceNone;352            break;353        }354      } else {355        const ownContext = getOwnHostContext(namespaceURI);356        context = getChildHostContextProd(ownContext, type);357      }358      break;359    }360  }361  if (__DEV__) {362    const validatedTag = type.toLowerCase();363    const ancestorInfo = updatedAncestorInfoDev(null, validatedTag);364    return {context, ancestorInfo};365  }366  return context;367}368369function getOwnHostContext(namespaceURI: string): HostContextNamespace {370  switch (namespaceURI) {371    case SVG_NAMESPACE:372      return HostContextNamespaceSvg;373    case MATH_NAMESPACE:374      return HostContextNamespaceMath;375    default:376      return HostContextNamespaceNone;377  }378}379380function getChildHostContextProd(381  parentNamespace: HostContextNamespace,382  type: string,383): HostContextNamespace {384  if (parentNamespace === HostContextNamespaceNone) {385    // No (or default) parent namespace: potential entry point.386    switch (type) {387      case 'svg':388        return HostContextNamespaceSvg;389      case 'math':390        return HostContextNamespaceMath;391      default:392        return HostContextNamespaceNone;393    }394  }395  if (parentNamespace === HostContextNamespaceSvg && type === 'foreignObject') {396    // We're leaving SVG.397    return HostContextNamespaceNone;398  }399  // By default, pass namespace below.400  return parentNamespace;401}402403export function getChildHostContext(404  parentHostContext: HostContext,405  type: string,406): HostContext {407  if (__DEV__) {408    const parentHostContextDev = parentHostContext as any as HostContextDev;409    const context = getChildHostContextProd(parentHostContextDev.context, type);410    const ancestorInfo = updatedAncestorInfoDev(411      parentHostContextDev.ancestorInfo,412      type,413    );414    return {context, ancestorInfo};415  }416  const parentNamespace = parentHostContext as any as HostContextProd;417  return getChildHostContextProd(parentNamespace, type);418}419420export function getPublicInstance(instance: Instance): Instance {421  return instance;422}423424export function prepareForCommit(containerInfo: Container): Object | null {425  eventsEnabled = ReactBrowserEventEmitterIsEnabled();426  selectionInformation = getSelectionInformation(containerInfo);427  let activeInstance = null;428  if (enableCreateEventHandleAPI) {429    const focusedElem = selectionInformation.focusedElem;430    if (focusedElem !== null) {431      activeInstance = getClosestInstanceFromNode(focusedElem);432    }433  }434  ReactBrowserEventEmitterSetEnabled(false);435  return activeInstance;436}437438export function beforeActiveInstanceBlur(internalInstanceHandle: Object): void {439  if (enableCreateEventHandleAPI) {440    ReactBrowserEventEmitterSetEnabled(true);441    dispatchBeforeDetachedBlur(442      (selectionInformation as any).focusedElem,443      internalInstanceHandle,444    );445    ReactBrowserEventEmitterSetEnabled(false);446  }447}448449export function afterActiveInstanceBlur(): void {450  if (enableCreateEventHandleAPI) {451    ReactBrowserEventEmitterSetEnabled(true);452    dispatchAfterDetachedBlur((selectionInformation as any).focusedElem);453    ReactBrowserEventEmitterSetEnabled(false);454  }455}456457export function resetAfterCommit(containerInfo: Container): void {458  restoreSelection(selectionInformation, containerInfo);459  ReactBrowserEventEmitterSetEnabled(eventsEnabled);460  eventsEnabled = null;461  selectionInformation = null;462}463464export function createHoistableInstance(465  type: string,466  props: Props,467  rootContainerInstance: Container,468  internalInstanceHandle: Object,469): Instance {470  const ownerDocument = getOwnerDocumentFromRootContainer(471    rootContainerInstance,472  );473474  const domElement: Instance = ownerDocument.createElement(type);475  precacheFiberNode(internalInstanceHandle, domElement);476  updateFiberProps(domElement, props);477  setInitialProperties(domElement, type, props);478  markNodeAsHoistable(domElement);479  return domElement;480}481482let didWarnScriptTags = false;483function isScriptDataBlock(props: Props): boolean {484  const scriptType = props.type;485  if (typeof scriptType !== 'string' || scriptType === '') {486    return false;487  }488  const lower = scriptType.toLowerCase();489  // Special non-MIME keywords recognized by the HTML spec490  // TODO: May be fine to also not warn about having these types be parsed as "parser-inserted"491  if (492    lower === 'module' ||493    lower === 'importmap' ||494    lower === 'speculationrules'495  ) {496    return false;497  }498  // JavaScript MIME types per https://mimesniff.spec.whatwg.org/#javascript-mime-type499  switch (lower) {500    case 'application/ecmascript':501    case 'application/javascript':502    case 'application/x-ecmascript':503    case 'application/x-javascript':504    case 'text/ecmascript':505    case 'text/javascript':506    case 'text/javascript1.0':507    case 'text/javascript1.1':508    case 'text/javascript1.2':509    case 'text/javascript1.3':510    case 'text/javascript1.4':511    case 'text/javascript1.5':512    case 'text/jscript':513    case 'text/livescript':514    case 'text/x-ecmascript':515    case 'text/x-javascript':516      return false;517  }518  // Any other non-empty type value means this is a data block519  return true;520}521const warnedUnknownTags: {522  [key: string]: boolean,523} = {524  // There are working polyfills for <dialog>. Let people use it.525  dialog: true,526  // Electron ships a custom <webview> tag to display external web content in527  // an isolated frame and process.528  // This tag is not present in non Electron environments such as JSDom which529  // is often used for testing purposes.530  // @see https://electronjs.org/docs/api/webview-tag531  webview: true,532};533534export function createInstance(535  type: string,536  props: Props,537  rootContainerInstance: Container,538  hostContext: HostContext,539  internalInstanceHandle: Object,540): Instance {541  let hostContextProd: HostContextProd;542  if (__DEV__) {543    // TODO: take namespace into account when validating.544    const hostContextDev: HostContextDev = hostContext as any;545    validateDOMNesting(type, hostContextDev.ancestorInfo);546    hostContextProd = hostContextDev.context;547  } else {548    hostContextProd = hostContext as any;549  }550551  const ownerDocument = getOwnerDocumentFromRootContainer(552    rootContainerInstance,553  );554555  let domElement: Instance;556  switch (hostContextProd) {557    case HostContextNamespaceSvg:558      domElement = ownerDocument.createElementNS(SVG_NAMESPACE, type);559      break;560    case HostContextNamespaceMath:561      domElement = ownerDocument.createElementNS(MATH_NAMESPACE, type);562      break;563    default:564      switch (type) {565        case 'svg': {566          domElement = ownerDocument.createElementNS(SVG_NAMESPACE, type);567          break;568        }569        case 'math': {570          domElement = ownerDocument.createElementNS(MATH_NAMESPACE, type);571          break;572        }573        case 'script': {574          // Create the script via .innerHTML so its "parser-inserted" flag is575          // set to true and it does not execute576          const div = ownerDocument.createElement('div');577          if (__DEV__) {578            if (579              enableTrustedTypesIntegration &&580              !didWarnScriptTags &&581              // Data block scripts are not executed by UAs anyway so582              // we don't need to warn: https://html.spec.whatwg.org/multipage/scripting.html#attr-script-type583              !isScriptDataBlock(props)584            ) {585              console.error(586                'Encountered a script tag while rendering React component. ' +587                  'Scripts inside React components are never executed when rendering ' +588                  'on the client. Consider using template tag instead ' +589                  '(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).',590              );591              didWarnScriptTags = true;592            }593          }594          div.innerHTML = '<script><' + '/script>';595          // This is guaranteed to yield a script element.596          const firstChild = div.firstChild as any as HTMLScriptElement;597          domElement = div.removeChild(firstChild);598          break;599        }600        case 'select': {601          if (typeof props.is === 'string') {602            domElement = ownerDocument.createElement('select', {is: props.is});603          } else {604            // Separate else branch instead of using `props.is || undefined` above because of a Firefox bug.605            // See discussion in https://github.com/facebook/react/pull/6896606            // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240607            domElement = ownerDocument.createElement('select');608          }609          if (props.multiple) {610            domElement.multiple = true;611          } else if (props.size) {612            // Setting a size greater than 1 causes a select to behave like `multiple=true`, where613            // it is possible that no option is selected.614            //615            // This is only necessary when a select in "single selection mode".616            domElement.size = props.size;617          }618          break;619        }620        default: {621          if (typeof props.is === 'string') {622            domElement = ownerDocument.createElement(type, {is: props.is});623          } else {624            // Separate else branch instead of using `props.is || undefined` above because of a Firefox bug.625            // See discussion in https://github.com/facebook/react/pull/6896626            // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240627            domElement = ownerDocument.createElement(type);628          }629630          if (__DEV__) {631            if (type.indexOf('-') === -1) {632              // We're not SVG/MathML and we don't have a dash, so we're not a custom element633              // Even if you use `is`, these should be of known type and lower case.634              if (type !== type.toLowerCase()) {635                console.error(636                  '<%s /> is using incorrect casing. ' +637                    'Use PascalCase for React components, ' +638                    'or lowercase for HTML elements.',639                  type,640                );641              }642              if (643                // $FlowFixMe[method-unbinding]644                Object.prototype.toString.call(domElement) ===645                  '[object HTMLUnknownElement]' &&646                !hasOwnProperty.call(warnedUnknownTags, type)647              ) {648                warnedUnknownTags[type] = true;649                console.error(650                  'The tag <%s> is unrecognized in this browser. ' +651                    'If you meant to render a React component, start its name with ' +652                    'an uppercase letter.',653                  type,654                );655              }656            }657          }658        }659      }660  }661  precacheFiberNode(internalInstanceHandle, domElement);662  updateFiberProps(domElement, props);663  return domElement;664}665666let didWarnForClone = false;667668export function cloneMutableInstance(669  instance: Instance,670  keepChildren: boolean,671): Instance {672  if (__DEV__) {673    // Warn for problematic674    const tagName = instance.tagName;675    switch (tagName) {676      case 'VIDEO':677      case 'IFRAME':678        if (!didWarnForClone) {679          didWarnForClone = true;680          // TODO: Once we have the ability to avoid cloning the root, suggest an absolutely681          // positioned ViewTransition instead as the solution.682          console.warn(683            'startGestureTransition() required cloning a <%s> element since it exists in ' +684              'both states of the gesture. This can be problematic since it will load it twice ' +685              'Try removing or hiding it with <Activity mode="offscreen"> in the optimistic state.',686            tagName.toLowerCase(),687          );688        }689        break;690    }691  }692  return instance.cloneNode(keepChildren);693}694695export function appendInitialChild(696  parentInstance: Instance,697  child: Instance | TextInstance,698): void {699  // Note: This should not use moveBefore() because initial are appended while disconnected.700  parentInstance.appendChild(child);701}702703export function finalizeInitialChildren(704  domElement: Instance,705  type: string,706  props: Props,707  hostContext: HostContext,708): boolean {709  setInitialProperties(domElement, type, props);710  switch (type) {711    case 'button':712    case 'input':713    case 'select':714    case 'textarea':715      return !!props.autoFocus;716    case 'img':717      return true;718    default:719      return false;720  }721}722723export function finalizeHydratedChildren(724  domElement: Instance,725  type: string,726  props: Props,727  hostContext: HostContext,728): boolean {729  // TOOD: Consider unifying this with hydrateInstance.730  if (!enableHydrationChangeEvent) {731    return false;732  }733  switch (type) {734    case 'input':735    case 'select':736    case 'textarea':737    case 'img':738      return true;739    default:740      return false;741  }742}743744export function shouldSetTextContent(type: string, props: Props): boolean {745  return (746    type === 'textarea' ||747    type === 'noscript' ||748    typeof props.children === 'string' ||749    typeof props.children === 'number' ||750    typeof props.children === 'bigint' ||751    (typeof props.dangerouslySetInnerHTML === 'object' &&752      props.dangerouslySetInnerHTML !== null &&753      props.dangerouslySetInnerHTML.__html != null)754  );755}756757export function createTextInstance(758  text: string,759  rootContainerInstance: Container,760  hostContext: HostContext,761  internalInstanceHandle: Object,762): TextInstance {763  if (__DEV__) {764    const hostContextDev = hostContext as any as HostContextDev;765    const ancestor = hostContextDev.ancestorInfo.current;766    if (ancestor != null) {767      validateTextNesting(768        text,769        ancestor.tag,770        hostContextDev.ancestorInfo.implicitRootScope,771      );772    }773  }774  const textNode: TextInstance = getOwnerDocumentFromRootContainer(775    rootContainerInstance,776  ).createTextNode(text);777  precacheFiberNode(internalInstanceHandle, textNode);778  return textNode;779}780781export function cloneMutableTextInstance(782  textInstance: TextInstance,783): TextInstance {784  return textInstance.cloneNode(false);785}786787let currentPopstateTransitionEvent: Event | null = null;788export function shouldAttemptEagerTransition(): boolean {789  const event = window.event;790  if (event && event.type === 'popstate') {791    // This is a popstate event. Attempt to render any transition during this792    // event synchronously. Unless we already attempted during this event.793    if (event === currentPopstateTransitionEvent) {794      // We already attempted to render this popstate transition synchronously.795      // Any subsequent attempts must have happened as the result of a derived796      // update, like startTransition inside useEffect, or useDV. Switch back to797      // the default behavior for all remaining transitions during the current798      // popstate event.799      return false;800    } else {801      // Cache the current event in case a derived transition is scheduled.802      // (Refer to previous branch.)803      currentPopstateTransitionEvent = event;804      return true;805    }806  }807  // We're not inside a popstate event.808  currentPopstateTransitionEvent = null;809  return false;810}811812let schedulerEvent: void | Event = undefined;813export function trackSchedulerEvent(): void {814  schedulerEvent = window.event;815}816817export function resolveEventType(): null | string {818  const event = window.event;819  return event && event !== schedulerEvent ? event.type : null;820}821822export function resolveEventTimeStamp(): number {823  const event = window.event;824  return event && event !== schedulerEvent ? event.timeStamp : -1.1;825}826827export const isPrimaryRenderer = true;828export const warnsIfNotActing = true;829// This initialization code may run even on server environments830// if a component just imports ReactDOM (e.g. for findDOMNode).831// Some environments might not have setTimeout or clearTimeout.832export const scheduleTimeout: any =833  typeof setTimeout === 'function' ? setTimeout : (undefined as any);834export const cancelTimeout: any =835  typeof clearTimeout === 'function' ? clearTimeout : (undefined as any);836export const noTimeout: -1 = -1;837const localPromise = typeof Promise === 'function' ? Promise : undefined;838const localRequestAnimationFrame =839  typeof requestAnimationFrame === 'function'840    ? requestAnimationFrame841    : scheduleTimeout;842843export {getClosestInstanceFromNode as getInstanceFromNode};844845export function preparePortalMount(portalInstance: Instance): void {846  listenToAllSupportedEvents(portalInstance);847}848849export function prepareScopeUpdate(850  scopeInstance: ReactScopeInstance,851  internalInstanceHandle: Object,852): void {853  if (enableScopeAPI) {854    precacheFiberNode(internalInstanceHandle, scopeInstance);855  }856}857858export function getInstanceFromScope(859  scopeInstance: ReactScopeInstance,860): null | Object {861  if (enableScopeAPI) {862    return getFiberFromScopeInstance(scopeInstance);863  }864  return null;865}866867// -------------------868//     Microtasks869// -------------------870export const supportsMicrotasks = true;871export const scheduleMicrotask: any =872  typeof queueMicrotask === 'function'873    ? queueMicrotask874    : typeof localPromise !== 'undefined'875      ? callback =>876          localPromise.resolve(null).then(callback).catch(handleErrorInNextTick)877      : scheduleTimeout; // TODO: Determine the best fallback here.878879function handleErrorInNextTick(error: any) {880  setTimeout(() => {881    throw error;882  });883}884885// -------------------886//     Mutation887// -------------------888889export const supportsMutation = true;890891export function commitMount(892  domElement: Instance,893  type: string,894  newProps: Props,895  internalInstanceHandle: Object,896): void {897  // Despite the naming that might imply otherwise, this method only898  // fires if there is an `Update` effect scheduled during mounting.899  // This happens if `finalizeInitialChildren` returns `true` (which it900  // does to implement the `autoFocus` attribute on the client). But901  // there are also other cases when this might happen (such as patching902  // up text content during hydration mismatch). So we'll check this again.903  switch (type) {904    case 'button':905    case 'input':906    case 'select':907    case 'textarea':908      if (newProps.autoFocus) {909        (910          domElement as any as911            | HTMLButtonElement912            | HTMLInputElement913            | HTMLSelectElement914            | HTMLTextAreaElement915        ).focus();916      }917      return;918    case 'img': {919      // The technique here is to assign the src or srcSet property to cause the browser920      // to issue a new load event. If it hasn't loaded yet it'll fire whenever the load actually completes.921      // If it has already loaded we missed it so the second load will still be the first one that executes922      // any associated onLoad props.923      // Even if we have srcSet we prefer to reassign src. The reason is that Firefox does not trigger a new924      // load event when only srcSet is assigned. Chrome will trigger a load event if either is assigned so we925      // only need to assign one. And Safari just never triggers a new load event which means this technique926      // is already a noop regardless of which properties are assigned. We should revisit if browsers update927      // this heuristic in the future.928      if (newProps.src) {929        const src = (newProps as any).src;930        if (enableSrcObject && typeof src === 'object') {931          // For object src, we can't just set the src again to the same blob URL because it might have932          // already revoked if it loaded before this. However, we can create a new blob URL and set that.933          // This is relatively cheap since the blob is already in memory but this might cause some934          // duplicated work.935          // TODO: We could maybe detect if load hasn't fired yet and if so reuse the URL.936          try {937            setSrcObject(domElement, type, src);938            return;939          } catch (x) {940            // If URL.createObjectURL() errors, it was probably some other object type941            // that should be toString:ed instead, so we just fall-through to the normal942            // path.943          }944        }945        (domElement as any as HTMLImageElement).src = src;946      } else if (newProps.srcSet) {947        (domElement as any as HTMLImageElement).srcset = (948          newProps as any949        ).srcSet;950      }951      return;952    }953  }954}955956export function commitHydratedInstance(957  domElement: Instance,958  type: string,959  props: Props,960  internalInstanceHandle: Object,961): void {962  if (!enableHydrationChangeEvent) {963    return;964  }965  // This fires in the commit phase if a hydrated instance needs to do further966  // work in the commit phase. Similar to commitMount. However, this should not967  // do things that would've already happened such as set auto focus since that968  // would steal focus. It's only scheduled if finalizeHydratedChildren returns969  // true.970  switch (type) {971    case 'input': {972      hydrateInput(973        domElement,974        props.value,975        props.defaultValue,976        props.checked,977        props.defaultChecked,978      );979      break;980    }981    case 'select': {982      hydrateSelect(983        domElement,984        props.value,985        props.defaultValue,986        props.multiple,987      );988      break;989    }990    case 'textarea':991      hydrateTextarea(domElement, props.value, props.defaultValue);992      break;993    case 'img':994      // TODO: Should we replay onLoad events?995      break;996  }997}998999export function commitUpdate(1000  domElement: Instance,1001  type: string,1002  oldProps: Props,1003  newProps: Props,1004  internalInstanceHandle: Object,1005): void {1006  // Diff and update the properties.1007  updateProperties(domElement, type, oldProps, newProps);10081009  // Update the props handle so that we know which props are the ones with1010  // with current event handlers.1011  updateFiberProps(domElement, newProps);1012}10131014export function resetTextContent(domElement: Instance): void {1015  setTextContent(domElement, '');1016}10171018export function commitTextUpdate(1019  textInstance: TextInstance,1020  oldText: string,1021  newText: string,1022): void {1023  textInstance.nodeValue = newText;1024}10251026const supportsMoveBefore =1027  // $FlowFixMe[prop-missing]: We're doing the feature detection here.1028  enableMoveBefore &&1029  typeof window !== 'undefined' &&1030  typeof window.Element.prototype.moveBefore === 'function';10311032export function appendChild(1033  parentInstance: Instance,1034  child: Instance | TextInstance,1035): void {1036  if (supportsMoveBefore && child.parentNode !== null) {1037    // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore.1038    parentInstance.moveBefore(child, null);1039  } else {1040    parentInstance.appendChild(child);1041  }1042}10431044function warnForReactChildrenConflict(container: Container): void {1045  if (__DEV__) {1046    if ((container as any).__reactWarnedAboutChildrenConflict) {1047      return;1048    }1049    const props = getFiberCurrentPropsFromNode(container);1050    if (props !== null) {1051      const fiber = getInstanceFromNode(container);1052      if (fiber !== null) {1053        if (1054          typeof props.children === 'string' ||1055          typeof props.children === 'number'1056        ) {1057          (container as any).__reactWarnedAboutChildrenConflict = true;1058          // Run the warning with the Fiber of the container for context of where the children are specified.1059          // We could also maybe use the Portal. The current execution context is the child being added.1060          runWithFiberInDEV(fiber, () => {1061            console.error(1062              'Cannot use a ref on a React element as a container to `createRoot` or `createPortal` ' +1063                'if that element also sets "children" text content using React. It should be a leaf with no children. ' +1064                "Otherwise it's ambiguous which children should be used.",1065            );1066          });1067        } else if (props.dangerouslySetInnerHTML != null) {1068          (container as any).__reactWarnedAboutChildrenConflict = true;1069          runWithFiberInDEV(fiber, () => {1070            console.error(1071              'Cannot use a ref on a React element as a container to `createRoot` or `createPortal` ' +1072                'if that element also sets "dangerouslySetInnerHTML" using React. It should be a leaf with no children. ' +1073                "Otherwise it's ambiguous which children should be used.",1074            );1075          });1076        }1077      }1078    }1079  }1080}10811082export function appendChildToContainer(1083  container: Container,1084  child: Instance | TextInstance,1085): void {1086  if (__DEV__) {1087    warnForReactChildrenConflict(container);1088  }1089  let parentNode: DocumentFragment | Element;1090  if (container.nodeType === DOCUMENT_NODE) {1091    parentNode = (container as any).body;1092  } else if (1093    !disableCommentsAsDOMContainers &&1094    container.nodeType === COMMENT_NODE1095  ) {1096    parentNode = container.parentNode as any;1097    if (supportsMoveBefore && child.parentNode !== null) {1098      // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore.1099      parentNode.moveBefore(child, container);1100    } else {1101      parentNode.insertBefore(child, container);1102    }1103    return;1104  } else if (container.nodeName === 'HTML') {1105    parentNode = container.ownerDocument.body as any;1106  } else {1107    parentNode = container as any;1108  }1109  if (supportsMoveBefore && child.parentNode !== null) {1110    // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore.1111    parentNode.moveBefore(child, null);1112  } else {1113    parentNode.appendChild(child);1114  }11151116  // This container might be used for a portal.1117  // If something inside a portal is clicked, that click should bubble1118  // through the React tree. However, on Mobile Safari the click would1119  // never bubble through the *DOM* tree unless an ancestor with onclick1120  // event exists. So we wouldn't see it and dispatch it.1121  // This is why we ensure that non React root containers have inline onclick1122  // defined.1123  // https://github.com/facebook/react/issues/119181124  const reactRootContainer = container._reactRootContainer;1125  if (1126    // $FlowFixMe[invalid-compare]1127    (reactRootContainer === null || reactRootContainer === undefined) &&1128    parentNode.onclick === null1129  ) {1130    // TODO: This cast may not be sound for SVG, MathML or custom elements.1131    trapClickOnNonInteractiveElement(parentNode as any as HTMLElement);1132  }1133}11341135export function insertBefore(1136  parentInstance: Instance,1137  child: Instance | TextInstance,1138  beforeChild: Instance | TextInstance | SuspenseInstance | ActivityInstance,1139): void {1140  if (supportsMoveBefore && child.parentNode !== null) {1141    // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore.1142    parentInstance.moveBefore(child, beforeChild);1143  } else {1144    parentInstance.insertBefore(child, beforeChild);1145  }1146}11471148export function insertInContainerBefore(1149  container: Container,1150  child: Instance | TextInstance,1151  beforeChild: Instance | TextInstance | SuspenseInstance | ActivityInstance,1152): void {1153  if (__DEV__) {1154    warnForReactChildrenConflict(container);1155  }1156  let parentNode: DocumentFragment | Element;1157  if (container.nodeType === DOCUMENT_NODE) {1158    parentNode = (container as any).body;1159  } else if (1160    !disableCommentsAsDOMContainers &&1161    container.nodeType === COMMENT_NODE1162  ) {1163    parentNode = container.parentNode as any;1164  } else if (container.nodeName === 'HTML') {1165    parentNode = container.ownerDocument.body as any;1166  } else {1167    parentNode = container as any;1168  }1169  if (supportsMoveBefore && child.parentNode !== null) {1170    // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore.1171    parentNode.moveBefore(child, beforeChild);1172  } else {1173    parentNode.insertBefore(child, beforeChild);1174  }1175}11761177export function isSingletonScope(type: string): boolean {1178  return type === 'head';1179}11801181function createEvent(type: DOMEventName, bubbles: boolean): Event {1182  const event = document.createEvent('Event');1183  event.initEvent(type as any as string, bubbles, false);1184  return event;1185}11861187function dispatchBeforeDetachedBlur(1188  target: HTMLElement,1189  internalInstanceHandle: Object,1190): void {1191  if (enableCreateEventHandleAPI) {1192    const event = createEvent('beforeblur', true);1193    // Dispatch "beforeblur" directly on the target,1194    // so it gets picked up by the event system and1195    // can propagate through the React internal tree.1196    // $FlowFixMe[prop-missing]: internal field1197    event._detachedInterceptFiber = internalInstanceHandle;1198    target.dispatchEvent(event);1199  }1200}12011202function dispatchAfterDetachedBlur(target: HTMLElement): void {1203  if (enableCreateEventHandleAPI) {1204    const event = createEvent('afterblur', false);1205    // So we know what was detached, make the relatedTarget the1206    // detached target on the "afterblur" event.1207    (event as any).relatedTarget = target;1208    // Dispatch the event on the document.1209    document.dispatchEvent(event);1210  }1211}12121213export function removeChild(1214  parentInstance: Instance,1215  child: Instance | TextInstance | SuspenseInstance | ActivityInstance,1216): void {1217  parentInstance.removeChild(child);1218}12191220export function removeChildFromContainer(1221  container: Container,1222  child: Instance | TextInstance | SuspenseInstance | ActivityInstance,1223): void {1224  let parentNode: DocumentFragment | Element;1225  if (container.nodeType === DOCUMENT_NODE) {1226    parentNode = (container as any).body;1227  } else if (1228    !disableCommentsAsDOMContainers &&1229    container.nodeType === COMMENT_NODE1230  ) {1231    parentNode = container.parentNode as any;1232  } else if (container.nodeName === 'HTML') {1233    parentNode = container.ownerDocument.body as any;1234  } else {1235    parentNode = container as any;1236  }1237  parentNode.removeChild(child);1238}12391240function clearHydrationBoundary(1241  parentInstance: Instance,1242  hydrationInstance: SuspenseInstance | ActivityInstance,1243): void {1244  let node: Node = hydrationInstance;1245  // Delete all nodes within this suspense boundary.1246  // There might be nested nodes so we need to keep track of how1247  // deep we are and only break out when we're back on top.1248  let depth = 0;1249  do {1250    const nextNode = node.nextSibling;1251    parentInstance.removeChild(node);1252    if (nextNode && nextNode.nodeType === COMMENT_NODE) {1253      const data = (nextNode as any).data as string;1254      if (data === SUSPENSE_END_DATA || data === ACTIVITY_END_DATA) {1255        if (depth === 0) {1256          parentInstance.removeChild(nextNode);1257          // Retry if any event replaying was blocked on this.1258          retryIfBlockedOn(hydrationInstance);1259          return;1260        } else {1261          depth--;1262        }1263      } else if (1264        data === SUSPENSE_START_DATA ||1265        data === SUSPENSE_PENDING_START_DATA ||1266        data === SUSPENSE_QUEUED_START_DATA ||1267        data === SUSPENSE_FALLBACK_START_DATA ||1268        data === ACTIVITY_START_DATA1269      ) {1270        depth++;1271      } else if (data === PREAMBLE_CONTRIBUTION_HTML) {1272        // If a preamble contribution marker is found within the bounds of this boundary,1273        // then it contributed to the html tag and we need to reset it.1274        const ownerDocument = parentInstance.ownerDocument;1275        const documentElement: Element = ownerDocument.documentElement as any;1276        clearSingletonPreambleContribution(documentElement);1277      } else if (data === PREAMBLE_CONTRIBUTION_HEAD) {1278        const ownerDocument = parentInstance.ownerDocument;1279        const head: Element = ownerDocument.head as any;1280        clearSingletonPreambleContribution(head);1281        // We need to clear the head because this is the only singleton that can have children that1282        // were part of this boundary but are not inside this boundary.1283        clearHead(head);1284      } else if (data === PREAMBLE_CONTRIBUTION_BODY) {1285        const ownerDocument = parentInstance.ownerDocument;1286        const body: Element = ownerDocument.body as any;1287        clearSingletonPreambleContribution(body);1288      }1289    }1290    // $FlowFixMe[incompatible-type] we bail out when we get a null1291    node = nextNode;1292  } while (node);1293  // TODO: Warn, we didn't find the end comment boundary.1294  // Retry if any event replaying was blocked on this.1295  retryIfBlockedOn(hydrationInstance);1296}12971298export function clearActivityBoundary(1299  parentInstance: Instance,1300  activityInstance: ActivityInstance,1301): void {1302  clearHydrationBoundary(parentInstance, activityInstance);1303}13041305export function clearSuspenseBoundary(1306  parentInstance: Instance,1307  suspenseInstance: SuspenseInstance,1308): void {1309  clearHydrationBoundary(parentInstance, suspenseInstance);1310}13111312function clearHydrationBoundaryFromContainer(1313  container: Container,1314  hydrationInstance: SuspenseInstance | ActivityInstance,1315): void {1316  let parentNode: DocumentFragment | Element;1317  if (container.nodeType === DOCUMENT_NODE) {1318    parentNode = (container as any).body;1319  } else if (1320    !disableCommentsAsDOMContainers &&1321    container.nodeType === COMMENT_NODE1322  ) {1323    parentNode = container.parentNode as any;1324  } else if (container.nodeName === 'HTML') {1325    parentNode = container.ownerDocument.body as any;1326  } else {1327    parentNode = container as any;1328  }1329  clearHydrationBoundary(parentNode, hydrationInstance);1330  // Retry if any event replaying was blocked on this.1331  retryIfBlockedOn(container);1332}13331334export function clearActivityBoundaryFromContainer(1335  container: Container,1336  activityInstance: ActivityInstance,1337): void {1338  clearHydrationBoundaryFromContainer(container, activityInstance);1339}13401341export function clearSuspenseBoundaryFromContainer(1342  container: Container,1343  suspenseInstance: SuspenseInstance,1344): void {1345  clearHydrationBoundaryFromContainer(container, suspenseInstance);1346}13471348function hideOrUnhideDehydratedBoundary(1349  suspenseInstance: SuspenseInstance | ActivityInstance,1350  isHidden: boolean,1351) {1352  let node: Node = suspenseInstance;1353  // Unhide all nodes within this suspense boundary.1354  let depth = 0;1355  do {1356    const nextNode = node.nextSibling;1357    if (node.nodeType === ELEMENT_NODE) {1358      const instance = node as any as HTMLElement & {_stashedDisplay?: string};1359      if (isHidden) {1360        instance._stashedDisplay = instance.style.display;1361        instance.style.display = 'none';1362      } else {1363        instance.style.display = instance._stashedDisplay || '';1364        if (instance.getAttribute('style') === '') {1365          instance.removeAttribute('style');1366        }1367      }1368    } else if (node.nodeType === TEXT_NODE) {1369      const textNode = node as any as Text & {_stashedText?: string};1370      if (isHidden) {1371        textNode._stashedText = textNode.nodeValue;1372        textNode.nodeValue = '';1373      } else {1374        textNode.nodeValue = textNode._stashedText || '';1375      }1376    }1377    if (nextNode && nextNode.nodeType === COMMENT_NODE) {1378      const data = (nextNode as any).data as string;1379      if (data === SUSPENSE_END_DATA) {1380        if (depth === 0) {1381          return;1382        } else {1383          depth--;1384        }1385      } else if (1386        data === SUSPENSE_START_DATA ||1387        data === SUSPENSE_PENDING_START_DATA ||1388        data === SUSPENSE_QUEUED_START_DATA ||1389        data === SUSPENSE_FALLBACK_START_DATA1390      ) {1391        depth++;1392      }1393      // TODO: Should we hide preamble contribution in this case?1394    }1395    // $FlowFixMe[incompatible-type] we bail out when we get a null1396    node = nextNode;1397  } while (node);1398}13991400export function hideDehydratedBoundary(1401  suspenseInstance: SuspenseInstance,1402): void {1403  hideOrUnhideDehydratedBoundary(suspenseInstance, true);1404}14051406export function hideInstance(instance: Instance): void {1407  // TODO: Does this work for all element types? What about MathML? Should we1408  // pass host context to this method?1409  instance = instance as any as HTMLElement;1410  const style = instance.style;1411  // $FlowFixMe[method-unbinding]1412  if (typeof style.setProperty === 'function') {1413    style.setProperty('display', 'none', 'important');1414  } else {1415    style.display = 'none';1416  }1417}14181419export function hideTextInstance(textInstance: TextInstance): void {1420  textInstance.nodeValue = '';1421}14221423export function unhideDehydratedBoundary(1424  dehydratedInstance: SuspenseInstance | ActivityInstance,1425): void {1426  hideOrUnhideDehydratedBoundary(dehydratedInstance, false);1427}14281429export function unhideInstance(instance: Instance, props: Props): void {1430  instance = instance as any as HTMLElement;1431  const styleProp = props[STYLE];1432  const display =1433    styleProp !== undefined &&1434    // $FlowFixMe[invalid-compare]1435    styleProp !== null &&1436    styleProp.hasOwnProperty('display')1437      ? styleProp.display1438      : null;1439  instance.style.display =1440    display == null || typeof display === 'boolean'1441      ? ''1442      : // The value would've errored already if it wasn't safe.1443        // eslint-disable-next-line react-internal/safe-string-coercion1444        ('' + display).trim();1445}14461447export function unhideTextInstance(1448  textInstance: TextInstance,1449  text: string,1450): void {1451  textInstance.nodeValue = text;1452}14531454function warnForBlockInsideInline(instance: HTMLElement) {1455  if (__DEV__) {1456    let nextNode = instance.firstChild;1457    outer: while (nextNode != null) {1458      let node: Node = nextNode;1459      if (1460        node.nodeType === ELEMENT_NODE &&1461        getComputedStyle(node as any).display === 'block'1462      ) {1463        const fiber =1464          getInstanceFromNode(node) || getInstanceFromNode(instance);1465        runWithFiberInDEV(1466          fiber,1467          (parentTag: string, childTag: string) => {1468            console.error(1469              "You're about to start a <ViewTransition> around a display: inline " +1470                'element <%s>, which itself has a display: block element <%s> inside it. ' +1471                'This might trigger a bug in Safari which causes the View Transition to ' +1472                'be skipped with a duplicate name error.\n' +1473                'https://bugs.webkit.org/show_bug.cgi?id=290923',1474              parentTag.toLocaleLowerCase(),1475              childTag.toLocaleLowerCase(),1476            );1477          },1478          instance.tagName,1479          (node as any).tagName,1480        );1481        break;1482      }1483      if (node.firstChild != null) {1484        nextNode = node.firstChild;1485        continue;1486      }1487      if (node === instance) {1488        break;1489      }1490      while (node.nextSibling == null) {1491        if (node.parentNode == null || node.parentNode === instance) {1492          break;1493        }1494        node = node.parentNode;1495      }1496      nextNode = node.nextSibling;1497    }1498  }1499}15001501function countClientRects(rects: Array<ClientRect>): number {1502  if (rects.length === 1) {1503    return 1;1504  }1505  // Count non-zero rects.1506  let count = 0;1507  for (let i = 0; i < rects.length; i++) {1508    const rect = rects[i];1509    if (rect.width > 0 && rect.height > 0) {1510      count++;1511    }1512  }1513  return count;1514}15151516export function applyViewTransitionName(1517  instance: Instance,1518  name: string,1519  className: ?string,1520): void {1521  instance = instance as any as HTMLElement;1522  // If the name isn't valid CSS identifier, base64 encode the name instead.1523  // This doesn't let you select it in custom CSS selectors but it does work in current1524  // browsers.1525  const escapedName =1526    CSS.escape(name) !== name ? 'r-' + btoa(name).replace(/=/g, '') : name;1527  // $FlowFixMe[prop-missing]1528  instance.style.viewTransitionName = escapedName;1529  if (className != null) {1530    // $FlowFixMe[prop-missing]1531    instance.style.viewTransitionClass = className;1532  }1533  const computedStyle = getComputedStyle(instance);1534  if (computedStyle.display === 'inline') {1535    // WebKit has a bug where assigning a name to display: inline elements errors1536    // if they have display: block children. We try to work around this bug in the1537    // simple case by converting it automatically to display: inline-block.1538    // https://bugs.webkit.org/show_bug.cgi?id=2909231539    const rects = instance.getClientRects();1540    if (1541      // $FlowFixMe[incompatible-type]1542      countClientRects(rects) === 11543    ) {1544      // If the instance has a single client rect, that means that it can be1545      // expressed as a display: inline-block or block.1546      // This will cause layout thrash but we live with it since inline view transitions1547      // are unusual.1548      const style = instance.style;1549      // If there's literally only one rect, then it's likely on a single line like an1550      // inline-block. If it's multiple rects but all but one of them are empty it's1551      // likely because it's a single block that caused a line break.1552      style.display = rects.length === 1 ? 'inline-block' : 'block';1553      // Margin doesn't apply to inline so should be zero. However, padding top/bottom1554      // applies to inline-block positioning which we can offset by setting the margin1555      // to the negative padding to get it back into original position.1556      style.marginTop = '-' + computedStyle.paddingTop;1557      style.marginBottom = '-' + computedStyle.paddingBottom;1558    } else {1559      // This case cannot be easily fixed if it has blocks but it's also fine if1560      // it doesn't have blocks. So we only warn in DEV about this being an issue.1561      warnForBlockInsideInline(instance);1562    }1563  }1564}15651566export function restoreViewTransitionName(1567  instance: Instance,1568  props: Props,1569): void {1570  instance = instance as any as HTMLElement;1571  const style = instance.style;1572  const styleProp = props[STYLE];1573  const viewTransitionName =1574    styleProp != null1575      ? styleProp.hasOwnProperty('viewTransitionName')1576        ? styleProp.viewTransitionName1577        : styleProp.hasOwnProperty('view-transition-name')1578          ? styleProp['view-transition-name']1579          : null1580      : null;1581  // $FlowFixMe[prop-missing]1582  style.viewTransitionName =1583    viewTransitionName == null || typeof viewTransitionName === 'boolean'1584      ? ''1585      : // The value would've errored already if it wasn't safe.1586        // eslint-disable-next-line react-internal/safe-string-coercion1587        ('' + viewTransitionName).trim();1588  const viewTransitionClass =1589    styleProp != null1590      ? styleProp.hasOwnProperty('viewTransitionClass')1591        ? styleProp.viewTransitionClass1592        : styleProp.hasOwnProperty('view-transition-class')1593          ? styleProp['view-transition-class']1594          : null1595      : null;1596  // $FlowFixMe[prop-missing]1597  style.viewTransitionClass =1598    viewTransitionClass == null || typeof viewTransitionClass === 'boolean'1599      ? ''1600      : // The value would've errored already if it wasn't safe.1601        // eslint-disable-next-line react-internal/safe-string-coercion1602        ('' + viewTransitionClass).trim();1603  if (style.display === 'inline-block') {1604    // We might have overridden the style. Reset it to what it should be.1605    if (styleProp == null) {1606      style.display = style.margin = '';1607    } else {1608      const display = styleProp.display;1609      style.display =1610        display == null || typeof display === 'boolean' ? '' : display;1611      const margin = styleProp.margin;1612      if (margin != null) {1613        style.margin = margin;1614      } else {1615        const marginTop = styleProp.hasOwnProperty('marginTop')1616          ? styleProp.marginTop1617          : styleProp['margin-top'];1618        style.marginTop =1619          marginTop == null || typeof marginTop === 'boolean' ? '' : marginTop;1620        const marginBottom = styleProp.hasOwnProperty('marginBottom')1621          ? styleProp.marginBottom1622          : styleProp['margin-bottom'];1623        style.marginBottom =1624          marginBottom == null || typeof marginBottom === 'boolean'1625            ? ''1626            : marginBottom;1627      }1628    }1629  }1630}16311632export function cancelViewTransitionName(1633  instance: Instance,1634  oldName: string,1635  props: Props,1636): void {1637  // To cancel the "new" state and paint this instance as part of the parent, all we have to do1638  // is remove the view-transition-name before we exit startViewTransition.1639  restoreViewTransitionName(instance, props);1640  // There isn't a way to cancel an "old" state but what we can do is hide it by animating it.1641  // Since it is already removed from the old state of the parent, this technique only works1642  // if the parent also isn't transitioning. Therefore we should only cancel the root most1643  // ViewTransitions.1644  const documentElement = instance.ownerDocument.documentElement;1645  if (documentElement !== null) {1646    documentElement.animate(1647      {opacity: [0, 0], pointerEvents: ['none', 'none']},1648      // $FlowFixMe[incompatible-type]1649      {1650        duration: 0,1651        fill: 'forwards',1652        pseudoElement: '::view-transition-group(' + oldName + ')',1653      },1654    );1655  }1656}16571658export function cancelRootViewTransitionName(rootContainer: Container): void {1659  const documentElement: null | HTMLElement =1660    rootContainer.nodeType === DOCUMENT_NODE1661      ? (rootContainer as any).documentElement1662      : rootContainer.ownerDocument.documentElement;16631664  if (1665    !disableCommentsAsDOMContainers &&1666    rootContainer.nodeType === COMMENT_NODE1667  ) {1668    if (__DEV__) {1669      console.warn(1670        'Cannot cancel root view transition on a comment node. All view transitions will be globally scoped.',1671      );1672    }1673    return;1674  }16751676  if (1677    documentElement !== null &&1678    // $FlowFixMe[prop-missing]1679    documentElement.style.viewTransitionName === ''1680  ) {1681    // $FlowFixMe[prop-missing]1682    documentElement.style.viewTransitionName = 'none';1683    documentElement.animate(1684      {opacity: [0, 0], pointerEvents: ['none', 'none']},1685      // $FlowFixMe[incompatible-type]1686      {1687        duration: 0,1688        fill: 'forwards',1689        pseudoElement: '::view-transition-group(root)',1690      },1691    );1692    // By default the root ::view-transition selector captures all pointer events,1693    // which means nothing gets interactive. We want to let whatever is not animating1694    // remain interactive during the transition. To do that, we set the size to nothing1695    // so that the transition doesn't capture any clicks. We don't set pointer-events1696    // on this one as that would apply to all running transitions. This lets animations1697    // that are running to block clicks so that they don't end up incorrectly hitting1698    // whatever is below the animation.1699    documentElement.animate(1700      {width: [0, 0], height: [0, 0]},1701      // $FlowFixMe[incompatible-call]1702      // $FlowFixMe[incompatible-type]1703      {1704        duration: 0,1705        fill: 'forwards',1706        pseudoElement: '::view-transition',1707      },1708    );1709  }1710}17111712export function restoreRootViewTransitionName(rootContainer: Container): void {1713  let containerInstance: Instance;1714  if (rootContainer.nodeType === DOCUMENT_NODE) {1715    containerInstance = (rootContainer as any).body;1716  } else if (rootContainer.nodeName === 'HTML') {1717    containerInstance = rootContainer.ownerDocument.body as any;1718  } else {1719    // If the container is not the whole document, then we ideally should probably1720    // clone the whole document outside of the React too.1721    containerInstance = rootContainer as any;1722  }1723  if (1724    !disableCommentsAsDOMContainers &&1725    containerInstance.nodeType === COMMENT_NODE1726  ) {1727    return;1728  }1729  if (1730    // $FlowFixMe[prop-missing]1731    containerInstance.style.viewTransitionName === 'root'1732  ) {1733    // If we moved the root view transition name to the container in a gesture1734    // we need to restore it now.1735    containerInstance.style.viewTransitionName = '';1736  }1737  const documentElement: null | HTMLElement =1738    containerInstance.ownerDocument.documentElement;1739  if (1740    documentElement !== null &&1741    // $FlowFixMe[prop-missing]1742    documentElement.style.viewTransitionName === 'none'1743  ) {1744    // $FlowFixMe[prop-missing]1745    documentElement.style.viewTransitionName = '';1746  }1747}17481749function getComputedTransform(style: CSSStyleDeclaration): string {1750  // Gets the merged transform of all the short hands.1751  const computedStyle: any = style;1752  let transform: string = computedStyle.transform;1753  if (transform === 'none') {1754    transform = '';1755  }1756  const scale: string = computedStyle.scale;1757  if (scale !== 'none' && scale !== '') {1758    const parts = scale.split(' ');1759    transform =1760      (parts.length === 3 ? 'scale3d' : 'scale') +1761      '(' +1762      parts.join(', ') +1763      ') ' +1764      transform;1765  }1766  const rotate: string = computedStyle.rotate;1767  if (rotate !== 'none' && rotate !== '') {1768    const parts = rotate.split(' ');1769    if (parts.length === 1) {1770      transform = 'rotate(' + parts[0] + ') ' + transform;1771    } else if (parts.length === 2) {1772      transform =1773        'rotate' + parts[0].toUpperCase() + '(' + parts[1] + ') ' + transform;1774    } else {1775      transform = 'rotate3d(' + parts.join(', ') + ') ' + transform;1776    }1777  }1778  const translate: string = computedStyle.translate;1779  if (translate !== 'none' && translate !== '') {1780    const parts = translate.split(' ');1781    transform =1782      (parts.length === 3 ? 'translate3d' : 'translate') +1783      '(' +1784      parts.join(', ') +1785      ') ' +1786      transform;1787  }1788  return transform;1789}17901791function moveOutOfViewport(1792  originalStyle: CSSStyleDeclaration,1793  element: HTMLElement,1794): void {1795  // Apply a transform that safely puts the whole element outside the viewport1796  // while still letting it paint its "old" state to a snapshot.1797  const transform = getComputedTransform(originalStyle);1798  // Clear the long form properties.1799  // $FlowFixMe[prop-missing]1800  element.style.translate = 'none';1801  // $FlowFixMe[prop-missing]1802  element.style.scale = 'none';1803  // $FlowFixMe[prop-missing]1804  element.style.rotate = 'none';1805  // Apply a translate to move it way out of the viewport. This is applied first1806  // so that it is in the coordinate space of the parent and not after applying1807  // other transforms. That's why we need to merge the long form properties.1808  // TODO: Ideally we'd adjust for the parent's rotate/scale. Otherwise when1809  // we move back the ::view-transition-group we might overshoot or undershoot.1810  element.style.transform = 'translate(-20000px, -20000px) ' + transform;1811}18121813function moveOldFrameIntoViewport(keyframe: any): void {1814  // In the resulting View Transition Animation, the first frame will be offset.1815  const computedTransform: ?string = keyframe.transform;1816  if (computedTransform != null) {1817    let transform = computedTransform === 'none' ? '' : computedTransform;1818    transform = 'translate(20000px, 20000px) ' + transform;1819    keyframe.transform = transform;1820  }1821}18221823export function cloneRootViewTransitionContainer(1824  rootContainer: Container,1825): Instance {1826  // This implies that we're not going to animate the root document but instead1827  // the clone so we first clear the name of the root container.1828  const documentElement: null | HTMLElement =1829    rootContainer.nodeType === DOCUMENT_NODE1830      ? (rootContainer as any).documentElement1831      : rootContainer.ownerDocument.documentElement;1832  if (1833    documentElement !== null &&1834    // $FlowFixMe[prop-missing]1835    documentElement.style.viewTransitionName === ''1836  ) {1837    // $FlowFixMe[prop-missing]1838    documentElement.style.viewTransitionName = 'none';1839  }18401841  let containerInstance: HTMLElement;1842  if (rootContainer.nodeType === DOCUMENT_NODE) {1843    containerInstance = (rootContainer as any).body;1844  } else if (rootContainer.nodeName === 'HTML') {1845    containerInstance = rootContainer.ownerDocument.body as any;1846  } else if (1847    !disableCommentsAsDOMContainers &&1848    rootContainer.nodeType === COMMENT_NODE1849  ) {1850    throw new Error(1851      'Cannot use a startGestureTransition() with a comment node root.',1852    );1853  } else {1854    // If the container is not the whole document, then we ideally should probably1855    // clone the whole document outside of the React too.1856    containerInstance = rootContainer as any;1857  }18581859  const containerParent = containerInstance.parentNode;1860  if (containerParent === null) {1861    throw new Error(1862      'Cannot use a startGestureTransition() on a detached root.',1863    );1864  }18651866  const clone: HTMLElement = containerInstance.cloneNode(false);18671868  const computedStyle = getComputedStyle(containerInstance);18691870  if (1871    computedStyle.position === 'absolute' ||1872    computedStyle.position === 'fixed'1873  ) {1874    // If the style is already absolute, we don't have to do anything because it'll appear1875    // in the same place.1876  } else {1877    // Otherwise we need to absolutely position the clone in the same location as the original.1878    let positionedAncestor: HTMLElement = containerParent;1879    while (1880      positionedAncestor.parentNode != null &&1881      positionedAncestor.parentNode.nodeType !== DOCUMENT_NODE1882    ) {1883      if (getComputedStyle(positionedAncestor).position !== 'static') {1884        break;1885      }1886      // $FlowFixMe[incompatible-type]: This is refined.1887      positionedAncestor = positionedAncestor.parentNode;1888    }18891890    const positionedAncestorStyle: any = positionedAncestor.style;1891    const containerInstanceStyle: any = containerInstance.style;1892    // Clear the transform while we're measuring since it affects the bounding client rect.1893    const prevAncestorTranslate = positionedAncestorStyle.translate;1894    const prevAncestorScale = positionedAncestorStyle.scale;1895    const prevAncestorRotate = positionedAncestorStyle.rotate;1896    const prevAncestorTransform = positionedAncestorStyle.transform;1897    const prevTranslate = containerInstanceStyle.translate;1898    const prevScale = containerInstanceStyle.scale;1899    const prevRotate = containerInstanceStyle.rotate;1900    const prevTransform = containerInstanceStyle.transform;1901    positionedAncestorStyle.translate = 'none';1902    positionedAncestorStyle.scale = 'none';1903    positionedAncestorStyle.rotate = 'none';1904    positionedAncestorStyle.transform = 'none';1905    containerInstanceStyle.translate = 'none';1906    containerInstanceStyle.scale = 'none';1907    containerInstanceStyle.rotate = 'none';1908    containerInstanceStyle.transform = 'none';19091910    const ancestorRect = positionedAncestor.getBoundingClientRect();1911    const rect = containerInstance.getBoundingClientRect();19121913    const cloneStyle = clone.style;1914    cloneStyle.position = 'absolute';1915    cloneStyle.top = rect.top - ancestorRect.top + 'px';1916    cloneStyle.left = rect.left - ancestorRect.left + 'px';1917    cloneStyle.width = rect.width + 'px';1918    cloneStyle.height = rect.height + 'px';1919    cloneStyle.margin = '0px';1920    cloneStyle.boxSizing = 'border-box';19211922    positionedAncestorStyle.translate = prevAncestorTranslate;1923    positionedAncestorStyle.scale = prevAncestorScale;1924    positionedAncestorStyle.rotate = prevAncestorRotate;1925    positionedAncestorStyle.transform = prevAncestorTransform;1926    containerInstanceStyle.translate = prevTranslate;1927    containerInstanceStyle.scale = prevScale;1928    containerInstanceStyle.rotate = prevRotate;1929    containerInstanceStyle.transform = prevTransform;1930  }19311932  // For this transition the container will act as the root. Nothing outside of it should1933  // be affected anyway. This lets us transition from the cloned container to the original.1934  // $FlowFixMe[prop-missing]1935  clone.style.viewTransitionName = 'root';19361937  // Move out of the viewport so that it's still painted for the snapshot but is not visible1938  // for the frame where the snapshot happens.1939  moveOutOfViewport(computedStyle, clone);19401941  // Insert the clone after the root container as a sibling. This may inject a body1942  // as the next sibling of an existing body. document.body will still point to the1943  // first one and any id selectors will still find the first one. That's why it's1944  // important that it's after the existing node.1945  containerInstance.parentNode.insertBefore(1946    clone,1947    containerInstance.nextSibling,1948  );19491950  return clone;1951}19521953export function removeRootViewTransitionClone(1954  rootContainer: Container,1955  clone: Instance,1956): void {1957  let containerInstance: Instance;1958  if (rootContainer.nodeType === DOCUMENT_NODE) {1959    containerInstance = (rootContainer as any).body;1960  } else if (rootContainer.nodeName === 'HTML') {1961    containerInstance = rootContainer.ownerDocument.body as any;1962  } else {1963    // If the container is not the whole document, then we ideally should probably1964    // clone the whole document outside of the React too.1965    containerInstance = rootContainer as any;1966  }1967  const containerParent = containerInstance.parentNode;1968  if (containerParent === null) {1969    throw new Error(1970      'Cannot use a startGestureTransition() on a detached root.',1971    );1972  }1973  // We assume that the clone is still within the same parent.1974  containerParent.removeChild(clone);19751976  // Now the root is on the containerInstance itself until we call restoreRootViewTransitionName.1977  containerInstance.style.viewTransitionName = 'root';1978}19791980export type InstanceMeasurement = {1981  rect: ClientRect | DOMRect,1982  abs: boolean, // is absolutely positioned1983  clip: boolean, // is a clipping parent1984  view: boolean, // is in viewport bounds1985};19861987function createMeasurement(1988  rect: ClientRect | DOMRect,1989  computedStyle: CSSStyleDeclaration,1990  element: Element,1991): InstanceMeasurement {1992  const ownerWindow = element.ownerDocument.defaultView;1993  return {1994    rect: rect,1995    abs:1996      // Absolutely positioned instances don't contribute their size to the parent.1997      computedStyle.position === 'absolute' ||1998      computedStyle.position === 'fixed',1999    clip:2000      // If a ViewTransition boundary acts as a clipping parent group we should

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.