packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js JAVASCRIPT 7,003 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 7,003.
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  getFragmentRootContainerInfo,75  isFiberContainedByFragment,76  isFragmentContainedByFiber,77} from 'react-reconciler/src/ReactFiberTreeReflection';78import {compareDocumentPositionForEmptyFragment} from 'shared/ReactDOMFragmentRefShared';7980export {detachDeletedInstance};81import {hasRole} from './DOMAccessibilityRoles';82import type {SingletonType} from './ReactDOMComponent';83import {84  setInitialProperties,85  updateProperties,86  clearSingletonProperties,87  hydrateProperties,88  hydrateText,89  diffHydratedProperties,90  getPropsFromElement,91  diffHydratedText,92  trapClickOnNonInteractiveElement,93  clearClickListener,94} from './ReactDOMComponent';95import {hydrateInput} from './ReactDOMInput';96import {hydrateTextarea} from './ReactDOMTextarea';97import {hydrateSelect} from './ReactDOMSelect';98import {getSelectionInformation, restoreSelection} from './ReactInputSelection';99import setTextContent from './setTextContent';100import {101  validateDOMNesting,102  validateTextNesting,103  updatedAncestorInfoDev,104} from './validateDOMNesting';105import {106  isEnabled as ReactBrowserEventEmitterIsEnabled,107  setEnabled as ReactBrowserEventEmitterSetEnabled,108} from '../events/ReactDOMEventListener';109import {SVG_NAMESPACE, MATH_NAMESPACE} from './DOMNamespaces';110import {111  ELEMENT_NODE,112  TEXT_NODE,113  COMMENT_NODE,114  DOCUMENT_NODE,115  DOCUMENT_TYPE_NODE,116  DOCUMENT_FRAGMENT_NODE,117} from './HTMLNodeType';118119import {120  flushEventReplaying,121  retryIfBlockedOn,122} from '../events/ReactDOMEventReplaying';123124import {125  enableCreateEventHandleAPI,126  enableScopeAPI,127  disableLegacyMode,128  enableMoveBefore,129  disableCommentsAsDOMContainers,130  enableSuspenseyImages,131  enableSrcObject,132  enableViewTransition,133  enableHydrationChangeEvent,134  enableProfilerTimer,135} from 'shared/ReactFeatureFlags';136import {137  HostComponent,138  HostHoistable,139  HostText,140  HostSingleton,141} from 'react-reconciler/src/ReactWorkTags';142import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';143import {validateLinkPropsForStyleResource} from '../shared/ReactDOMResourceValidation';144import escapeSelectorAttributeValueInsideDoubleQuotes from './escapeSelectorAttributeValueInsideDoubleQuotes';145import {flushSyncWork as flushSyncWorkOnAllRoots} from 'react-reconciler/src/ReactFiberWorkLoop';146import {requestFormReset as requestFormResetOnFiber} from 'react-reconciler/src/ReactFiberHooks';147148import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';149150export {default as rendererVersion} from 'shared/ReactVersion';151152import noop from 'shared/noop';153import estimateBandwidth from './estimateBandwidth';154155export const rendererPackageName = 'react-dom';156export const extraDevToolsConfig = null;157158export type Type = string;159export type Props = {160  autoFocus?: boolean,161  children?: mixed,162  disabled?: boolean,163  hidden?: boolean,164  suppressHydrationWarning?: boolean,165  dangerouslySetInnerHTML?: mixed,166  style?: {167    display?: string,168    viewTransitionName?: string,169    'view-transition-name'?: string,170    viewTransitionClass?: string,171    'view-transition-class'?: string,172    margin?: string,173    marginTop?: string,174    'margin-top'?: string,175    marginBottom?: string,176    'margin-bottom'?: string,177    ...178  },179  bottom?: null | number,180  left?: null | number,181  right?: null | number,182  top?: null | number,183  is?: string,184  size?: number,185  value?: string,186  defaultValue?: string,187  checked?: boolean,188  defaultChecked?: boolean,189  multiple?: boolean,190  type?: string,191  src?: string | Blob | MediaSource | MediaStream, // TODO: Response192  srcSet?: string,193  loading?: 'eager' | 'lazy',194  onLoad?: (event: any) => void,195  ...196};197type RawProps = {198  [string]: mixed,199};200export type EventTargetChildElement = {201  type: string,202  props: null | {203    style?: {204      position?: string,205      zIndex?: number,206      bottom?: string,207      left?: string,208      right?: string,209      top?: string,210      ...211    },212    ...213  },214  ...215};216217export type Container =218  | interface extends Element {_reactRootContainer?: FiberRoot}219  | interface extends Document {_reactRootContainer?: FiberRoot}220  | interface extends DocumentFragment {_reactRootContainer?: FiberRoot};221export type Instance = Element;222export type TextInstance = Text;223224type InstanceWithFragmentHandles = Instance & {225  reactFragments?: Set<FragmentInstanceType>,226};227type HostNodeWithFragmentHandles = (Instance | TextInstance) & {228  reactFragments?: Set<FragmentInstanceType>,229};230231declare class ActivityInterface extends Comment {}232declare class SuspenseInterface extends Comment {233  _reactRetry: void | (() => void);234}235236export type ActivityInstance = ActivityInterface;237export type SuspenseInstance = SuspenseInterface;238239type FormStateMarkerInstance = Comment;240export type HydratableInstance =241  | Instance242  | TextInstance243  | ActivityInstance244  | SuspenseInstance245  | FormStateMarkerInstance;246export type PublicInstance = Element | Text;247export type HostContextDev = {248  context: HostContextProd,249  ancestorInfo: AncestorInfoDev,250};251type HostContextProd = HostContextNamespace;252export type HostContext = HostContextDev | HostContextProd;253export type UpdatePayload = Array<mixed>;254export type ChildSet = void; // Unused255export type TimeoutHandle = TimeoutID;256export type NoTimeout = -1;257export type RendererInspectionConfig = $ReadOnly<{}>;258259export type TransitionStatus = FormStatus;260261export type ViewTransitionInstance = {262  name: string,263  group: mixin$Animatable,264  imagePair: mixin$Animatable,265  old: mixin$Animatable,266  new: mixin$Animatable,267};268269type SelectionInformation = {270  focusedElem: null | HTMLElement,271  selectionRange: mixed,272};273274const SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';275276const ACTIVITY_START_DATA = '&';277const ACTIVITY_END_DATA = '/&';278const SUSPENSE_START_DATA = '$';279const SUSPENSE_END_DATA = '/$';280const SUSPENSE_PENDING_START_DATA = '$?';281const SUSPENSE_QUEUED_START_DATA = '$~';282const SUSPENSE_FALLBACK_START_DATA = '$!';283const PREAMBLE_CONTRIBUTION_HTML = 'html';284const PREAMBLE_CONTRIBUTION_BODY = 'body';285const PREAMBLE_CONTRIBUTION_HEAD = 'head';286const FORM_STATE_IS_MATCHING = 'F!';287const FORM_STATE_IS_NOT_MATCHING = 'F';288289const DOCUMENT_READY_STATE_LOADING = 'loading';290291const STYLE = 'style';292293opaque type HostContextNamespace = 0 | 1 | 2;294export const HostContextNamespaceNone: HostContextNamespace = 0;295const HostContextNamespaceSvg: HostContextNamespace = 1;296const HostContextNamespaceMath: HostContextNamespace = 2;297298let eventsEnabled: ?boolean = null;299let selectionInformation: null | SelectionInformation = null;300301export * from 'react-reconciler/src/ReactFiberConfigWithNoPersistence';302303function getOwnerDocumentFromRootContainer(304  rootContainerElement: Element | Document | DocumentFragment,305): Document {306  return rootContainerElement.nodeType === DOCUMENT_NODE307    ? (rootContainerElement as any)308    : rootContainerElement.ownerDocument;309}310311export function getRootHostContext(312  rootContainerInstance: Container,313): HostContext {314  let type;315  let context: HostContextProd;316  const nodeType = rootContainerInstance.nodeType;317  switch (nodeType) {318    case DOCUMENT_NODE:319    case DOCUMENT_FRAGMENT_NODE: {320      type = nodeType === DOCUMENT_NODE ? '#document' : '#fragment';321      const root = (rootContainerInstance as any).documentElement;322      if (root) {323        const namespaceURI = root.namespaceURI;324        context = namespaceURI325          ? getOwnHostContext(namespaceURI)326          : HostContextNamespaceNone;327      } else {328        context = HostContextNamespaceNone;329      }330      break;331    }332    default: {333      const container: any =334        !disableCommentsAsDOMContainers && nodeType === COMMENT_NODE335          ? rootContainerInstance.parentNode336          : rootContainerInstance;337      type = container.tagName;338      const namespaceURI = container.namespaceURI;339      if (!namespaceURI) {340        switch (type) {341          case 'svg':342            context = HostContextNamespaceSvg;343            break;344          case 'math':345            context = HostContextNamespaceMath;346            break;347          default:348            context = HostContextNamespaceNone;349            break;350        }351      } else {352        const ownContext = getOwnHostContext(namespaceURI);353        context = getChildHostContextProd(ownContext, type);354      }355      break;356    }357  }358  if (__DEV__) {359    const validatedTag = type.toLowerCase();360    const ancestorInfo = updatedAncestorInfoDev(null, validatedTag);361    return {context, ancestorInfo};362  }363  return context;364}365366function getOwnHostContext(namespaceURI: string): HostContextNamespace {367  switch (namespaceURI) {368    case SVG_NAMESPACE:369      return HostContextNamespaceSvg;370    case MATH_NAMESPACE:371      return HostContextNamespaceMath;372    default:373      return HostContextNamespaceNone;374  }375}376377function getChildHostContextProd(378  parentNamespace: HostContextNamespace,379  type: string,380): HostContextNamespace {381  if (parentNamespace === HostContextNamespaceNone) {382    // No (or default) parent namespace: potential entry point.383    switch (type) {384      case 'svg':385        return HostContextNamespaceSvg;386      case 'math':387        return HostContextNamespaceMath;388      default:389        return HostContextNamespaceNone;390    }391  }392  if (parentNamespace === HostContextNamespaceSvg && type === 'foreignObject') {393    // We're leaving SVG.394    return HostContextNamespaceNone;395  }396  // By default, pass namespace below.397  return parentNamespace;398}399400export function getChildHostContext(401  parentHostContext: HostContext,402  type: string,403): HostContext {404  if (__DEV__) {405    const parentHostContextDev = parentHostContext as any as HostContextDev;406    const context = getChildHostContextProd(parentHostContextDev.context, type);407    const ancestorInfo = updatedAncestorInfoDev(408      parentHostContextDev.ancestorInfo,409      type,410    );411    return {context, ancestorInfo};412  }413  const parentNamespace = parentHostContext as any as HostContextProd;414  return getChildHostContextProd(parentNamespace, type);415}416417export function getPublicInstance(instance: Instance): Instance {418  return instance;419}420421export function prepareForCommit(containerInfo: Container): Object | null {422  eventsEnabled = ReactBrowserEventEmitterIsEnabled();423  selectionInformation = getSelectionInformation(containerInfo);424  let activeInstance = null;425  if (enableCreateEventHandleAPI) {426    const focusedElem = selectionInformation.focusedElem;427    if (focusedElem !== null) {428      activeInstance = getClosestInstanceFromNode(focusedElem);429    }430  }431  ReactBrowserEventEmitterSetEnabled(false);432  return activeInstance;433}434435export function beforeActiveInstanceBlur(internalInstanceHandle: Object): void {436  if (enableCreateEventHandleAPI) {437    ReactBrowserEventEmitterSetEnabled(true);438    dispatchBeforeDetachedBlur(439      (selectionInformation as any).focusedElem,440      internalInstanceHandle,441    );442    ReactBrowserEventEmitterSetEnabled(false);443  }444}445446export function afterActiveInstanceBlur(): void {447  if (enableCreateEventHandleAPI) {448    ReactBrowserEventEmitterSetEnabled(true);449    dispatchAfterDetachedBlur((selectionInformation as any).focusedElem);450    ReactBrowserEventEmitterSetEnabled(false);451  }452}453454export function resetAfterCommit(containerInfo: Container): void {455  restoreSelection(selectionInformation, containerInfo);456  ReactBrowserEventEmitterSetEnabled(eventsEnabled);457  eventsEnabled = null;458  selectionInformation = null;459}460461export function createHoistableInstance(462  type: string,463  props: Props,464  rootContainerInstance: Container,465  internalInstanceHandle: Object,466): Instance {467  const ownerDocument = getOwnerDocumentFromRootContainer(468    rootContainerInstance,469  );470471  const domElement: Instance = ownerDocument.createElement(type);472  precacheFiberNode(internalInstanceHandle, domElement);473  updateFiberProps(domElement, props);474  setInitialProperties(domElement, type, props);475  markNodeAsHoistable(domElement);476  return domElement;477}478479let didWarnScriptTags = false;480function isScriptDataBlock(props: Props): boolean {481  const scriptType = props.type;482  if (typeof scriptType !== 'string' || scriptType === '') {483    return false;484  }485  const lower = scriptType.toLowerCase();486  // Special non-MIME keywords recognized by the HTML spec487  // TODO: May be fine to also not warn about having these types be parsed as "parser-inserted"488  if (489    lower === 'module' ||490    lower === 'importmap' ||491    lower === 'speculationrules'492  ) {493    return false;494  }495  // JavaScript MIME types per https://mimesniff.spec.whatwg.org/#javascript-mime-type496  switch (lower) {497    case 'application/ecmascript':498    case 'application/javascript':499    case 'application/x-ecmascript':500    case 'application/x-javascript':501    case 'text/ecmascript':502    case 'text/javascript':503    case 'text/javascript1.0':504    case 'text/javascript1.1':505    case 'text/javascript1.2':506    case 'text/javascript1.3':507    case 'text/javascript1.4':508    case 'text/javascript1.5':509    case 'text/jscript':510    case 'text/livescript':511    case 'text/x-ecmascript':512    case 'text/x-javascript':513      return false;514  }515  // Any other non-empty type value means this is a data block516  return true;517}518const warnedUnknownTags: {519  [key: string]: boolean,520} = {521  // There are working polyfills for <dialog>. Let people use it.522  dialog: true,523  // Electron ships a custom <webview> tag to display external web content in524  // an isolated frame and process.525  // This tag is not present in non Electron environments such as JSDom which526  // is often used for testing purposes.527  // @see https://electronjs.org/docs/api/webview-tag528  webview: true,529};530531export function createInstance(532  type: string,533  props: Props,534  rootContainerInstance: Container,535  hostContext: HostContext,536  internalInstanceHandle: Object,537): Instance {538  let hostContextProd: HostContextProd;539  if (__DEV__) {540    // TODO: take namespace into account when validating.541    const hostContextDev: HostContextDev = hostContext as any;542    validateDOMNesting(type, hostContextDev.ancestorInfo);543    hostContextProd = hostContextDev.context;544  } else {545    hostContextProd = hostContext as any;546  }547548  const ownerDocument = getOwnerDocumentFromRootContainer(549    rootContainerInstance,550  );551552  let domElement: Instance;553  switch (hostContextProd) {554    case HostContextNamespaceSvg:555      domElement = ownerDocument.createElementNS(SVG_NAMESPACE, type);556      break;557    case HostContextNamespaceMath:558      domElement = ownerDocument.createElementNS(MATH_NAMESPACE, type);559      break;560    default:561      switch (type) {562        case 'svg': {563          domElement = ownerDocument.createElementNS(SVG_NAMESPACE, type);564          break;565        }566        case 'math': {567          domElement = ownerDocument.createElementNS(MATH_NAMESPACE, type);568          break;569        }570        case 'script': {571          // Create the script via .innerHTML so its "parser-inserted" flag is572          // set to true and it does not execute573          const div = ownerDocument.createElement('div');574          if (__DEV__) {575            if (576              !didWarnScriptTags &&577              // Data block scripts are not executed by UAs anyway so578              // we don't need to warn: https://html.spec.whatwg.org/multipage/scripting.html#attr-script-type579              !isScriptDataBlock(props)580            ) {581              console.error(582                'Encountered a script tag while rendering React component. ' +583                  'Scripts inside React components are never executed when rendering ' +584                  'on the client. Consider using template tag instead ' +585                  '(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).',586              );587              didWarnScriptTags = true;588            }589          }590          div.innerHTML = '<script><' + '/script>';591          // This is guaranteed to yield a script element.592          const firstChild = div.firstChild as any as HTMLScriptElement;593          domElement = div.removeChild(firstChild);594          break;595        }596        case 'select': {597          if (typeof props.is === 'string') {598            domElement = ownerDocument.createElement('select', {is: props.is});599          } else {600            // Separate else branch instead of using `props.is || undefined` above because of a Firefox bug.601            // See discussion in https://github.com/react/react/pull/6896602            // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240603            domElement = ownerDocument.createElement('select');604          }605          if (props.multiple) {606            domElement.multiple = true;607          } else if (props.size) {608            // Setting a size greater than 1 causes a select to behave like `multiple=true`, where609            // it is possible that no option is selected.610            //611            // This is only necessary when a select in "single selection mode".612            domElement.size = props.size;613          }614          break;615        }616        default: {617          if (typeof props.is === 'string') {618            domElement = ownerDocument.createElement(type, {is: props.is});619          } else {620            // Separate else branch instead of using `props.is || undefined` above because of a Firefox bug.621            // See discussion in https://github.com/react/react/pull/6896622            // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240623            domElement = ownerDocument.createElement(type);624          }625626          if (__DEV__) {627            if (type.indexOf('-') === -1) {628              // We're not SVG/MathML and we don't have a dash, so we're not a custom element629              // Even if you use `is`, these should be of known type and lower case.630              if (type !== type.toLowerCase()) {631                console.error(632                  '<%s /> is using incorrect casing. ' +633                    'Use PascalCase for React components, ' +634                    'or lowercase for HTML elements.',635                  type,636                );637              }638              if (639                // $FlowFixMe[method-unbinding]640                Object.prototype.toString.call(domElement) ===641                  '[object HTMLUnknownElement]' &&642                !hasOwnProperty.call(warnedUnknownTags, type)643              ) {644                warnedUnknownTags[type] = true;645                console.error(646                  'The tag <%s> is unrecognized in this browser. ' +647                    'If you meant to render a React component, start its name with ' +648                    'an uppercase letter.',649                  type,650                );651              }652            }653          }654        }655      }656  }657  precacheFiberNode(internalInstanceHandle, domElement);658  updateFiberProps(domElement, props);659  return domElement;660}661662let didWarnForClone = false;663664export function cloneMutableInstance(665  instance: Instance,666  keepChildren: boolean,667): Instance {668  if (__DEV__) {669    // Warn for problematic670    const tagName = instance.tagName;671    switch (tagName) {672      case 'VIDEO':673      case 'IFRAME':674        if (!didWarnForClone) {675          didWarnForClone = true;676          // TODO: Once we have the ability to avoid cloning the root, suggest an absolutely677          // positioned ViewTransition instead as the solution.678          console.warn(679            'startGestureTransition() required cloning a <%s> element since it exists in ' +680              'both states of the gesture. This can be problematic since it will load it twice ' +681              'Try removing or hiding it with <Activity mode="offscreen"> in the optimistic state.',682            tagName.toLowerCase(),683          );684        }685        break;686    }687  }688  return instance.cloneNode(keepChildren);689}690691export function appendInitialChild(692  parentInstance: Instance,693  child: Instance | TextInstance,694): void {695  // Note: This should not use moveBefore() because initial are appended while disconnected.696  parentInstance.appendChild(child);697}698699export function finalizeInitialChildren(700  domElement: Instance,701  type: string,702  props: Props,703  hostContext: HostContext,704): boolean {705  setInitialProperties(domElement, type, props);706  switch (type) {707    case 'button':708    case 'input':709    case 'select':710    case 'textarea':711      return !!props.autoFocus;712    case 'img':713      return true;714    default:715      return false;716  }717}718719export function finalizeHydratedChildren(720  domElement: Instance,721  type: string,722  props: Props,723  hostContext: HostContext,724): boolean {725  // TOOD: Consider unifying this with hydrateInstance.726  if (!enableHydrationChangeEvent) {727    return false;728  }729  switch (type) {730    case 'input':731    case 'select':732    case 'textarea':733    case 'img':734      return true;735    default:736      return false;737  }738}739740export function shouldSetTextContent(type: string, props: Props): boolean {741  return (742    type === 'textarea' ||743    type === 'noscript' ||744    typeof props.children === 'string' ||745    typeof props.children === 'number' ||746    typeof props.children === 'bigint' ||747    (typeof props.dangerouslySetInnerHTML === 'object' &&748      props.dangerouslySetInnerHTML !== null &&749      props.dangerouslySetInnerHTML.__html != null)750  );751}752753export function createTextInstance(754  text: string,755  rootContainerInstance: Container,756  hostContext: HostContext,757  internalInstanceHandle: Object,758): TextInstance {759  if (__DEV__) {760    const hostContextDev = hostContext as any as HostContextDev;761    const ancestor = hostContextDev.ancestorInfo.current;762    if (ancestor != null) {763      validateTextNesting(764        text,765        ancestor.tag,766        hostContextDev.ancestorInfo.implicitRootScope,767      );768    }769  }770  const textNode: TextInstance = getOwnerDocumentFromRootContainer(771    rootContainerInstance,772  ).createTextNode(text);773  precacheFiberNode(internalInstanceHandle, textNode);774  return textNode;775}776777export function cloneMutableTextInstance(778  textInstance: TextInstance,779): TextInstance {780  return textInstance.cloneNode(false);781}782783let currentPopstateTransitionEvent: Event | null = null;784export function shouldAttemptEagerTransition(): boolean {785  const event = window.event;786  if (event && event.type === 'popstate') {787    // This is a popstate event. Attempt to render any transition during this788    // event synchronously. Unless we already attempted during this event.789    if (event === currentPopstateTransitionEvent) {790      // We already attempted to render this popstate transition synchronously.791      // Any subsequent attempts must have happened as the result of a derived792      // update, like startTransition inside useEffect, or useDV. Switch back to793      // the default behavior for all remaining transitions during the current794      // popstate event.795      return false;796    } else {797      // Cache the current event in case a derived transition is scheduled.798      // (Refer to previous branch.)799      currentPopstateTransitionEvent = event;800      return true;801    }802  }803  // We're not inside a popstate event.804  currentPopstateTransitionEvent = null;805  return false;806}807808let schedulerEvent: void | Event = undefined;809export function trackSchedulerEvent(): void {810  schedulerEvent = window.event;811}812813export function resolveEventType(): null | string {814  const event = window.event;815  return event && event !== schedulerEvent ? event.type : null;816}817818export function resolveEventTimeStamp(): number {819  const event = window.event;820  return event && event !== schedulerEvent ? event.timeStamp : -1.1;821}822823export const isPrimaryRenderer = true;824export const warnsIfNotActing = true;825// This initialization code may run even on server environments826// if a component just imports ReactDOM (e.g. for findDOMNode).827// Some environments might not have setTimeout or clearTimeout.828export const scheduleTimeout: any =829  typeof setTimeout === 'function' ? setTimeout : (undefined as any);830export const cancelTimeout: any =831  typeof clearTimeout === 'function' ? clearTimeout : (undefined as any);832export const noTimeout: -1 = -1;833const localPromise = typeof Promise === 'function' ? Promise : undefined;834const localRequestAnimationFrame =835  typeof requestAnimationFrame === 'function'836    ? requestAnimationFrame837    : scheduleTimeout;838839export {getClosestInstanceFromNode as getInstanceFromNode};840841export function preparePortalMount(portalInstance: Instance): void {842  listenToAllSupportedEvents(portalInstance);843}844845export function prepareScopeUpdate(846  scopeInstance: ReactScopeInstance,847  internalInstanceHandle: Object,848): void {849  if (enableScopeAPI) {850    precacheFiberNode(internalInstanceHandle, scopeInstance);851  }852}853854export function getInstanceFromScope(855  scopeInstance: ReactScopeInstance,856): null | Object {857  if (enableScopeAPI) {858    return getFiberFromScopeInstance(scopeInstance);859  }860  return null;861}862863// -------------------864//     Microtasks865// -------------------866export const supportsMicrotasks: boolean = true;867export const scheduleMicrotask: any =868  typeof queueMicrotask === 'function'869    ? queueMicrotask870    : typeof localPromise !== 'undefined'871      ? callback =>872          localPromise.resolve(null).then(callback).catch(handleErrorInNextTick)873      : scheduleTimeout; // TODO: Determine the best fallback here.874875function handleErrorInNextTick(error: any) {876  setTimeout(() => {877    throw error;878  });879}880881// -------------------882//     Mutation883// -------------------884885export const supportsMutation: boolean = true;886887export function commitMount(888  domElement: Instance,889  type: string,890  newProps: Props,891  internalInstanceHandle: Object,892): void {893  // Despite the naming that might imply otherwise, this method only894  // fires if there is an `Update` effect scheduled during mounting.895  // This happens if `finalizeInitialChildren` returns `true` (which it896  // does to implement the `autoFocus` attribute on the client). But897  // there are also other cases when this might happen (such as patching898  // up text content during hydration mismatch). So we'll check this again.899  switch (type) {900    case 'button':901    case 'input':902    case 'select':903    case 'textarea':904      if (newProps.autoFocus) {905        (906          domElement as any as907            | HTMLButtonElement908            | HTMLInputElement909            | HTMLSelectElement910            | HTMLTextAreaElement911        ).focus();912      }913      return;914    case 'img': {915      // The technique here is to assign the src or srcSet property to cause the browser916      // to issue a new load event. If it hasn't loaded yet it'll fire whenever the load actually completes.917      // If it has already loaded we missed it so the second load will still be the first one that executes918      // any associated onLoad props.919      // Even if we have srcSet we prefer to reassign src. The reason is that Firefox does not trigger a new920      // load event when only srcSet is assigned. Chrome will trigger a load event if either is assigned so we921      // only need to assign one. And Safari just never triggers a new load event which means this technique922      // is already a noop regardless of which properties are assigned. We should revisit if browsers update923      // this heuristic in the future.924      if (newProps.src) {925        const src = (newProps as any).src;926        if (enableSrcObject && typeof src === 'object') {927          // For object src, we can't just set the src again to the same blob URL because it might have928          // already revoked if it loaded before this. However, we can create a new blob URL and set that.929          // This is relatively cheap since the blob is already in memory but this might cause some930          // duplicated work.931          // TODO: We could maybe detect if load hasn't fired yet and if so reuse the URL.932          try {933            setSrcObject(domElement, type, src);934            return;935          } catch (x) {936            // If URL.createObjectURL() errors, it was probably some other object type937            // that should be toString:ed instead, so we just fall-through to the normal938            // path.939          }940        }941        (domElement as any as HTMLImageElement).src = src;942      } else if (newProps.srcSet) {943        (domElement as any as HTMLImageElement).srcset = (944          newProps as any945        ).srcSet;946      }947      return;948    }949  }950}951952export function commitHydratedInstance(953  domElement: Instance,954  type: string,955  props: Props,956  internalInstanceHandle: Object,957): void {958  if (!enableHydrationChangeEvent) {959    return;960  }961  // This fires in the commit phase if a hydrated instance needs to do further962  // work in the commit phase. Similar to commitMount. However, this should not963  // do things that would've already happened such as set auto focus since that964  // would steal focus. It's only scheduled if finalizeHydratedChildren returns965  // true.966  switch (type) {967    case 'input': {968      hydrateInput(969        domElement,970        props.value,971        props.defaultValue,972        props.checked,973        props.defaultChecked,974      );975      break;976    }977    case 'select': {978      hydrateSelect(979        domElement,980        props.value,981        props.defaultValue,982        props.multiple,983      );984      break;985    }986    case 'textarea':987      hydrateTextarea(domElement, props.value, props.defaultValue);988      break;989    case 'img':990      // TODO: Should we replay onLoad events?991      break;992  }993}994995export function commitUpdate(996  domElement: Instance,997  type: string,998  oldProps: Props,999  newProps: Props,1000  internalInstanceHandle: Object,1001): void {1002  // Diff and update the properties.1003  updateProperties(domElement, type, oldProps, newProps);10041005  // Update the props handle so that we know which props are the ones with1006  // with current event handlers.1007  updateFiberProps(domElement, newProps);1008}10091010export function resetTextContent(domElement: Instance): void {1011  setTextContent(domElement, '');1012}10131014export function commitTextUpdate(1015  textInstance: TextInstance,1016  oldText: string,1017  newText: string,1018): void {1019  textInstance.nodeValue = newText;1020}10211022const supportsMoveBefore =1023  // $FlowFixMe[prop-missing]: We're doing the feature detection here.1024  enableMoveBefore &&1025  typeof window !== 'undefined' &&1026  typeof window.Element.prototype.moveBefore === 'function';10271028export function appendChild(1029  parentInstance: Instance,1030  child: Instance | TextInstance,1031): void {1032  if (supportsMoveBefore && child.parentNode !== null) {1033    // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore.1034    parentInstance.moveBefore(child, null);1035  } else {1036    parentInstance.appendChild(child);1037  }1038}10391040function warnForReactChildrenConflict(container: Container): void {1041  if (__DEV__) {1042    if ((container as any).__reactWarnedAboutChildrenConflict) {1043      return;1044    }1045    const props = getFiberCurrentPropsFromNode(container);1046    if (props !== null) {1047      const fiber = getInstanceFromNode(container);1048      if (fiber !== null) {1049        if (1050          typeof props.children === 'string' ||1051          typeof props.children === 'number'1052        ) {1053          (container as any).__reactWarnedAboutChildrenConflict = true;1054          // Run the warning with the Fiber of the container for context of where the children are specified.1055          // We could also maybe use the Portal. The current execution context is the child being added.1056          runWithFiberInDEV(fiber, () => {1057            console.error(1058              'Cannot use a ref on a React element as a container to `createRoot` or `createPortal` ' +1059                'if that element also sets "children" text content using React. It should be a leaf with no children. ' +1060                "Otherwise it's ambiguous which children should be used.",1061            );1062          });1063        } else if (props.dangerouslySetInnerHTML != null) {1064          (container as any).__reactWarnedAboutChildrenConflict = true;1065          runWithFiberInDEV(fiber, () => {1066            console.error(1067              'Cannot use a ref on a React element as a container to `createRoot` or `createPortal` ' +1068                'if that element also sets "dangerouslySetInnerHTML" using React. It should be a leaf with no children. ' +1069                "Otherwise it's ambiguous which children should be used.",1070            );1071          });1072        }1073      }1074    }1075  }1076}10771078export function appendChildToContainer(1079  container: Container,1080  child: Instance | TextInstance,1081): void {1082  if (__DEV__) {1083    warnForReactChildrenConflict(container);1084  }1085  let parentNode: DocumentFragment | Element;1086  if (container.nodeType === DOCUMENT_NODE) {1087    parentNode = (container as any).body;1088  } else if (1089    !disableCommentsAsDOMContainers &&1090    container.nodeType === COMMENT_NODE1091  ) {1092    parentNode = container.parentNode as any;1093    if (supportsMoveBefore && child.parentNode !== null) {1094      // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore.1095      parentNode.moveBefore(child, container);1096    } else {1097      parentNode.insertBefore(child, container);1098    }1099    return;1100  } else if (container.nodeName === 'HTML') {1101    parentNode = container.ownerDocument.body as any;1102  } else {1103    parentNode = container as any;1104  }1105  if (supportsMoveBefore && child.parentNode !== null) {1106    // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore.1107    parentNode.moveBefore(child, null);1108  } else {1109    parentNode.appendChild(child);1110  }11111112  // This container might be used for a portal.1113  // If something inside a portal is clicked, that click should bubble1114  // through the React tree. However, on Mobile Safari the click would1115  // never bubble through the *DOM* tree unless an ancestor with onclick1116  // event exists. So we wouldn't see it and dispatch it.1117  // This is why we ensure that non React root containers have inline onclick1118  // defined.1119  // https://github.com/react/react/issues/119181120  const reactRootContainer = container._reactRootContainer;1121  if (1122    // $FlowFixMe[invalid-compare]1123    (reactRootContainer === null || reactRootContainer === undefined) &&1124    parentNode.onclick === null1125  ) {1126    // TODO: This cast may not be sound for SVG, MathML or custom elements.1127    trapClickOnNonInteractiveElement(parentNode as any as HTMLElement);1128  }1129}11301131export function insertBefore(1132  parentInstance: Instance,1133  child: Instance | TextInstance,1134  beforeChild: Instance | TextInstance | SuspenseInstance | ActivityInstance,1135): void {1136  if (supportsMoveBefore && child.parentNode !== null) {1137    // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore.1138    parentInstance.moveBefore(child, beforeChild);1139  } else {1140    parentInstance.insertBefore(child, beforeChild);1141  }1142}11431144export function insertInContainerBefore(1145  container: Container,1146  child: Instance | TextInstance,1147  beforeChild: Instance | TextInstance | SuspenseInstance | ActivityInstance,1148): void {1149  if (__DEV__) {1150    warnForReactChildrenConflict(container);1151  }1152  let parentNode: DocumentFragment | Element;1153  if (container.nodeType === DOCUMENT_NODE) {1154    parentNode = (container as any).body;1155  } else if (1156    !disableCommentsAsDOMContainers &&1157    container.nodeType === COMMENT_NODE1158  ) {1159    parentNode = container.parentNode as any;1160  } else if (container.nodeName === 'HTML') {1161    parentNode = container.ownerDocument.body as any;1162  } else {1163    parentNode = container as any;1164  }1165  if (supportsMoveBefore && child.parentNode !== null) {1166    // $FlowFixMe[prop-missing]: We've checked this with supportsMoveBefore.1167    parentNode.moveBefore(child, beforeChild);1168  } else {1169    parentNode.insertBefore(child, beforeChild);1170  }1171}11721173export function isSingletonScope(type: string): boolean {1174  return type === 'head';1175}11761177function createEvent(type: DOMEventName, bubbles: boolean): Event {1178  const event = document.createEvent('Event');1179  event.initEvent(type as any as string, bubbles, false);1180  return event;1181}11821183function dispatchBeforeDetachedBlur(1184  target: HTMLElement,1185  internalInstanceHandle: Object,1186): void {1187  if (enableCreateEventHandleAPI) {1188    const event = createEvent('beforeblur', true);1189    // Dispatch "beforeblur" directly on the target,1190    // so it gets picked up by the event system and1191    // can propagate through the React internal tree.1192    // $FlowFixMe[prop-missing]: internal field1193    event._detachedInterceptFiber = internalInstanceHandle;1194    target.dispatchEvent(event);1195  }1196}11971198function dispatchAfterDetachedBlur(target: HTMLElement): void {1199  if (enableCreateEventHandleAPI) {1200    const event = createEvent('afterblur', false);1201    // So we know what was detached, make the relatedTarget the1202    // detached target on the "afterblur" event.1203    (event as any).relatedTarget = target;1204    // Dispatch the event on the document.1205    document.dispatchEvent(event);1206  }1207}12081209export function removeChild(1210  parentInstance: Instance,1211  child: Instance | TextInstance | SuspenseInstance | ActivityInstance,1212): void {1213  parentInstance.removeChild(child);1214}12151216export function removeChildFromContainer(1217  container: Container,1218  child: Instance | TextInstance | SuspenseInstance | ActivityInstance,1219): void {1220  let parentNode: DocumentFragment | Element;1221  if (container.nodeType === DOCUMENT_NODE) {1222    parentNode = (container as any).body;1223  } else if (1224    !disableCommentsAsDOMContainers &&1225    container.nodeType === COMMENT_NODE1226  ) {1227    parentNode = container.parentNode as any;1228  } else if (container.nodeName === 'HTML') {1229    parentNode = container.ownerDocument.body as any;1230  } else {1231    parentNode = container as any;1232  }1233  parentNode.removeChild(child);1234}12351236function clearHydrationBoundary(1237  parentInstance: Instance,1238  hydrationInstance: SuspenseInstance | ActivityInstance,1239): void {1240  let node: Node = hydrationInstance;1241  // Delete all nodes within this suspense boundary.1242  // There might be nested nodes so we need to keep track of how1243  // deep we are and only break out when we're back on top.1244  let depth = 0;1245  do {1246    const nextNode = node.nextSibling;1247    parentInstance.removeChild(node);1248    if (nextNode && nextNode.nodeType === COMMENT_NODE) {1249      const data = (nextNode as any).data as string;1250      if (data === SUSPENSE_END_DATA || data === ACTIVITY_END_DATA) {1251        if (depth === 0) {1252          parentInstance.removeChild(nextNode);1253          // Retry if any event replaying was blocked on this.1254          retryIfBlockedOn(hydrationInstance);1255          return;1256        } else {1257          depth--;1258        }1259      } else if (1260        data === SUSPENSE_START_DATA ||1261        data === SUSPENSE_PENDING_START_DATA ||1262        data === SUSPENSE_QUEUED_START_DATA ||1263        data === SUSPENSE_FALLBACK_START_DATA ||1264        data === ACTIVITY_START_DATA1265      ) {1266        depth++;1267      } else if (data === PREAMBLE_CONTRIBUTION_HTML) {1268        // If a preamble contribution marker is found within the bounds of this boundary,1269        // then it contributed to the html tag and we need to reset it.1270        const ownerDocument = parentInstance.ownerDocument;1271        const documentElement: Element = ownerDocument.documentElement as any;1272        clearSingletonPreambleContribution(documentElement);1273      } else if (data === PREAMBLE_CONTRIBUTION_HEAD) {1274        const ownerDocument = parentInstance.ownerDocument;1275        const head: Element = ownerDocument.head as any;1276        clearSingletonPreambleContribution(head);1277        // We need to clear the head because this is the only singleton that can have children that1278        // were part of this boundary but are not inside this boundary.1279        clearHead(head);1280      } else if (data === PREAMBLE_CONTRIBUTION_BODY) {1281        const ownerDocument = parentInstance.ownerDocument;1282        const body: Element = ownerDocument.body as any;1283        clearSingletonPreambleContribution(body);1284      }1285    }1286    // $FlowFixMe[incompatible-type] we bail out when we get a null1287    node = nextNode;1288  } while (node);1289  // TODO: Warn, we didn't find the end comment boundary.1290  // Retry if any event replaying was blocked on this.1291  retryIfBlockedOn(hydrationInstance);1292}12931294export function clearActivityBoundary(1295  parentInstance: Instance,1296  activityInstance: ActivityInstance,1297): void {1298  clearHydrationBoundary(parentInstance, activityInstance);1299}13001301export function clearSuspenseBoundary(1302  parentInstance: Instance,1303  suspenseInstance: SuspenseInstance,1304): void {1305  clearHydrationBoundary(parentInstance, suspenseInstance);1306}13071308function clearHydrationBoundaryFromContainer(1309  container: Container,1310  hydrationInstance: SuspenseInstance | ActivityInstance,1311): void {1312  let parentNode: DocumentFragment | Element;1313  if (container.nodeType === DOCUMENT_NODE) {1314    parentNode = (container as any).body;1315  } else if (1316    !disableCommentsAsDOMContainers &&1317    container.nodeType === COMMENT_NODE1318  ) {1319    parentNode = container.parentNode as any;1320  } else if (container.nodeName === 'HTML') {1321    parentNode = container.ownerDocument.body as any;1322  } else {1323    parentNode = container as any;1324  }1325  clearHydrationBoundary(parentNode, hydrationInstance);1326  // Retry if any event replaying was blocked on this.1327  retryIfBlockedOn(container);1328}13291330export function clearActivityBoundaryFromContainer(1331  container: Container,1332  activityInstance: ActivityInstance,1333): void {1334  clearHydrationBoundaryFromContainer(container, activityInstance);1335}13361337export function clearSuspenseBoundaryFromContainer(1338  container: Container,1339  suspenseInstance: SuspenseInstance,1340): void {1341  clearHydrationBoundaryFromContainer(container, suspenseInstance);1342}13431344function hideOrUnhideDehydratedBoundary(1345  suspenseInstance: SuspenseInstance | ActivityInstance,1346  isHidden: boolean,1347) {1348  let node: Node = suspenseInstance;1349  // Unhide all nodes within this suspense boundary.1350  let depth = 0;1351  do {1352    const nextNode = node.nextSibling;1353    if (node.nodeType === ELEMENT_NODE) {1354      const instance = node as any as HTMLElement & {_stashedDisplay?: string};1355      if (isHidden) {1356        instance._stashedDisplay = instance.style.display;1357        instance.style.display = 'none';1358      } else {1359        instance.style.display = instance._stashedDisplay || '';1360        if (instance.getAttribute('style') === '') {1361          instance.removeAttribute('style');1362        }1363      }1364    } else if (node.nodeType === TEXT_NODE) {1365      const textNode = node as any as Text & {_stashedText?: string};1366      if (isHidden) {1367        textNode._stashedText = textNode.nodeValue;1368        textNode.nodeValue = '';1369      } else {1370        textNode.nodeValue = textNode._stashedText || '';1371      }1372    }1373    if (nextNode && nextNode.nodeType === COMMENT_NODE) {1374      const data = (nextNode as any).data as string;1375      if (data === SUSPENSE_END_DATA || data === ACTIVITY_END_DATA) {1376        if (depth === 0) {1377          return;1378        } else {1379          depth--;1380        }1381      } else if (1382        data === SUSPENSE_START_DATA ||1383        data === SUSPENSE_PENDING_START_DATA ||1384        data === SUSPENSE_QUEUED_START_DATA ||1385        data === SUSPENSE_FALLBACK_START_DATA ||1386        data === ACTIVITY_START_DATA1387      ) {1388        depth++;1389      }1390      // TODO: Should we hide preamble contribution in this case?1391    }1392    // $FlowFixMe[incompatible-type] we bail out when we get a null1393    node = nextNode;1394  } while (node);1395}13961397export function hideDehydratedBoundary(1398  suspenseInstance: SuspenseInstance,1399): void {1400  hideOrUnhideDehydratedBoundary(suspenseInstance, true);1401}14021403export function hideInstance(instance: Instance): void {1404  // TODO: Does this work for all element types? What about MathML? Should we1405  // pass host context to this method?1406  instance = instance as any as HTMLElement;1407  const style = instance.style;1408  // $FlowFixMe[method-unbinding]1409  if (typeof style.setProperty === 'function') {1410    style.setProperty('display', 'none', 'important');1411  } else {1412    style.display = 'none';1413  }1414}14151416export function hideTextInstance(textInstance: TextInstance): void {1417  textInstance.nodeValue = '';1418}14191420export function unhideDehydratedBoundary(1421  dehydratedInstance: SuspenseInstance | ActivityInstance,1422): void {1423  hideOrUnhideDehydratedBoundary(dehydratedInstance, false);1424}14251426export function unhideInstance(instance: Instance, props: Props): void {1427  instance = instance as any as HTMLElement;1428  const styleProp = props[STYLE];1429  const display =1430    styleProp !== undefined &&1431    // $FlowFixMe[invalid-compare]1432    styleProp !== null &&1433    styleProp.hasOwnProperty('display')1434      ? styleProp.display1435      : null;1436  instance.style.display =1437    display == null || typeof display === 'boolean'1438      ? ''1439      : // The value would've errored already if it wasn't safe.1440        // eslint-disable-next-line react-internal/safe-string-coercion1441        ('' + display).trim();1442}14431444export function unhideTextInstance(1445  textInstance: TextInstance,1446  text: string,1447): void {1448  textInstance.nodeValue = text;1449}14501451function warnForBlockInsideInline(instance: HTMLElement) {1452  if (__DEV__) {1453    let nextNode = instance.firstChild;1454    outer: while (nextNode != null) {1455      let node: Node = nextNode;1456      if (1457        node.nodeType === ELEMENT_NODE &&1458        getComputedStyle(node as any).display === 'block'1459      ) {1460        const fiber =1461          getInstanceFromNode(node) || getInstanceFromNode(instance);1462        runWithFiberInDEV(1463          fiber,1464          (parentTag: string, childTag: string) => {1465            console.error(1466              "You're about to start a <ViewTransition> around a display: inline " +1467                'element <%s>, which itself has a display: block element <%s> inside it. ' +1468                'This might trigger a bug in Safari which causes the View Transition to ' +1469                'be skipped with a duplicate name error.\n' +1470                'https://bugs.webkit.org/show_bug.cgi?id=290923',1471              parentTag.toLocaleLowerCase(),1472              childTag.toLocaleLowerCase(),1473            );1474          },1475          instance.tagName,1476          (node as any).tagName,1477        );1478        break;1479      }1480      if (node.firstChild != null) {1481        nextNode = node.firstChild;1482        continue;1483      }1484      if (node === instance) {1485        break;1486      }1487      while (node.nextSibling == null) {1488        if (node.parentNode == null || node.parentNode === instance) {1489          break;1490        }1491        node = node.parentNode;1492      }1493      nextNode = node.nextSibling;1494    }1495  }1496}14971498function countClientRects(rects: Array<ClientRect>): number {1499  if (rects.length === 1) {1500    return 1;1501  }1502  // Count non-zero rects.1503  let count = 0;1504  for (let i = 0; i < rects.length; i++) {1505    const rect = rects[i];1506    if (rect.width > 0 && rect.height > 0) {1507      count++;1508    }1509  }1510  return count;1511}15121513export function applyViewTransitionName(1514  instance: Instance,1515  name: string,1516  className: ?string,1517): void {1518  instance = instance as any as HTMLElement;1519  // If the name isn't valid CSS identifier, base64 encode the name instead.1520  // This doesn't let you select it in custom CSS selectors but it does work in current1521  // browsers.1522  const escapedName =1523    CSS.escape(name) !== name ? 'r-' + btoa(name).replace(/=/g, '') : name;1524  // $FlowFixMe[prop-missing]1525  instance.style.viewTransitionName = escapedName;1526  if (className != null) {1527    // $FlowFixMe[prop-missing]1528    instance.style.viewTransitionClass = className;1529  }1530  const computedStyle = getComputedStyle(instance);1531  if (computedStyle.display === 'inline') {1532    // WebKit has a bug where assigning a name to display: inline elements errors1533    // if they have display: block children. We try to work around this bug in the1534    // simple case by converting it automatically to display: inline-block.1535    // https://bugs.webkit.org/show_bug.cgi?id=2909231536    const rects = instance.getClientRects();1537    if (1538      // $FlowFixMe[incompatible-type]1539      countClientRects(rects) === 11540    ) {1541      // If the instance has a single client rect, that means that it can be1542      // expressed as a display: inline-block or block.1543      // This will cause layout thrash but we live with it since inline view transitions1544      // are unusual.1545      const style = instance.style;1546      // If there's literally only one rect, then it's likely on a single line like an1547      // inline-block. If it's multiple rects but all but one of them are empty it's1548      // likely because it's a single block that caused a line break.1549      style.display = rects.length === 1 ? 'inline-block' : 'block';1550      // Margin doesn't apply to inline so should be zero. However, padding top/bottom1551      // applies to inline-block positioning which we can offset by setting the margin1552      // to the negative padding to get it back into original position.1553      style.marginTop = '-' + computedStyle.paddingTop;1554      style.marginBottom = '-' + computedStyle.paddingBottom;1555    } else {1556      // This case cannot be easily fixed if it has blocks but it's also fine if1557      // it doesn't have blocks. So we only warn in DEV about this being an issue.1558      warnForBlockInsideInline(instance);1559    }1560  }1561}15621563export function restoreViewTransitionName(1564  instance: Instance,1565  props: Props,1566): void {1567  instance = instance as any as HTMLElement;1568  const style = instance.style;1569  const styleProp = props[STYLE];1570  const viewTransitionName =1571    styleProp != null1572      ? styleProp.hasOwnProperty('viewTransitionName')1573        ? styleProp.viewTransitionName1574        : styleProp.hasOwnProperty('view-transition-name')1575          ? styleProp['view-transition-name']1576          : null1577      : null;1578  // $FlowFixMe[prop-missing]1579  style.viewTransitionName =1580    viewTransitionName == null || typeof viewTransitionName === 'boolean'1581      ? ''1582      : // The value would've errored already if it wasn't safe.1583        // eslint-disable-next-line react-internal/safe-string-coercion1584        ('' + viewTransitionName).trim();1585  const viewTransitionClass =1586    styleProp != null1587      ? styleProp.hasOwnProperty('viewTransitionClass')1588        ? styleProp.viewTransitionClass1589        : styleProp.hasOwnProperty('view-transition-class')1590          ? styleProp['view-transition-class']1591          : null1592      : null;1593  // $FlowFixMe[prop-missing]1594  style.viewTransitionClass =1595    viewTransitionClass == null || typeof viewTransitionClass === 'boolean'1596      ? ''1597      : // The value would've errored already if it wasn't safe.1598        // eslint-disable-next-line react-internal/safe-string-coercion1599        ('' + viewTransitionClass).trim();1600  if (style.display === 'inline-block') {1601    // We might have overridden the style. Reset it to what it should be.1602    if (styleProp == null) {1603      style.display = style.margin = '';1604    } else {1605      const display = styleProp.display;1606      style.display =1607        display == null || typeof display === 'boolean' ? '' : display;1608      const margin = styleProp.margin;1609      if (margin != null) {1610        style.margin = margin;1611      } else {1612        const marginTop = styleProp.hasOwnProperty('marginTop')1613          ? styleProp.marginTop1614          : styleProp['margin-top'];1615        style.marginTop =1616          marginTop == null || typeof marginTop === 'boolean' ? '' : marginTop;1617        const marginBottom = styleProp.hasOwnProperty('marginBottom')1618          ? styleProp.marginBottom1619          : styleProp['margin-bottom'];1620        style.marginBottom =1621          marginBottom == null || typeof marginBottom === 'boolean'1622            ? ''1623            : marginBottom;1624      }1625    }1626  }1627}16281629export function cancelViewTransitionName(1630  instance: Instance,1631  oldName: string,1632  props: Props,1633): void {1634  // To cancel the "new" state and paint this instance as part of the parent, all we have to do1635  // is remove the view-transition-name before we exit startViewTransition.1636  restoreViewTransitionName(instance, props);1637  // There isn't a way to cancel an "old" state but what we can do is hide it by animating it.1638  // Since it is already removed from the old state of the parent, this technique only works1639  // if the parent also isn't transitioning. Therefore we should only cancel the root most1640  // ViewTransitions.1641  const documentElement = instance.ownerDocument.documentElement;1642  if (documentElement !== null) {1643    documentElement.animate(1644      {opacity: [0, 0], pointerEvents: ['none', 'none']},1645      // $FlowFixMe[incompatible-type]1646      {1647        duration: 0,1648        fill: 'forwards',1649        pseudoElement: '::view-transition-group(' + oldName + ')',1650      },1651    );1652  }1653}16541655export function cancelRootViewTransitionName(rootContainer: Container): void {1656  const documentElement: null | HTMLElement =1657    rootContainer.nodeType === DOCUMENT_NODE1658      ? (rootContainer as any).documentElement1659      : rootContainer.ownerDocument.documentElement;16601661  if (1662    !disableCommentsAsDOMContainers &&1663    rootContainer.nodeType === COMMENT_NODE1664  ) {1665    if (__DEV__) {1666      console.warn(1667        'Cannot cancel root view transition on a comment node. All view transitions will be globally scoped.',1668      );1669    }1670    return;1671  }16721673  if (1674    documentElement !== null &&1675    // $FlowFixMe[prop-missing]1676    documentElement.style.viewTransitionName === ''1677  ) {1678    // $FlowFixMe[prop-missing]1679    documentElement.style.viewTransitionName = 'none';1680    documentElement.animate(1681      {opacity: [0, 0], pointerEvents: ['none', 'none']},1682      // $FlowFixMe[incompatible-type]1683      {1684        duration: 0,1685        fill: 'forwards',1686        pseudoElement: '::view-transition-group(root)',1687      },1688    );1689    // By default the root ::view-transition selector captures all pointer events,1690    // which means nothing gets interactive. We want to let whatever is not animating1691    // remain interactive during the transition. To do that, we set the size to nothing1692    // so that the transition doesn't capture any clicks. We don't set pointer-events1693    // on this one as that would apply to all running transitions. This lets animations1694    // that are running to block clicks so that they don't end up incorrectly hitting1695    // whatever is below the animation.1696    documentElement.animate(1697      {width: [0, 0], height: [0, 0]},1698      // $FlowFixMe[incompatible-call]1699      // $FlowFixMe[incompatible-type]1700      {1701        duration: 0,1702        fill: 'forwards',1703        pseudoElement: '::view-transition',1704      },1705    );1706  }1707}17081709export function restoreRootViewTransitionName(rootContainer: Container): void {1710  let containerInstance: Instance;1711  if (rootContainer.nodeType === DOCUMENT_NODE) {1712    containerInstance = (rootContainer as any).body;1713  } else if (rootContainer.nodeName === 'HTML') {1714    containerInstance = rootContainer.ownerDocument.body as any;1715  } else {1716    // If the container is not the whole document, then we ideally should probably1717    // clone the whole document outside of the React too.1718    containerInstance = rootContainer as any;1719  }1720  if (1721    !disableCommentsAsDOMContainers &&1722    containerInstance.nodeType === COMMENT_NODE1723  ) {1724    return;1725  }1726  if (1727    // $FlowFixMe[prop-missing]1728    containerInstance.style.viewTransitionName === 'root'1729  ) {1730    // If we moved the root view transition name to the container in a gesture1731    // we need to restore it now.1732    containerInstance.style.viewTransitionName = '';1733  }1734  const documentElement: null | HTMLElement =1735    containerInstance.ownerDocument.documentElement;1736  if (1737    documentElement !== null &&1738    // $FlowFixMe[prop-missing]1739    documentElement.style.viewTransitionName === 'none'1740  ) {1741    // $FlowFixMe[prop-missing]1742    documentElement.style.viewTransitionName = '';1743  }1744}17451746function getComputedTransform(style: CSSStyleDeclaration): string {1747  // Gets the merged transform of all the short hands.1748  const computedStyle: any = style;1749  let transform: string = computedStyle.transform;1750  if (transform === 'none') {1751    transform = '';1752  }1753  const scale: string = computedStyle.scale;1754  if (scale !== 'none' && scale !== '') {1755    const parts = scale.split(' ');1756    transform =1757      (parts.length === 3 ? 'scale3d' : 'scale') +1758      '(' +1759      parts.join(', ') +1760      ') ' +1761      transform;1762  }1763  const rotate: string = computedStyle.rotate;1764  if (rotate !== 'none' && rotate !== '') {1765    const parts = rotate.split(' ');1766    if (parts.length === 1) {1767      transform = 'rotate(' + parts[0] + ') ' + transform;1768    } else if (parts.length === 2) {1769      transform =1770        'rotate' + parts[0].toUpperCase() + '(' + parts[1] + ') ' + transform;1771    } else {1772      transform = 'rotate3d(' + parts.join(', ') + ') ' + transform;1773    }1774  }1775  const translate: string = computedStyle.translate;1776  if (translate !== 'none' && translate !== '') {1777    const parts = translate.split(' ');1778    transform =1779      (parts.length === 3 ? 'translate3d' : 'translate') +1780      '(' +1781      parts.join(', ') +1782      ') ' +1783      transform;1784  }1785  return transform;1786}17871788function moveOutOfViewport(1789  originalStyle: CSSStyleDeclaration,1790  element: HTMLElement,1791): void {1792  // Apply a transform that safely puts the whole element outside the viewport1793  // while still letting it paint its "old" state to a snapshot.1794  const transform = getComputedTransform(originalStyle);1795  // Clear the long form properties.1796  // $FlowFixMe[prop-missing]1797  element.style.translate = 'none';1798  // $FlowFixMe[prop-missing]1799  element.style.scale = 'none';1800  // $FlowFixMe[prop-missing]1801  element.style.rotate = 'none';1802  // Apply a translate to move it way out of the viewport. This is applied first1803  // so that it is in the coordinate space of the parent and not after applying1804  // other transforms. That's why we need to merge the long form properties.1805  // TODO: Ideally we'd adjust for the parent's rotate/scale. Otherwise when1806  // we move back the ::view-transition-group we might overshoot or undershoot.1807  element.style.transform = 'translate(-20000px, -20000px) ' + transform;1808}18091810function moveOldFrameIntoViewport(keyframe: any): void {1811  // In the resulting View Transition Animation, the first frame will be offset.1812  const computedTransform: ?string = keyframe.transform;1813  if (computedTransform != null) {1814    let transform = computedTransform === 'none' ? '' : computedTransform;1815    transform = 'translate(20000px, 20000px) ' + transform;1816    keyframe.transform = transform;1817  }1818}18191820export function cloneRootViewTransitionContainer(1821  rootContainer: Container,1822): Instance {1823  // This implies that we're not going to animate the root document but instead1824  // the clone so we first clear the name of the root container.1825  const documentElement: null | HTMLElement =1826    rootContainer.nodeType === DOCUMENT_NODE1827      ? (rootContainer as any).documentElement1828      : rootContainer.ownerDocument.documentElement;1829  if (1830    documentElement !== null &&1831    // $FlowFixMe[prop-missing]1832    documentElement.style.viewTransitionName === ''1833  ) {1834    // $FlowFixMe[prop-missing]1835    documentElement.style.viewTransitionName = 'none';1836  }18371838  let containerInstance: HTMLElement;1839  if (rootContainer.nodeType === DOCUMENT_NODE) {1840    containerInstance = (rootContainer as any).body;1841  } else if (rootContainer.nodeName === 'HTML') {1842    containerInstance = rootContainer.ownerDocument.body as any;1843  } else if (1844    !disableCommentsAsDOMContainers &&1845    rootContainer.nodeType === COMMENT_NODE1846  ) {1847    throw new Error(1848      'Cannot use a startGestureTransition() with a comment node root.',1849    );1850  } else {1851    // If the container is not the whole document, then we ideally should probably1852    // clone the whole document outside of the React too.1853    containerInstance = rootContainer as any;1854  }18551856  const containerParent = containerInstance.parentNode;1857  if (containerParent === null) {1858    throw new Error(1859      'Cannot use a startGestureTransition() on a detached root.',1860    );1861  }18621863  const clone: HTMLElement = containerInstance.cloneNode(false);18641865  const computedStyle = getComputedStyle(containerInstance);18661867  if (1868    computedStyle.position === 'absolute' ||1869    computedStyle.position === 'fixed'1870  ) {1871    // If the style is already absolute, we don't have to do anything because it'll appear1872    // in the same place.1873  } else {1874    // Otherwise we need to absolutely position the clone in the same location as the original.1875    let positionedAncestor: HTMLElement = containerParent;1876    while (1877      positionedAncestor.parentNode != null &&1878      positionedAncestor.parentNode.nodeType !== DOCUMENT_NODE1879    ) {1880      if (getComputedStyle(positionedAncestor).position !== 'static') {1881        break;1882      }1883      // $FlowFixMe[incompatible-type]: This is refined.1884      positionedAncestor = positionedAncestor.parentNode;1885    }18861887    const positionedAncestorStyle: any = positionedAncestor.style;1888    const containerInstanceStyle: any = containerInstance.style;1889    // Clear the transform while we're measuring since it affects the bounding client rect.1890    const prevAncestorTranslate = positionedAncestorStyle.translate;1891    const prevAncestorScale = positionedAncestorStyle.scale;1892    const prevAncestorRotate = positionedAncestorStyle.rotate;1893    const prevAncestorTransform = positionedAncestorStyle.transform;1894    const prevTranslate = containerInstanceStyle.translate;1895    const prevScale = containerInstanceStyle.scale;1896    const prevRotate = containerInstanceStyle.rotate;1897    const prevTransform = containerInstanceStyle.transform;1898    positionedAncestorStyle.translate = 'none';1899    positionedAncestorStyle.scale = 'none';1900    positionedAncestorStyle.rotate = 'none';1901    positionedAncestorStyle.transform = 'none';1902    containerInstanceStyle.translate = 'none';1903    containerInstanceStyle.scale = 'none';1904    containerInstanceStyle.rotate = 'none';1905    containerInstanceStyle.transform = 'none';19061907    const ancestorRect = positionedAncestor.getBoundingClientRect();1908    const rect = containerInstance.getBoundingClientRect();19091910    const cloneStyle = clone.style;1911    cloneStyle.position = 'absolute';1912    cloneStyle.top = rect.top - ancestorRect.top + 'px';1913    cloneStyle.left = rect.left - ancestorRect.left + 'px';1914    cloneStyle.width = rect.width + 'px';1915    cloneStyle.height = rect.height + 'px';1916    cloneStyle.margin = '0px';1917    cloneStyle.boxSizing = 'border-box';19181919    positionedAncestorStyle.translate = prevAncestorTranslate;1920    positionedAncestorStyle.scale = prevAncestorScale;1921    positionedAncestorStyle.rotate = prevAncestorRotate;1922    positionedAncestorStyle.transform = prevAncestorTransform;1923    containerInstanceStyle.translate = prevTranslate;1924    containerInstanceStyle.scale = prevScale;1925    containerInstanceStyle.rotate = prevRotate;1926    containerInstanceStyle.transform = prevTransform;1927  }19281929  // For this transition the container will act as the root. Nothing outside of it should1930  // be affected anyway. This lets us transition from the cloned container to the original.1931  // $FlowFixMe[prop-missing]1932  clone.style.viewTransitionName = 'root';19331934  // Move out of the viewport so that it's still painted for the snapshot but is not visible1935  // for the frame where the snapshot happens.1936  moveOutOfViewport(computedStyle, clone);19371938  // Insert the clone after the root container as a sibling. This may inject a body1939  // as the next sibling of an existing body. document.body will still point to the1940  // first one and any id selectors will still find the first one. That's why it's1941  // important that it's after the existing node.1942  containerInstance.parentNode.insertBefore(1943    clone,1944    containerInstance.nextSibling,1945  );19461947  return clone;1948}19491950export function removeRootViewTransitionClone(1951  rootContainer: Container,1952  clone: Instance,1953): void {1954  let containerInstance: Instance;1955  if (rootContainer.nodeType === DOCUMENT_NODE) {1956    containerInstance = (rootContainer as any).body;1957  } else if (rootContainer.nodeName === 'HTML') {1958    containerInstance = rootContainer.ownerDocument.body as any;1959  } else {1960    // If the container is not the whole document, then we ideally should probably1961    // clone the whole document outside of the React too.1962    containerInstance = rootContainer as any;1963  }1964  const containerParent = containerInstance.parentNode;1965  if (containerParent === null) {1966    throw new Error(1967      'Cannot use a startGestureTransition() on a detached root.',1968    );1969  }1970  // We assume that the clone is still within the same parent.1971  containerParent.removeChild(clone);19721973  // Now the root is on the containerInstance itself until we call restoreRootViewTransitionName.1974  containerInstance.style.viewTransitionName = 'root';1975}19761977export type InstanceMeasurement = {1978  rect: ClientRect | DOMRect,1979  abs: boolean, // is absolutely positioned1980  clip: boolean, // is a clipping parent1981  view: boolean, // is in viewport bounds1982};19831984function createMeasurement(1985  rect: ClientRect | DOMRect,1986  computedStyle: CSSStyleDeclaration,1987  element: Element,1988): InstanceMeasurement {1989  const ownerWindow = element.ownerDocument.defaultView;1990  return {1991    rect: rect,1992    abs:1993      // Absolutely positioned instances don't contribute their size to the parent.1994      computedStyle.position === 'absolute' ||1995      computedStyle.position === 'fixed',1996    clip:1997      // If a ViewTransition boundary acts as a clipping parent group we should1998      // always mark it to animate if its children do so that we can clip them.1999      // This doesn't actually have any effect yet until browsers implement2000      // layered capture and nested view transitions.

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.

Generated by searchcode.com · powered by code_get_file + code_get_findings