packages/react-dom-bindings/src/client/ReactDOMComponent.js JAVASCRIPT 3,415 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 3,415.
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 {HostContext, HostContextDev} from './ReactFiberConfigDOM';1112import {HostContextNamespaceNone} from './ReactFiberConfigDOM';1314import {15  registrationNameDependencies,16  possibleRegistrationNames,17} from '../events/EventRegistry';1819import {checkHtmlStringCoercion} from 'shared/CheckStringCoercion';20import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion';21import {checkControlledValueProps} from '../shared/ReactControlledValuePropTypes';2223import {24  getValueForAttribute,25  getValueForAttributeOnCustomComponent,26  setValueForPropertyOnCustomComponent,27  setValueForKnownAttribute,28  setValueForAttribute,29  setValueForNamespacedAttribute,30} from './DOMPropertyOperations';31import {32  validateInputProps,33  initInput,34  updateInput,35  restoreControlledInputState,36} from './ReactDOMInput';37import {validateOptionProps} from './ReactDOMOption';38import {39  validateSelectProps,40  initSelect,41  restoreControlledSelectState,42  updateSelect,43} from './ReactDOMSelect';44import {45  validateTextareaProps,46  initTextarea,47  updateTextarea,48  restoreControlledTextareaState,49} from './ReactDOMTextarea';50import {setSrcObject} from './ReactDOMSrcObject';51import {validateTextNesting} from './validateDOMNesting';52import setTextContent from './setTextContent';53import {54  createDangerousStringForStyles,55  setValueForStyles,56} from './CSSPropertyOperations';57import {SVG_NAMESPACE, MATH_NAMESPACE} from './DOMNamespaces';58import isCustomElement from '../shared/isCustomElement';59import getAttributeAlias from '../shared/getAttributeAlias';60import possibleStandardNames from '../shared/possibleStandardNames';61import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook';62import {validateProperties as validateInputProperties} from '../shared/ReactDOMNullInputValuePropHook';63import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook';64import sanitizeURL from '../shared/sanitizeURL';6566import noop from 'shared/noop';6768import {trackHostMutation} from 'react-reconciler/src/ReactFiberMutationTracking';6970import {71  enableHydrationChangeEvent,72  enableScrollEndPolyfill,73  enableSrcObject,74  enableViewTransition,75  enableViewTransitionParentEnterExit,76} from 'shared/ReactFeatureFlags';77import {78  mediaEventTypes,79  listenToNonDelegatedEvent,80} from '../events/DOMPluginEventSystem';8182let didWarnControlledToUncontrolled = false;83let didWarnUncontrolledToControlled = false;84let didWarnFormActionType = false;85let didWarnFormActionName = false;86let didWarnFormActionTarget = false;87let didWarnFormActionMethod = false;88let didWarnForNewBooleanPropsWithEmptyValue: {[string]: boolean};89let didWarnPopoverTargetObject = false;90if (__DEV__) {91  didWarnForNewBooleanPropsWithEmptyValue = {};92}9394function validatePropertiesInDevelopment(type: string, props: any) {95  if (__DEV__) {96    validateARIAProperties(type, props);97    validateInputProperties(type, props);98    validateUnknownProperties(type, props, {99      registrationNameDependencies,100      possibleRegistrationNames,101    });102    if (103      props.contentEditable &&104      !props.suppressContentEditableWarning &&105      props.children != null106    ) {107      console.error(108        'A component is `contentEditable` and contains `children` managed by ' +109          'React. It is now your responsibility to guarantee that none of ' +110          'those nodes are unexpectedly modified or duplicated. This is ' +111          'probably not intentional.',112      );113    }114  }115}116117function validateFormActionInDevelopment(118  tag: string,119  key: string,120  value: mixed,121  props: any,122) {123  if (__DEV__) {124    if (value == null) {125      return;126    }127    if (tag === 'form') {128      if (key === 'formAction') {129        console.error(130          'You can only pass the formAction prop to <input> or <button>. Use the action prop on <form>.',131        );132      } else if (typeof value === 'function') {133        if (134          (props.encType != null || props.method != null) &&135          !didWarnFormActionMethod136        ) {137          didWarnFormActionMethod = true;138          console.error(139            'Cannot specify a encType or method for a form that specifies a ' +140              'function as the action. React provides those automatically. ' +141              'They will get overridden.',142          );143        }144        if (props.target != null && !didWarnFormActionTarget) {145          didWarnFormActionTarget = true;146          console.error(147            'Cannot specify a target for a form that specifies a function as the action. ' +148              'The function will always be executed in the same window.',149          );150        }151      }152    } else if (tag === 'input' || tag === 'button') {153      if (key === 'action') {154        console.error(155          'You can only pass the action prop to <form>. Use the formAction prop on <input> or <button>.',156        );157      } else if (158        tag === 'input' &&159        props.type !== 'submit' &&160        props.type !== 'image' &&161        !didWarnFormActionType162      ) {163        didWarnFormActionType = true;164        console.error(165          'An input can only specify a formAction along with type="submit" or type="image".',166        );167      } else if (168        tag === 'button' &&169        props.type != null &&170        props.type !== 'submit' &&171        !didWarnFormActionType172      ) {173        didWarnFormActionType = true;174        console.error(175          'A button can only specify a formAction along with type="submit" or no type.',176        );177      } else if (typeof value === 'function') {178        // Function form actions cannot control the form properties179        if (props.name != null && !didWarnFormActionName) {180          didWarnFormActionName = true;181          console.error(182            'Cannot specify a "name" prop for a button that specifies a function as a formAction. ' +183              'React needs it to encode which action should be invoked. It will get overridden.',184          );185        }186        if (187          (props.formEncType != null || props.formMethod != null) &&188          !didWarnFormActionMethod189        ) {190          didWarnFormActionMethod = true;191          console.error(192            'Cannot specify a formEncType or formMethod for a button that specifies a ' +193              'function as a formAction. React provides those automatically. They will get overridden.',194          );195        }196        if (props.formTarget != null && !didWarnFormActionTarget) {197          didWarnFormActionTarget = true;198          console.error(199            'Cannot specify a formTarget for a button that specifies a function as a formAction. ' +200              'The function will always be executed in the same window.',201          );202        }203      }204    } else {205      if (key === 'action') {206        console.error('You can only pass the action prop to <form>.');207      } else {208        console.error(209          'You can only pass the formAction prop to <input> or <button>.',210        );211      }212    }213  }214}215216function warnForPropDifference(217  propName: string,218  serverValue: mixed,219  clientValue: mixed,220  serverDifferences: {[propName: string]: mixed},221): void {222  if (__DEV__) {223    if (serverValue === clientValue) {224      return;225    }226    const normalizedClientValue =227      normalizeMarkupForTextOrAttribute(clientValue);228    const normalizedServerValue =229      normalizeMarkupForTextOrAttribute(serverValue);230    if (normalizedServerValue === normalizedClientValue) {231      return;232    }233234    serverDifferences[propName] = serverValue;235  }236}237238function hasViewTransition(htmlElement: HTMLElement): boolean {239  return !!(240    htmlElement.getAttribute('vt-share') ||241    htmlElement.getAttribute('vt-exit') ||242    htmlElement.getAttribute('vt-enter') ||243    htmlElement.getAttribute('vt-update') ||244    (enableViewTransitionParentEnterExit &&245      (htmlElement.getAttribute('vt-parent-enter') ||246        htmlElement.getAttribute('vt-parent-exit')))247  );248}249250function isExpectedViewTransitionName(htmlElement: HTMLElement): boolean {251  if (!hasViewTransition(htmlElement)) {252    // We didn't expect to see a view transition name applied.253    return false;254  }255  const expectedVtName = htmlElement.getAttribute('vt-name');256  const actualVtName: string = (htmlElement.style as any)[257    'view-transition-name'258  ];259  if (expectedVtName) {260    return expectedVtName === actualVtName;261  } else {262    // Auto-generated name.263    // TODO: If Fizz starts applying a prefix to this name, we need to consider that.264    return actualVtName.startsWith('_T_');265  }266}267268function warnForExtraAttributes(269  domElement: Element,270  attributeNames: Set<string>,271  serverDifferences: {[propName: string]: mixed},272) {273  if (__DEV__) {274    attributeNames.forEach(function (attributeName) {275      if (attributeName === 'style') {276        if (domElement.getAttribute(attributeName) === '') {277          // Skip empty style. It's fine.278          return;279        }280        const htmlElement = domElement as any as HTMLElement;281        const style = htmlElement.style;282        const isOnlyVTStyles =283          (style.length === 1 && style[0] === 'view-transition-name') ||284          (style.length === 2 &&285            style[0] === 'view-transition-class' &&286            style[1] === 'view-transition-name');287        if (isOnlyVTStyles && isExpectedViewTransitionName(htmlElement)) {288          // If the only extra style was the view-transition-name that we applied from the Fizz289          // runtime, then we should ignore it.290        } else {291          serverDifferences.style = getStylesObjectFromElement(domElement);292        }293      } else {294        serverDifferences[getPropNameFromAttributeName(attributeName)] =295          domElement.getAttribute(attributeName);296      }297    });298  }299}300301function warnForInvalidEventListener(registrationName: string, listener: any) {302  if (__DEV__) {303    if (listener === false) {304      console.error(305        'Expected `%s` listener to be a function, instead got `false`.\n\n' +306          'If you used to conditionally omit it with %s={condition && value}, ' +307          'pass %s={condition ? value : undefined} instead.',308        registrationName,309        registrationName,310        registrationName,311      );312    } else {313      console.error(314        'Expected `%s` listener to be a function, instead got a value of `%s` type.',315        registrationName,316        typeof listener,317      );318    }319  }320}321322// Parse the HTML and read it back to normalize the HTML string so that it323// can be used for comparison.324function normalizeHTML(parent: Element, html: string) {325  if (__DEV__) {326    // We could have created a separate document here to avoid327    // re-initializing custom elements if they exist. But this breaks328    // how <noscript> is being handled. So we use the same document.329    // See the discussion in https://github.com/react/react/pull/11157.330    const testElement =331      parent.namespaceURI === MATH_NAMESPACE ||332      parent.namespaceURI === SVG_NAMESPACE333        ? parent.ownerDocument.createElementNS(334            parent.namespaceURI as any,335            parent.tagName,336          )337        : parent.ownerDocument.createElement(parent.tagName);338    testElement.innerHTML = html;339    return testElement.innerHTML;340  }341}342343// HTML parsing normalizes CR and CRLF to LF.344// It also can turn \u0000 into \uFFFD inside attributes.345// https://www.w3.org/TR/html5/single-page.html#preprocessing-the-input-stream346// If we have a mismatch, it might be caused by that.347// We will still patch up in this case but not fire the warning.348const NORMALIZE_NEWLINES_REGEX = /\r\n?/g;349const NORMALIZE_NULL_AND_REPLACEMENT_REGEX = /\u0000|\uFFFD/g;350351function normalizeMarkupForTextOrAttribute(markup: mixed): string {352  if (__DEV__) {353    checkHtmlStringCoercion(markup);354  }355  const markupString =356    typeof markup === 'string' ? markup : '' + (markup as any);357  return markupString358    .replace(NORMALIZE_NEWLINES_REGEX, '\n')359    .replace(NORMALIZE_NULL_AND_REPLACEMENT_REGEX, '');360}361362function checkForUnmatchedText(363  serverText: string,364  clientText: string | number | bigint,365) {366  const normalizedClientText = normalizeMarkupForTextOrAttribute(clientText);367  const normalizedServerText = normalizeMarkupForTextOrAttribute(serverText);368  if (normalizedServerText === normalizedClientText) {369    return true;370  }371  return false;372}373374export function trapClickOnNonInteractiveElement(node: HTMLElement) {375  // Mobile Safari does not fire properly bubble click events on376  // non-interactive elements, which means delegated click listeners do not377  // fire. The workaround for this bug involves attaching an empty click378  // listener on the target node.379  // https://www.quirksmode.org/blog/archives/2010/09/click_event_del.html380  // Just set it using the onclick property so that we don't have to manage any381  // bookkeeping for it. HostSingleton release clears the property only if it382  // still points to this noop.383  // TODO: Only do this for the relevant Safaris maybe?384  node.onclick = noop;385}386387export function clearClickListener(node: HTMLElement) {388  if (node.onclick === noop) {389    node.onclick = null;390  }391}392393const xlinkNamespace = 'http://www.w3.org/1999/xlink';394const xmlNamespace = 'http://www.w3.org/XML/1998/namespace';395396function setProp(397  domElement: Element,398  tag: string,399  key: string,400  value: mixed,401  props: any,402  prevValue: mixed,403): void {404  switch (key) {405    case 'children': {406      if (typeof value === 'string') {407        if (__DEV__) {408          validateTextNesting(value, tag, false);409        }410        // Avoid setting initial textContent when the text is empty. In IE11 setting411        // textContent on a <textarea> will cause the placeholder to not412        // show within the <textarea> until it has been focused and blurred again.413        // https://github.com/react/react/issues/6731#issuecomment-254874553414        const canSetTextContent =415          tag !== 'body' && (tag !== 'textarea' || value !== '');416        if (canSetTextContent) {417          setTextContent(domElement, value);418        }419      } else if (typeof value === 'number' || typeof value === 'bigint') {420        if (__DEV__) {421          // $FlowFixMe[unsafe-addition] Flow doesn't want us to use `+` operator with string and bigint422          validateTextNesting('' + value, tag, false);423        }424        const canSetTextContent = tag !== 'body';425        if (canSetTextContent) {426          // $FlowFixMe[unsafe-addition] Flow doesn't want us to use `+` operator with string and bigint427          setTextContent(domElement, '' + value);428        }429      } else {430        return;431      }432      break;433    }434    // These are very common props and therefore are in the beginning of the switch.435    // TODO: aria-label is a very common prop but allows booleans so is not like the others436    // but should ideally go in this list too.437    case 'className':438      setValueForKnownAttribute(domElement, 'class', value);439      break;440    case 'tabIndex':441      // This has to be case sensitive in SVG.442      setValueForKnownAttribute(domElement, 'tabindex', value);443      break;444    case 'dir':445    case 'role':446    case 'viewBox':447    case 'width':448    case 'height': {449      setValueForKnownAttribute(domElement, key, value);450      break;451    }452    case 'style': {453      setValueForStyles(domElement, value, prevValue);454      return;455    }456    // These attributes accept URLs. These must not allow javascript: URLS.457    case 'data':458      if (tag !== 'object') {459        setValueForKnownAttribute(domElement, 'data', value);460        break;461      }462    // fallthrough463    case 'src': {464      if (enableSrcObject && typeof value === 'object' && value !== null) {465        // Some tags support object sources like Blob, File, MediaSource and MediaStream.466        if (tag === 'img' || tag === 'video' || tag === 'audio') {467          try {468            setSrcObject(domElement, tag, value);469            break;470          } catch (x) {471            // If URL.createObjectURL() errors, it was probably some other object type472            // that should be toString:ed instead, so we just fall-through to the normal473            // path.474          }475        } else {476          if (__DEV__) {477            try {478              // This should always error.479              URL.revokeObjectURL(URL.createObjectURL(value as any));480              if (tag === 'source') {481                console.error(482                  'Passing Blob, MediaSource or MediaStream to <source src> is not supported. ' +483                    'Pass it directly to <img src>, <video src> or <audio src> instead.',484                );485              } else {486                console.error(487                  'Passing Blob, MediaSource or MediaStream to <%s src> is not supported.',488                  tag,489                );490              }491            } catch (x) {}492          }493        }494      }495      // Fallthrough496    }497    case 'href': {498      if (499        value === '' &&500        // <a href=""> is fine for "reload" links.501        !(tag === 'a' && key === 'href')502      ) {503        if (__DEV__) {504          if (key === 'src') {505            console.error(506              'An empty string ("") was passed to the %s attribute. ' +507                'This may cause the browser to download the whole page again over the network. ' +508                'To fix this, either do not render the element at all ' +509                'or pass null to %s instead of an empty string.',510              key,511              key,512            );513          } else {514            console.error(515              'An empty string ("") was passed to the %s attribute. ' +516                'To fix this, either do not render the element at all ' +517                'or pass null to %s instead of an empty string.',518              key,519              key,520            );521          }522        }523        domElement.removeAttribute(key);524        break;525      }526      if (527        value == null ||528        typeof value === 'function' ||529        typeof value === 'symbol' ||530        typeof value === 'boolean'531      ) {532        domElement.removeAttribute(key);533        break;534      }535      if (__DEV__) {536        checkAttributeStringCoercion(value, key);537      }538      const sanitizedValue = sanitizeURL(value) as any;539      domElement.setAttribute(key, sanitizedValue);540      break;541    }542    case 'action':543    case 'formAction': {544      // TODO: Consider moving these special cases to the form, input and button tags.545      if (__DEV__) {546        validateFormActionInDevelopment(tag, key, value, props);547      }548      if (typeof value === 'function') {549        // Set a javascript URL that doesn't do anything. We don't expect this to be invoked550        // because we'll preventDefault, but it can happen if a form is manually submitted or551        // if someone calls stopPropagation before React gets the event.552        // If CSP is used to block javascript: URLs that's fine too. It just won't show this553        // error message but the URL will be logged.554        domElement.setAttribute(555          key,556          // eslint-disable-next-line no-script-url557          "javascript:throw new Error('" +558            'A React form was unexpectedly submitted. If you called form.submit() manually, ' +559            "consider using form.requestSubmit() instead. If you\\'re trying to use " +560            'event.stopPropagation() in a submit event handler, consider also calling ' +561            'event.preventDefault().' +562            "')",563        );564        break;565      } else if (typeof prevValue === 'function') {566        // When we're switching off a Server Action that was originally hydrated.567        // The server control these fields during SSR that are now trailing.568        // The regular diffing doesn't apply since we compare against the previous props.569        // Instead, we need to force them to be set to whatever they should be now.570        // This would be a lot cleaner if we did this whole fork in the per-tag approach.571        if (key === 'formAction') {572          if (tag !== 'input') {573            // Setting the name here isn't completely safe for inputs if this is switching574            // to become a radio button. In that case we let the tag based override take575            // control.576            setProp(domElement, tag, 'name', props.name, props, null);577          }578          setProp(579            domElement,580            tag,581            'formEncType',582            props.formEncType,583            props,584            null,585          );586          setProp(domElement, tag, 'formMethod', props.formMethod, props, null);587          setProp(domElement, tag, 'formTarget', props.formTarget, props, null);588        } else {589          setProp(domElement, tag, 'encType', props.encType, props, null);590          setProp(domElement, tag, 'method', props.method, props, null);591          setProp(domElement, tag, 'target', props.target, props, null);592        }593      }594      if (595        value == null ||596        typeof value === 'symbol' ||597        typeof value === 'boolean'598      ) {599        domElement.removeAttribute(key);600        break;601      }602      if (__DEV__) {603        checkAttributeStringCoercion(value, key);604      }605      const sanitizedValue = sanitizeURL(value) as any;606      domElement.setAttribute(key, sanitizedValue);607      break;608    }609    case 'onClick': {610      // TODO: This cast may not be sound for SVG, MathML or custom elements.611      if (value != null) {612        if (__DEV__ && typeof value !== 'function') {613          warnForInvalidEventListener(key, value);614        }615        trapClickOnNonInteractiveElement(domElement as any as HTMLElement);616      }617      return;618    }619    case 'onScroll': {620      if (value != null) {621        if (__DEV__ && typeof value !== 'function') {622          warnForInvalidEventListener(key, value);623        }624        listenToNonDelegatedEvent('scroll', domElement);625      }626      return;627    }628    case 'onScrollEnd': {629      if (value != null) {630        if (__DEV__ && typeof value !== 'function') {631          warnForInvalidEventListener(key, value);632        }633        listenToNonDelegatedEvent('scrollend', domElement);634        if (enableScrollEndPolyfill) {635          // For use by the polyfill.636          listenToNonDelegatedEvent('scroll', domElement);637        }638      }639      return;640    }641    case 'dangerouslySetInnerHTML': {642      if (value != null) {643        if (typeof value !== 'object' || !('__html' in value)) {644          throw new Error(645            '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +646              'Please visit https://react.dev/link/dangerously-set-inner-html ' +647              'for more information.',648          );649        }650        const nextHtml: any = value.__html;651        if (nextHtml != null) {652          if (props.children != null) {653            throw new Error(654              'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',655            );656          }657          const lastHtml: any =658            prevValue != null ? (prevValue as any).__html : undefined;659          if (lastHtml !== nextHtml) {660            domElement.innerHTML = nextHtml;661          }662        }663      }664      break;665    }666    // Note: `option.selected` is not updated if `select.multiple` is667    // disabled with `removeAttribute`. We have special logic for handling this.668    case 'multiple': {669      (domElement as any).multiple =670        value && typeof value !== 'function' && typeof value !== 'symbol';671      break;672    }673    case 'muted': {674      (domElement as any).muted =675        value && typeof value !== 'function' && typeof value !== 'symbol';676      break;677    }678    case 'suppressContentEditableWarning':679    case 'suppressHydrationWarning':680    case 'defaultValue': // Reserved681    case 'defaultChecked':682    case 'innerHTML':683    case 'ref': {684      // TODO: `ref` is pretty common, should we move it up?685      // Noop686      break;687    }688    case 'autoFocus': {689      // We polyfill it separately on the client during commit.690      // We could have excluded it in the property list instead of691      // adding a special case here, but then it wouldn't be emitted692      // on server rendering (but we *do* want to emit it in SSR).693      break;694    }695    case 'xlinkHref': {696      if (697        value == null ||698        typeof value === 'function' ||699        typeof value === 'boolean' ||700        typeof value === 'symbol'701      ) {702        domElement.removeAttribute('xlink:href');703        break;704      }705      if (__DEV__) {706        checkAttributeStringCoercion(value, key);707      }708      const sanitizedValue = sanitizeURL(value) as any;709      domElement.setAttributeNS(xlinkNamespace, 'xlink:href', sanitizedValue);710      break;711    }712    case 'contentEditable':713    case 'spellCheck':714    case 'draggable':715    case 'value':716    case 'autoReverse':717    case 'externalResourcesRequired':718    case 'focusable':719    case 'preserveAlpha': {720      // Booleanish String721      // These are "enumerated" attributes that accept "true" and "false".722      // In React, we let users pass `true` and `false` even though technically723      // these aren't boolean attributes (they are coerced to strings).724      // The SVG attributes are case-sensitive. Since the HTML attributes are725      // insensitive they also work even though we canonically use lower case.726      if (727        value != null &&728        typeof value !== 'function' &&729        typeof value !== 'symbol'730      ) {731        if (__DEV__) {732          checkAttributeStringCoercion(value, key);733        }734        domElement.setAttribute(key, value as any);735      } else {736        domElement.removeAttribute(key);737      }738      break;739    }740    // Boolean741    case 'inert': {742      if (__DEV__) {743        if (value === '' && !didWarnForNewBooleanPropsWithEmptyValue[key]) {744          didWarnForNewBooleanPropsWithEmptyValue[key] = true;745          console.error(746            'Received an empty string for a boolean attribute `%s`. ' +747              'This will treat the attribute as if it were false. ' +748              'Either pass `false` to silence this warning, or ' +749              'pass `true` if you used an empty string in earlier versions of React to indicate this attribute is true.',750            key,751          );752        }753      }754    }755    // Fallthrough for boolean props that don't have a warning for empty strings.756    case 'allowFullScreen':757    case 'async':758    case 'autoPlay':759    case 'controls':760    case 'credentialless':761    case 'default':762    case 'defer':763    case 'disabled':764    case 'disablePictureInPicture':765    case 'disableRemotePlayback':766    case 'formNoValidate':767    case 'hidden':768    case 'loop':769    case 'noModule':770    case 'noValidate':771    case 'open':772    case 'playsInline':773    case 'readOnly':774    case 'required':775    case 'reversed':776    case 'scoped':777    case 'seamless':778    case 'itemScope': {779      if (value && typeof value !== 'function' && typeof value !== 'symbol') {780        domElement.setAttribute(key, '');781      } else {782        domElement.removeAttribute(key);783      }784      break;785    }786    // Overloaded Boolean787    case 'capture':788    case 'download': {789      // An attribute that can be used as a flag as well as with a value.790      // When true, it should be present (set either to an empty string or its name).791      // When false, it should be omitted.792      // For any other value, should be present with that value.793      if (value === true) {794        domElement.setAttribute(key, '');795      } else if (796        value !== false &&797        value != null &&798        typeof value !== 'function' &&799        typeof value !== 'symbol'800      ) {801        if (__DEV__) {802          checkAttributeStringCoercion(value, key);803        }804        domElement.setAttribute(key, value as any);805      } else {806        domElement.removeAttribute(key);807      }808      break;809    }810    case 'cols':811    case 'rows':812    case 'size':813    case 'span': {814      // These are HTML attributes that must be positive numbers.815      if (816        value != null &&817        typeof value !== 'function' &&818        typeof value !== 'symbol' &&819        !isNaN(value) &&820        (value as any) >= 1821      ) {822        if (__DEV__) {823          checkAttributeStringCoercion(value, key);824        }825        domElement.setAttribute(key, value as any);826      } else {827        domElement.removeAttribute(key);828      }829      break;830    }831    case 'rowSpan':832    case 'start': {833      // These are HTML attributes that must be numbers.834      if (835        value != null &&836        typeof value !== 'function' &&837        typeof value !== 'symbol' &&838        !isNaN(value)839      ) {840        if (__DEV__) {841          checkAttributeStringCoercion(value, key);842        }843        domElement.setAttribute(key, value as any);844      } else {845        domElement.removeAttribute(key);846      }847      break;848    }849    case 'popover':850      listenToNonDelegatedEvent('beforetoggle', domElement);851      listenToNonDelegatedEvent('toggle', domElement);852      setValueForAttribute(domElement, 'popover', value);853      break;854    case 'xlinkActuate':855      setValueForNamespacedAttribute(856        domElement,857        xlinkNamespace,858        'xlink:actuate',859        value,860      );861      break;862    case 'xlinkArcrole':863      setValueForNamespacedAttribute(864        domElement,865        xlinkNamespace,866        'xlink:arcrole',867        value,868      );869      break;870    case 'xlinkRole':871      setValueForNamespacedAttribute(872        domElement,873        xlinkNamespace,874        'xlink:role',875        value,876      );877      break;878    case 'xlinkShow':879      setValueForNamespacedAttribute(880        domElement,881        xlinkNamespace,882        'xlink:show',883        value,884      );885      break;886    case 'xlinkTitle':887      setValueForNamespacedAttribute(888        domElement,889        xlinkNamespace,890        'xlink:title',891        value,892      );893      break;894    case 'xlinkType':895      setValueForNamespacedAttribute(896        domElement,897        xlinkNamespace,898        'xlink:type',899        value,900      );901      break;902    case 'xmlBase':903      setValueForNamespacedAttribute(904        domElement,905        xmlNamespace,906        'xml:base',907        value,908      );909      break;910    case 'xmlLang':911      setValueForNamespacedAttribute(912        domElement,913        xmlNamespace,914        'xml:lang',915        value,916      );917      break;918    case 'xmlSpace':919      setValueForNamespacedAttribute(920        domElement,921        xmlNamespace,922        'xml:space',923        value,924      );925      break;926    // Properties that should not be allowed on custom elements.927    case 'is': {928      if (__DEV__) {929        if (prevValue != null) {930          console.error(931            'Cannot update the "is" prop after it has been initialized.',932          );933        }934      }935      // TODO: We shouldn't actually set this attribute, because we've already936      // passed it to createElement. We don't also need the attribute.937      // However, our tests currently query for it so it's plausible someone938      // else does too so it's break.939      setValueForAttribute(domElement, 'is', value);940      break;941    }942    case 'innerText':943    case 'textContent':944      return;945    case 'popoverTarget':946      if (__DEV__) {947        if (948          !didWarnPopoverTargetObject &&949          value != null &&950          typeof value === 'object'951        ) {952          didWarnPopoverTargetObject = true;953          console.error(954            'The `popoverTarget` prop expects the ID of an Element as a string. Received %s instead.',955            value,956          );957        }958      }959    // Fall through960    default: {961      if (962        key.length > 2 &&963        (key[0] === 'o' || key[0] === 'O') &&964        (key[1] === 'n' || key[1] === 'N')965      ) {966        if (967          __DEV__ &&968          registrationNameDependencies.hasOwnProperty(key) &&969          value != null &&970          typeof value !== 'function'971        ) {972          warnForInvalidEventListener(key, value);973        }974        // Updating events doesn't affect the visuals.975        return;976      } else {977        const attributeName = getAttributeAlias(key);978        setValueForAttribute(domElement, attributeName, value);979      }980    }981  }982  // To avoid marking things as host mutations we do early returns above.983  trackHostMutation();984}985986function setPropOnCustomElement(987  domElement: Element,988  tag: string,989  key: string,990  value: mixed,991  props: any,992  prevValue: mixed,993): void {994  switch (key) {995    case 'style': {996      setValueForStyles(domElement, value, prevValue);997      return;998    }999    case 'dangerouslySetInnerHTML': {1000      if (value != null) {1001        if (typeof value !== 'object' || !('__html' in value)) {1002          throw new Error(1003            '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +1004              'Please visit https://react.dev/link/dangerously-set-inner-html ' +1005              'for more information.',1006          );1007        }1008        const nextHtml: any = value.__html;1009        if (nextHtml != null) {1010          if (props.children != null) {1011            throw new Error(1012              'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',1013            );1014          }1015          const lastHtml: any =1016            prevValue != null ? (prevValue as any).__html : undefined;1017          if (lastHtml !== nextHtml) {1018            domElement.innerHTML = nextHtml;1019          }1020        }1021      }1022      break;1023    }1024    case 'children': {1025      if (typeof value === 'string') {1026        setTextContent(domElement, value);1027      } else if (typeof value === 'number' || typeof value === 'bigint') {1028        // $FlowFixMe[unsafe-addition] Flow doesn't want us to use `+` operator with string and bigint1029        setTextContent(domElement, '' + value);1030      } else {1031        return;1032      }1033      break;1034    }1035    case 'onScroll': {1036      if (value != null) {1037        if (__DEV__ && typeof value !== 'function') {1038          warnForInvalidEventListener(key, value);1039        }1040        listenToNonDelegatedEvent('scroll', domElement);1041      }1042      return;1043    }1044    case 'onScrollEnd': {1045      if (value != null) {1046        if (__DEV__ && typeof value !== 'function') {1047          warnForInvalidEventListener(key, value);1048        }1049        listenToNonDelegatedEvent('scrollend', domElement);1050        if (enableScrollEndPolyfill) {1051          // For use by the polyfill.1052          listenToNonDelegatedEvent('scroll', domElement);1053        }1054      }1055      return;1056    }1057    case 'onClick': {1058      // TODO: This cast may not be sound for SVG, MathML or custom elements.1059      if (value != null) {1060        if (__DEV__ && typeof value !== 'function') {1061          warnForInvalidEventListener(key, value);1062        }1063        trapClickOnNonInteractiveElement(domElement as any as HTMLElement);1064      }1065      return;1066    }1067    case 'suppressContentEditableWarning':1068    case 'suppressHydrationWarning':1069    case 'innerHTML':1070    case 'ref': {1071      // Noop1072      return;1073    }1074    case 'innerText': // Properties1075    case 'textContent':1076      return;1077    // Fall through1078    default: {1079      if (registrationNameDependencies.hasOwnProperty(key)) {1080        if (__DEV__ && value != null && typeof value !== 'function') {1081          warnForInvalidEventListener(key, value);1082        }1083        return;1084      } else {1085        setValueForPropertyOnCustomComponent(domElement, key, value);1086        // We track mutations inside this call.1087        return;1088      }1089    }1090  }1091  // To avoid marking things as host mutations we do early returns above.1092  trackHostMutation();1093}10941095export function setInitialProperties(1096  domElement: Element,1097  tag: string,1098  props: Object,1099): void {1100  if (__DEV__) {1101    validatePropertiesInDevelopment(tag, props);1102  }11031104  // TODO: Make sure that we check isMounted before firing any of these events.11051106  switch (tag) {1107    case 'div':1108    case 'span':1109    case 'svg':1110    case 'path':1111    case 'a':1112    case 'g':1113    case 'p':1114    case 'li': {1115      // Fast track the most common tag types1116      break;1117    }1118    // img tags previously were implemented as void elements with non delegated events however Safari (and possibly Firefox)1119    // begin fetching the image as soon as the `src` or `srcSet` property is set and if we set these before other properties1120    // that can modify the request (such as crossorigin) or the resource fetch (such as sizes) then the browser will load1121    // the wrong thing or load more than one thing. This implementation ensures src and srcSet are set on the instance last1122    case 'img': {1123      listenToNonDelegatedEvent('error', domElement);1124      listenToNonDelegatedEvent('load', domElement);1125      // Mostly a port of Void Element logic with special casing to ensure srcset and src are set last1126      let hasSrc = false;1127      let hasSrcSet = false;1128      for (const propKey in props) {1129        if (!props.hasOwnProperty(propKey)) {1130          continue;1131        }1132        const propValue = props[propKey];1133        if (propValue == null) {1134          continue;1135        }1136        switch (propKey) {1137          case 'src':1138            hasSrc = true;1139            break;1140          case 'srcSet':1141            hasSrcSet = true;1142            break;1143          case 'children':1144          case 'dangerouslySetInnerHTML': {1145            // TODO: Can we make this a DEV warning to avoid this deny list?1146            throw new Error(1147              `${tag} is a void element tag and must neither have \`children\` nor ` +1148                'use `dangerouslySetInnerHTML`.',1149            );1150          }1151          // defaultChecked and defaultValue are ignored by setProp1152          default: {1153            setProp(domElement, tag, propKey, propValue, props, null);1154          }1155        }1156      }1157      if (hasSrcSet) {1158        setProp(domElement, tag, 'srcSet', props.srcSet, props, null);1159      }1160      if (hasSrc) {1161        setProp(domElement, tag, 'src', props.src, props, null);1162      }1163      return;1164    }1165    case 'input': {1166      if (__DEV__) {1167        checkControlledValueProps('input', props);1168      }1169      // We listen to this event in case to ensure emulated bubble1170      // listeners still fire for the invalid event.1171      listenToNonDelegatedEvent('invalid', domElement);11721173      let name = null;1174      let type = null;1175      let value = null;1176      let defaultValue = null;1177      let checked = null;1178      let defaultChecked = null;1179      for (const propKey in props) {1180        if (!props.hasOwnProperty(propKey)) {1181          continue;1182        }1183        const propValue = props[propKey];1184        if (propValue == null) {1185          continue;1186        }1187        switch (propKey) {1188          case 'name': {1189            name = propValue;1190            break;1191          }1192          case 'type': {1193            type = propValue;1194            break;1195          }1196          case 'checked': {1197            checked = propValue;1198            break;1199          }1200          case 'defaultChecked': {1201            defaultChecked = propValue;1202            break;1203          }1204          case 'value': {1205            value = propValue;1206            break;1207          }1208          case 'defaultValue': {1209            defaultValue = propValue;1210            break;1211          }1212          case 'children':1213          case 'dangerouslySetInnerHTML': {1214            if (propValue != null) {1215              throw new Error(1216                `${tag} is a void element tag and must neither have \`children\` nor ` +1217                  'use `dangerouslySetInnerHTML`.',1218              );1219            }1220            break;1221          }1222          default: {1223            setProp(domElement, tag, propKey, propValue, props, null);1224          }1225        }1226      }1227      // TODO: Make sure we check if this is still unmounted or do any clean1228      // up necessary since we never stop tracking anymore.1229      validateInputProps(domElement, props);1230      initInput(1231        domElement,1232        value,1233        defaultValue,1234        checked,1235        defaultChecked,1236        type,1237        name,1238        false,1239      );1240      return;1241    }1242    case 'select': {1243      if (__DEV__) {1244        checkControlledValueProps('select', props);1245      }1246      // We listen to this event in case to ensure emulated bubble1247      // listeners still fire for the invalid event.1248      listenToNonDelegatedEvent('invalid', domElement);1249      let value = null;1250      let defaultValue = null;1251      let multiple = null;1252      for (const propKey in props) {1253        if (!props.hasOwnProperty(propKey)) {1254          continue;1255        }1256        const propValue = props[propKey];1257        if (propValue == null) {1258          continue;1259        }1260        switch (propKey) {1261          case 'value': {1262            value = propValue;1263            // This is handled by initSelect below.1264            break;1265          }1266          case 'defaultValue': {1267            defaultValue = propValue;1268            // This is handled by initSelect below.1269            break;1270          }1271          case 'multiple': {1272            multiple = propValue;1273            // TODO: We don't actually have to fall through here because we set it1274            // in initSelect anyway. We can remove the special case in setProp.1275          }1276          // Fallthrough1277          default: {1278            setProp(domElement, tag, propKey, propValue, props, null);1279          }1280        }1281      }1282      validateSelectProps(domElement, props);1283      initSelect(domElement, value, defaultValue, multiple);1284      return;1285    }1286    case 'textarea': {1287      if (__DEV__) {1288        checkControlledValueProps('textarea', props);1289      }1290      // We listen to this event in case to ensure emulated bubble1291      // listeners still fire for the invalid event.1292      listenToNonDelegatedEvent('invalid', domElement);1293      let value = null;1294      let defaultValue = null;1295      let children = null;1296      for (const propKey in props) {1297        if (!props.hasOwnProperty(propKey)) {1298          continue;1299        }1300        const propValue = props[propKey];1301        if (propValue == null) {1302          continue;1303        }1304        switch (propKey) {1305          case 'value': {1306            value = propValue;1307            // This is handled by initTextarea below.1308            break;1309          }1310          case 'defaultValue': {1311            defaultValue = propValue;1312            break;1313          }1314          case 'children': {1315            children = propValue;1316            // Handled by initTextarea above.1317            break;1318          }1319          case 'dangerouslySetInnerHTML': {1320            if (propValue != null) {1321              // TODO: Do we really need a special error message for this. It's also pretty blunt.1322              throw new Error(1323                '`dangerouslySetInnerHTML` does not make sense on <textarea>.',1324              );1325            }1326            break;1327          }1328          default: {1329            setProp(domElement, tag, propKey, propValue, props, null);1330          }1331        }1332      }1333      // TODO: Make sure we check if this is still unmounted or do any clean1334      // up necessary since we never stop tracking anymore.1335      validateTextareaProps(domElement, props);1336      initTextarea(domElement, value, defaultValue, children);1337      return;1338    }1339    case 'option': {1340      validateOptionProps(domElement, props);1341      for (const propKey in props) {1342        if (!props.hasOwnProperty(propKey)) {1343          continue;1344        }1345        const propValue = props[propKey];1346        if (propValue == null) {1347          continue;1348        }1349        switch (propKey) {1350          case 'selected': {1351            // TODO: Remove support for selected on option.1352            (domElement as any).selected =1353              propValue &&1354              typeof propValue !== 'function' &&1355              typeof propValue !== 'symbol';1356            break;1357          }1358          default: {1359            setProp(domElement, tag, propKey, propValue, props, null);1360          }1361        }1362      }1363      return;1364    }1365    case 'dialog': {1366      listenToNonDelegatedEvent('beforetoggle', domElement);1367      listenToNonDelegatedEvent('toggle', domElement);1368      listenToNonDelegatedEvent('cancel', domElement);1369      listenToNonDelegatedEvent('close', domElement);1370      break;1371    }1372    case 'iframe':1373    case 'object': {1374      // We listen to this event in case to ensure emulated bubble1375      // listeners still fire for the load event.1376      listenToNonDelegatedEvent('load', domElement);1377      break;1378    }1379    case 'video':1380    case 'audio': {1381      // We listen to these events in case to ensure emulated bubble1382      // listeners still fire for all the media events.1383      for (let i = 0; i < mediaEventTypes.length; i++) {1384        listenToNonDelegatedEvent(mediaEventTypes[i], domElement);1385      }1386      break;1387    }1388    case 'image': {1389      // We listen to these events in case to ensure emulated bubble1390      // listeners still fire for error and load events.1391      listenToNonDelegatedEvent('error', domElement);1392      listenToNonDelegatedEvent('load', domElement);1393      break;1394    }1395    case 'details': {1396      // We listen to this event in case to ensure emulated bubble1397      // listeners still fire for the toggle event.1398      listenToNonDelegatedEvent('toggle', domElement);1399      break;1400    }1401    case 'embed':1402    case 'source':1403    case 'link': {1404      // These are void elements that also need delegated events.1405      listenToNonDelegatedEvent('error', domElement);1406      listenToNonDelegatedEvent('load', domElement);1407      // We fallthrough to the return of the void elements1408    }1409    case 'area':1410    case 'base':1411    case 'br':1412    case 'col':1413    case 'hr':1414    case 'keygen':1415    case 'meta':1416    case 'param':1417    case 'track':1418    case 'wbr':1419    case 'menuitem': {1420      // Void elements1421      for (const propKey in props) {1422        if (!props.hasOwnProperty(propKey)) {1423          continue;1424        }1425        const propValue = props[propKey];1426        if (propValue == null) {1427          continue;1428        }1429        switch (propKey) {1430          case 'children':1431          case 'dangerouslySetInnerHTML': {1432            // TODO: Can we make this a DEV warning to avoid this deny list?1433            throw new Error(1434              `${tag} is a void element tag and must neither have \`children\` nor ` +1435                'use `dangerouslySetInnerHTML`.',1436            );1437          }1438          // defaultChecked and defaultValue are ignored by setProp1439          default: {1440            setProp(domElement, tag, propKey, propValue, props, null);1441          }1442        }1443      }1444      return;1445    }1446    default: {1447      if (isCustomElement(tag, props)) {1448        for (const propKey in props) {1449          if (!props.hasOwnProperty(propKey)) {1450            continue;1451          }1452          const propValue = props[propKey];1453          if (propValue === undefined) {1454            continue;1455          }1456          setPropOnCustomElement(1457            domElement,1458            tag,1459            propKey,1460            propValue,1461            props,1462            undefined,1463          );1464        }1465        return;1466      }1467    }1468  }14691470  for (const propKey in props) {1471    if (!props.hasOwnProperty(propKey)) {1472      continue;1473    }1474    const propValue = props[propKey];1475    if (propValue == null) {1476      continue;1477    }1478    setProp(domElement, tag, propKey, propValue, props, null);1479  }1480}14811482export type SingletonType = 'html' | 'head' | 'body';14831484const emptyProps = {};14851486export function clearSingletonProperties(1487  domElement: Element,1488  tag: SingletonType,1489  props: Object,1490): void {1491  // This is equivalent to updating to empty props for tags without1492  // tag-specific update logic. Host singletons are limited to html, head, and1493  // body, so they always use this generic path.1494  for (const propKey in props) {1495    const propValue = props[propKey];1496    if (props.hasOwnProperty(propKey) && propValue != null) {1497      setProp(domElement, tag, propKey, null, emptyProps, propValue);1498    }1499  }1500}15011502export function updateProperties(1503  domElement: Element,1504  tag: string,1505  lastProps: Object,1506  nextProps: Object,1507): void {1508  if (__DEV__) {1509    validatePropertiesInDevelopment(tag, nextProps);1510  }15111512  switch (tag) {1513    case 'div':1514    case 'span':1515    case 'svg':1516    case 'path':1517    case 'a':1518    case 'g':1519    case 'p':1520    case 'li': {1521      // Fast track the most common tag types1522      break;1523    }1524    case 'input': {1525      let name = null;1526      let type = null;1527      let value = null;1528      let defaultValue = null;1529      let lastDefaultValue = null;1530      let checked = null;1531      let defaultChecked = null;1532      for (const propKey in lastProps) {1533        const lastProp = lastProps[propKey];1534        if (lastProps.hasOwnProperty(propKey) && lastProp != null) {1535          switch (propKey) {1536            case 'checked': {1537              break;1538            }1539            case 'value': {1540              // This is handled by updateWrapper below.1541              break;1542            }1543            case 'defaultValue': {1544              lastDefaultValue = lastProp;1545            }1546            // defaultChecked and defaultValue are ignored by setProp1547            // Fallthrough1548            default: {1549              if (!nextProps.hasOwnProperty(propKey))1550                setProp(domElement, tag, propKey, null, nextProps, lastProp);1551            }1552          }1553        }1554      }1555      for (const propKey in nextProps) {1556        const nextProp = nextProps[propKey];1557        const lastProp = lastProps[propKey];1558        if (1559          nextProps.hasOwnProperty(propKey) &&1560          (nextProp != null || lastProp != null)1561        ) {1562          switch (propKey) {1563            case 'type': {1564              if (nextProp !== lastProp) {1565                trackHostMutation();1566              }1567              type = nextProp;1568              break;1569            }1570            case 'name': {1571              if (nextProp !== lastProp) {1572                trackHostMutation();1573              }1574              name = nextProp;1575              break;1576            }1577            case 'checked': {1578              if (nextProp !== lastProp) {1579                trackHostMutation();1580              }1581              checked = nextProp;1582              break;1583            }1584            case 'defaultChecked': {1585              if (nextProp !== lastProp) {1586                trackHostMutation();1587              }1588              defaultChecked = nextProp;1589              break;1590            }1591            case 'value': {1592              if (nextProp !== lastProp) {1593                trackHostMutation();1594              }1595              value = nextProp;1596              break;1597            }1598            case 'defaultValue': {1599              if (nextProp !== lastProp) {1600                trackHostMutation();1601              }1602              defaultValue = nextProp;1603              break;1604            }1605            case 'children':1606            case 'dangerouslySetInnerHTML': {1607              if (nextProp != null) {1608                throw new Error(1609                  `${tag} is a void element tag and must neither have \`children\` nor ` +1610                    'use `dangerouslySetInnerHTML`.',1611                );1612              }1613              break;1614            }1615            default: {1616              if (nextProp !== lastProp)1617                setProp(1618                  domElement,1619                  tag,1620                  propKey,1621                  nextProp,1622                  nextProps,1623                  lastProp,1624                );1625            }1626          }1627        }1628      }16291630      if (__DEV__) {1631        const wasControlled =1632          lastProps.type === 'checkbox' || lastProps.type === 'radio'1633            ? lastProps.checked != null1634            : lastProps.value != null;1635        const isControlled =1636          nextProps.type === 'checkbox' || nextProps.type === 'radio'1637            ? nextProps.checked != null1638            : nextProps.value != null;16391640        if (1641          !wasControlled &&1642          isControlled &&1643          !didWarnUncontrolledToControlled1644        ) {1645          console.error(1646            'A component is changing an uncontrolled input to be controlled. ' +1647              'This is likely caused by the value changing from undefined to ' +1648              'a defined value, which should not happen. ' +1649              'Decide between using a controlled or uncontrolled input ' +1650              'element for the lifetime of the component. More info: https://react.dev/link/controlled-components',1651          );1652          didWarnUncontrolledToControlled = true;1653        }1654        if (1655          wasControlled &&1656          !isControlled &&1657          !didWarnControlledToUncontrolled1658        ) {1659          console.error(1660            'A component is changing a controlled input to be uncontrolled. ' +1661              'This is likely caused by the value changing from a defined to ' +1662              'undefined, which should not happen. ' +1663              'Decide between using a controlled or uncontrolled input ' +1664              'element for the lifetime of the component. More info: https://react.dev/link/controlled-components',1665          );1666          didWarnControlledToUncontrolled = true;1667        }1668      }16691670      // Update the wrapper around inputs *after* updating props. This has to1671      // happen after updating the rest of props. Otherwise HTML5 input validations1672      // raise warnings and prevent the new value from being assigned.1673      updateInput(1674        domElement,1675        value,1676        defaultValue,1677        lastDefaultValue,1678        checked,1679        defaultChecked,1680        type,1681        name,1682      );1683      return;1684    }1685    case 'select': {1686      let value = null;1687      let defaultValue = null;1688      let multiple = null;1689      let wasMultiple = null;1690      for (const propKey in lastProps) {1691        const lastProp = lastProps[propKey];1692        if (lastProps.hasOwnProperty(propKey) && lastProp != null) {1693          switch (propKey) {1694            case 'value': {1695              // This is handled by updateWrapper below.1696              break;1697            }1698            // defaultValue are ignored by setProp1699            case 'multiple': {1700              wasMultiple = lastProp;1701              // TODO: Move special case in here from setProp.1702            }1703            // Fallthrough1704            default: {1705              if (!nextProps.hasOwnProperty(propKey)) {1706                setProp(domElement, tag, propKey, null, nextProps, lastProp);1707              }1708            }1709          }1710        }1711      }1712      for (const propKey in nextProps) {1713        const nextProp = nextProps[propKey];1714        const lastProp = lastProps[propKey];1715        if (1716          nextProps.hasOwnProperty(propKey) &&1717          (nextProp != null || lastProp != null)1718        ) {1719          switch (propKey) {1720            case 'value': {1721              if (nextProp !== lastProp) {1722                trackHostMutation();1723              }1724              value = nextProp;1725              // This is handled by updateSelect below.1726              break;1727            }1728            case 'defaultValue': {1729              if (nextProp !== lastProp) {1730                trackHostMutation();1731              }1732              defaultValue = nextProp;1733              break;1734            }1735            case 'multiple': {1736              if (nextProp !== lastProp) {1737                trackHostMutation();1738              }1739              multiple = nextProp;1740              // TODO: Just move the special case in here from setProp.1741            }1742            // Fallthrough1743            default: {1744              if (nextProp !== lastProp)1745                setProp(1746                  domElement,1747                  tag,1748                  propKey,1749                  nextProp,1750                  nextProps,1751                  lastProp,1752                );1753            }1754          }1755        }1756      }1757      // <select> value update needs to occur after <option> children1758      // reconciliation1759      updateSelect(domElement, value, defaultValue, multiple, wasMultiple);1760      return;1761    }1762    case 'textarea': {1763      let value = null;1764      let defaultValue = null;1765      for (const propKey in lastProps) {1766        const lastProp = lastProps[propKey];1767        if (1768          lastProps.hasOwnProperty(propKey) &&1769          lastProp != null &&1770          !nextProps.hasOwnProperty(propKey)1771        ) {1772          switch (propKey) {1773            case 'value': {1774              // This is handled by updateTextarea below.1775              break;1776            }1777            case 'children': {1778              // TODO: This doesn't actually do anything if it updates.1779              break;1780            }1781            // defaultValue is ignored by setProp1782            default: {1783              setProp(domElement, tag, propKey, null, nextProps, lastProp);1784            }1785          }1786        }1787      }1788      for (const propKey in nextProps) {1789        const nextProp = nextProps[propKey];1790        const lastProp = lastProps[propKey];1791        if (1792          nextProps.hasOwnProperty(propKey) &&1793          (nextProp != null || lastProp != null)1794        ) {1795          switch (propKey) {1796            case 'value': {1797              if (nextProp !== lastProp) {1798                trackHostMutation();1799              }1800              value = nextProp;1801              // This is handled by updateTextarea below.1802              break;1803            }1804            case 'defaultValue': {1805              if (nextProp !== lastProp) {1806                trackHostMutation();1807              }1808              defaultValue = nextProp;1809              break;1810            }1811            case 'children': {1812              // TODO: This doesn't actually do anything if it updates.1813              break;1814            }1815            case 'dangerouslySetInnerHTML': {1816              if (nextProp != null) {1817                // TODO: Do we really need a special error message for this. It's also pretty blunt.1818                throw new Error(1819                  '`dangerouslySetInnerHTML` does not make sense on <textarea>.',1820                );1821              }1822              break;1823            }1824            default: {1825              if (nextProp !== lastProp)1826                setProp(1827                  domElement,1828                  tag,1829                  propKey,1830                  nextProp,1831                  nextProps,1832                  lastProp,1833                );1834            }1835          }1836        }1837      }1838      updateTextarea(domElement, value, defaultValue);1839      return;1840    }1841    case 'option': {1842      for (const propKey in lastProps) {1843        const lastProp = lastProps[propKey];1844        if (1845          lastProps.hasOwnProperty(propKey) &&1846          lastProp != null &&1847          !nextProps.hasOwnProperty(propKey)1848        ) {1849          switch (propKey) {1850            case 'selected': {1851              // TODO: Remove support for selected on option.1852              (domElement as any).selected = false;1853              break;1854            }1855            default: {1856              setProp(domElement, tag, propKey, null, nextProps, lastProp);1857            }1858          }1859        }1860      }1861      for (const propKey in nextProps) {1862        const nextProp = nextProps[propKey];1863        const lastProp = lastProps[propKey];1864        if (1865          nextProps.hasOwnProperty(propKey) &&1866          nextProp !== lastProp &&1867          (nextProp != null || lastProp != null)1868        ) {1869          switch (propKey) {1870            case 'selected': {1871              if (nextProp !== lastProp) {1872                trackHostMutation();1873              }1874              // TODO: Remove support for selected on option.1875              (domElement as any).selected =1876                nextProp &&1877                typeof nextProp !== 'function' &&1878                typeof nextProp !== 'symbol';1879              break;1880            }1881            default: {1882              setProp(domElement, tag, propKey, nextProp, nextProps, lastProp);1883            }1884          }1885        }1886      }1887      return;1888    }1889    case 'img':1890    case 'link':1891    case 'area':1892    case 'base':1893    case 'br':1894    case 'col':1895    case 'embed':1896    case 'hr':1897    case 'keygen':1898    case 'meta':1899    case 'param':1900    case 'source':1901    case 'track':1902    case 'wbr':1903    case 'menuitem': {1904      // Void elements1905      for (const propKey in lastProps) {1906        const lastProp = lastProps[propKey];1907        if (1908          lastProps.hasOwnProperty(propKey) &&1909          lastProp != null &&1910          !nextProps.hasOwnProperty(propKey)1911        ) {1912          setProp(domElement, tag, propKey, null, nextProps, lastProp);1913        }1914      }1915      for (const propKey in nextProps) {1916        const nextProp = nextProps[propKey];1917        const lastProp = lastProps[propKey];1918        if (1919          nextProps.hasOwnProperty(propKey) &&1920          nextProp !== lastProp &&1921          (nextProp != null || lastProp != null)1922        ) {1923          switch (propKey) {1924            case 'children':1925            case 'dangerouslySetInnerHTML': {1926              if (nextProp != null) {1927                // TODO: Can we make this a DEV warning to avoid this deny list?1928                throw new Error(1929                  `${tag} is a void element tag and must neither have \`children\` nor ` +1930                    'use `dangerouslySetInnerHTML`.',1931                );1932              }1933              break;1934            }1935            // defaultChecked and defaultValue are ignored by setProp1936            default: {1937              setProp(domElement, tag, propKey, nextProp, nextProps, lastProp);1938            }1939          }1940        }1941      }1942      return;1943    }1944    default: {1945      if (isCustomElement(tag, nextProps)) {1946        for (const propKey in lastProps) {1947          const lastProp = lastProps[propKey];1948          if (1949            lastProps.hasOwnProperty(propKey) &&1950            lastProp !== undefined &&1951            !nextProps.hasOwnProperty(propKey)1952          ) {1953            setPropOnCustomElement(1954              domElement,1955              tag,1956              propKey,1957              undefined,1958              nextProps,1959              lastProp,1960            );1961          }1962        }1963        for (const propKey in nextProps) {1964          const nextProp = nextProps[propKey];1965          const lastProp = lastProps[propKey];1966          if (1967            nextProps.hasOwnProperty(propKey) &&1968            nextProp !== lastProp &&1969            (nextProp !== undefined || lastProp !== undefined)1970          ) {1971            setPropOnCustomElement(1972              domElement,1973              tag,1974              propKey,1975              nextProp,1976              nextProps,1977              lastProp,1978            );1979          }1980        }1981        return;1982      }1983    }1984  }19851986  for (const propKey in lastProps) {1987    const lastProp = lastProps[propKey];1988    if (1989      lastProps.hasOwnProperty(propKey) &&1990      lastProp != null &&1991      !nextProps.hasOwnProperty(propKey)1992    ) {1993      setProp(domElement, tag, propKey, null, nextProps, lastProp);1994    }1995  }1996  for (const propKey in nextProps) {1997    const nextProp = nextProps[propKey];1998    const lastProp = lastProps[propKey];1999    if (2000      nextProps.hasOwnProperty(propKey) &&

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