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

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.