1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import type {HostContext, HostContextDev} from './ReactFiberConfigDOM';
    
  11. 
    
  12. import {HostContextNamespaceNone} from './ReactFiberConfigDOM';
    
  13. 
    
  14. import {
    
  15.   registrationNameDependencies,
    
  16.   possibleRegistrationNames,
    
  17. } from '../events/EventRegistry';
    
  18. 
    
  19. import {canUseDOM} from 'shared/ExecutionEnvironment';
    
  20. import {checkHtmlStringCoercion} from 'shared/CheckStringCoercion';
    
  21. import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion';
    
  22. import {checkControlledValueProps} from '../shared/ReactControlledValuePropTypes';
    
  23. 
    
  24. import {
    
  25.   getValueForAttribute,
    
  26.   getValueForAttributeOnCustomComponent,
    
  27.   setValueForPropertyOnCustomComponent,
    
  28.   setValueForKnownAttribute,
    
  29.   setValueForAttribute,
    
  30.   setValueForNamespacedAttribute,
    
  31. } from './DOMPropertyOperations';
    
  32. import {
    
  33.   validateInputProps,
    
  34.   initInput,
    
  35.   updateInput,
    
  36.   restoreControlledInputState,
    
  37. } from './ReactDOMInput';
    
  38. import {validateOptionProps} from './ReactDOMOption';
    
  39. import {
    
  40.   validateSelectProps,
    
  41.   initSelect,
    
  42.   restoreControlledSelectState,
    
  43.   updateSelect,
    
  44. } from './ReactDOMSelect';
    
  45. import {
    
  46.   validateTextareaProps,
    
  47.   initTextarea,
    
  48.   updateTextarea,
    
  49.   restoreControlledTextareaState,
    
  50. } from './ReactDOMTextarea';
    
  51. import {validateTextNesting} from './validateDOMNesting';
    
  52. import {track} from './inputValueTracking';
    
  53. import setInnerHTML from './setInnerHTML';
    
  54. import setTextContent from './setTextContent';
    
  55. import {
    
  56.   createDangerousStringForStyles,
    
  57.   setValueForStyles,
    
  58. } from './CSSPropertyOperations';
    
  59. import {SVG_NAMESPACE, MATH_NAMESPACE} from './DOMNamespaces';
    
  60. import isCustomElement from '../shared/isCustomElement';
    
  61. import getAttributeAlias from '../shared/getAttributeAlias';
    
  62. import possibleStandardNames from '../shared/possibleStandardNames';
    
  63. import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook';
    
  64. import {validateProperties as validateInputProperties} from '../shared/ReactDOMNullInputValuePropHook';
    
  65. import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook';
    
  66. import sanitizeURL from '../shared/sanitizeURL';
    
  67. 
    
  68. import {
    
  69.   enableCustomElementPropertySupport,
    
  70.   enableClientRenderFallbackOnTextMismatch,
    
  71.   enableFormActions,
    
  72.   enableHostSingletons,
    
  73.   disableIEWorkarounds,
    
  74.   enableTrustedTypesIntegration,
    
  75.   enableFilterEmptyStringAttributesDOM,
    
  76. } from 'shared/ReactFeatureFlags';
    
  77. import {
    
  78.   mediaEventTypes,
    
  79.   listenToNonDelegatedEvent,
    
  80. } from '../events/DOMPluginEventSystem';
    
  81. 
    
  82. let didWarnControlledToUncontrolled = false;
    
  83. let didWarnUncontrolledToControlled = false;
    
  84. let didWarnInvalidHydration = false;
    
  85. let didWarnFormActionType = false;
    
  86. let didWarnFormActionName = false;
    
  87. let didWarnFormActionTarget = false;
    
  88. let didWarnFormActionMethod = false;
    
  89. let canDiffStyleForHydrationWarning;
    
  90. if (__DEV__) {
    
  91.   // IE 11 parses & normalizes the style attribute as opposed to other
    
  92.   // browsers. It adds spaces and sorts the properties in some
    
  93.   // non-alphabetical order. Handling that would require sorting CSS
    
  94.   // properties in the client & server versions or applying
    
  95.   // `expectedStyle` to a temporary DOM node to read its `style` attribute
    
  96.   // normalized. Since it only affects IE, we're skipping style warnings
    
  97.   // in that browser completely in favor of doing all that work.
    
  98.   // See https://github.com/facebook/react/issues/11807
    
  99.   canDiffStyleForHydrationWarning =
    
  100.     disableIEWorkarounds || (canUseDOM && !document.documentMode);
    
  101. }
    
  102. 
    
  103. function validatePropertiesInDevelopment(type: string, props: any) {
    
  104.   if (__DEV__) {
    
  105.     validateARIAProperties(type, props);
    
  106.     validateInputProperties(type, props);
    
  107.     validateUnknownProperties(type, props, {
    
  108.       registrationNameDependencies,
    
  109.       possibleRegistrationNames,
    
  110.     });
    
  111.     if (
    
  112.       props.contentEditable &&
    
  113.       !props.suppressContentEditableWarning &&
    
  114.       props.children != null
    
  115.     ) {
    
  116.       console.error(
    
  117.         'A component is `contentEditable` and contains `children` managed by ' +
    
  118.           'React. It is now your responsibility to guarantee that none of ' +
    
  119.           'those nodes are unexpectedly modified or duplicated. This is ' +
    
  120.           'probably not intentional.',
    
  121.       );
    
  122.     }
    
  123.   }
    
  124. }
    
  125. 
    
  126. function validateFormActionInDevelopment(
    
  127.   tag: string,
    
  128.   key: string,
    
  129.   value: mixed,
    
  130.   props: any,
    
  131. ) {
    
  132.   if (__DEV__) {
    
  133.     if (value == null) {
    
  134.       return;
    
  135.     }
    
  136.     if (tag === 'form') {
    
  137.       if (key === 'formAction') {
    
  138.         console.error(
    
  139.           'You can only pass the formAction prop to <input> or <button>. Use the action prop on <form>.',
    
  140.         );
    
  141.       } else if (typeof value === 'function') {
    
  142.         if (
    
  143.           (props.encType != null || props.method != null) &&
    
  144.           !didWarnFormActionMethod
    
  145.         ) {
    
  146.           didWarnFormActionMethod = true;
    
  147.           console.error(
    
  148.             'Cannot specify a encType or method for a form that specifies a ' +
    
  149.               'function as the action. React provides those automatically. ' +
    
  150.               'They will get overridden.',
    
  151.           );
    
  152.         }
    
  153.         if (props.target != null && !didWarnFormActionTarget) {
    
  154.           didWarnFormActionTarget = true;
    
  155.           console.error(
    
  156.             'Cannot specify a target for a form that specifies a function as the action. ' +
    
  157.               'The function will always be executed in the same window.',
    
  158.           );
    
  159.         }
    
  160.       }
    
  161.     } else if (tag === 'input' || tag === 'button') {
    
  162.       if (key === 'action') {
    
  163.         console.error(
    
  164.           'You can only pass the action prop to <form>. Use the formAction prop on <input> or <button>.',
    
  165.         );
    
  166.       } else if (
    
  167.         tag === 'input' &&
    
  168.         props.type !== 'submit' &&
    
  169.         props.type !== 'image' &&
    
  170.         !didWarnFormActionType
    
  171.       ) {
    
  172.         didWarnFormActionType = true;
    
  173.         console.error(
    
  174.           'An input can only specify a formAction along with type="submit" or type="image".',
    
  175.         );
    
  176.       } else if (
    
  177.         tag === 'button' &&
    
  178.         props.type != null &&
    
  179.         props.type !== 'submit' &&
    
  180.         !didWarnFormActionType
    
  181.       ) {
    
  182.         didWarnFormActionType = true;
    
  183.         console.error(
    
  184.           'A button can only specify a formAction along with type="submit" or no type.',
    
  185.         );
    
  186.       } else if (typeof value === 'function') {
    
  187.         // Function form actions cannot control the form properties
    
  188.         if (props.name != null && !didWarnFormActionName) {
    
  189.           didWarnFormActionName = true;
    
  190.           console.error(
    
  191.             'Cannot specify a "name" prop for a button that specifies a function as a formAction. ' +
    
  192.               'React needs it to encode which action should be invoked. It will get overridden.',
    
  193.           );
    
  194.         }
    
  195.         if (
    
  196.           (props.formEncType != null || props.formMethod != null) &&
    
  197.           !didWarnFormActionMethod
    
  198.         ) {
    
  199.           didWarnFormActionMethod = true;
    
  200.           console.error(
    
  201.             'Cannot specify a formEncType or formMethod for a button that specifies a ' +
    
  202.               'function as a formAction. React provides those automatically. They will get overridden.',
    
  203.           );
    
  204.         }
    
  205.         if (props.formTarget != null && !didWarnFormActionTarget) {
    
  206.           didWarnFormActionTarget = true;
    
  207.           console.error(
    
  208.             'Cannot specify a formTarget for a button that specifies a function as a formAction. ' +
    
  209.               'The function will always be executed in the same window.',
    
  210.           );
    
  211.         }
    
  212.       }
    
  213.     } else {
    
  214.       if (key === 'action') {
    
  215.         console.error('You can only pass the action prop to <form>.');
    
  216.       } else {
    
  217.         console.error(
    
  218.           'You can only pass the formAction prop to <input> or <button>.',
    
  219.         );
    
  220.       }
    
  221.     }
    
  222.   }
    
  223. }
    
  224. 
    
  225. function warnForPropDifference(
    
  226.   propName: string,
    
  227.   serverValue: mixed,
    
  228.   clientValue: mixed,
    
  229. ) {
    
  230.   if (__DEV__) {
    
  231.     if (didWarnInvalidHydration) {
    
  232.       return;
    
  233.     }
    
  234.     if (serverValue === clientValue) {
    
  235.       return;
    
  236.     }
    
  237.     const normalizedClientValue =
    
  238.       normalizeMarkupForTextOrAttribute(clientValue);
    
  239.     const normalizedServerValue =
    
  240.       normalizeMarkupForTextOrAttribute(serverValue);
    
  241.     if (normalizedServerValue === normalizedClientValue) {
    
  242.       return;
    
  243.     }
    
  244.     didWarnInvalidHydration = true;
    
  245.     console.error(
    
  246.       'Prop `%s` did not match. Server: %s Client: %s',
    
  247.       propName,
    
  248.       JSON.stringify(normalizedServerValue),
    
  249.       JSON.stringify(normalizedClientValue),
    
  250.     );
    
  251.   }
    
  252. }
    
  253. 
    
  254. function warnForExtraAttributes(attributeNames: Set<string>) {
    
  255.   if (__DEV__) {
    
  256.     if (didWarnInvalidHydration) {
    
  257.       return;
    
  258.     }
    
  259.     didWarnInvalidHydration = true;
    
  260.     const names = [];
    
  261.     attributeNames.forEach(function (name) {
    
  262.       names.push(name);
    
  263.     });
    
  264.     console.error('Extra attributes from the server: %s', names);
    
  265.   }
    
  266. }
    
  267. 
    
  268. function warnForInvalidEventListener(registrationName: string, listener: any) {
    
  269.   if (__DEV__) {
    
  270.     if (listener === false) {
    
  271.       console.error(
    
  272.         'Expected `%s` listener to be a function, instead got `false`.\n\n' +
    
  273.           'If you used to conditionally omit it with %s={condition && value}, ' +
    
  274.           'pass %s={condition ? value : undefined} instead.',
    
  275.         registrationName,
    
  276.         registrationName,
    
  277.         registrationName,
    
  278.       );
    
  279.     } else {
    
  280.       console.error(
    
  281.         'Expected `%s` listener to be a function, instead got a value of `%s` type.',
    
  282.         registrationName,
    
  283.         typeof listener,
    
  284.       );
    
  285.     }
    
  286.   }
    
  287. }
    
  288. 
    
  289. // Parse the HTML and read it back to normalize the HTML string so that it
    
  290. // can be used for comparison.
    
  291. function normalizeHTML(parent: Element, html: string) {
    
  292.   if (__DEV__) {
    
  293.     // We could have created a separate document here to avoid
    
  294.     // re-initializing custom elements if they exist. But this breaks
    
  295.     // how <noscript> is being handled. So we use the same document.
    
  296.     // See the discussion in https://github.com/facebook/react/pull/11157.
    
  297.     const testElement =
    
  298.       parent.namespaceURI === MATH_NAMESPACE ||
    
  299.       parent.namespaceURI === SVG_NAMESPACE
    
  300.         ? parent.ownerDocument.createElementNS(
    
  301.             (parent.namespaceURI: any),
    
  302.             parent.tagName,
    
  303.           )
    
  304.         : parent.ownerDocument.createElement(parent.tagName);
    
  305.     testElement.innerHTML = html;
    
  306.     return testElement.innerHTML;
    
  307.   }
    
  308. }
    
  309. 
    
  310. // HTML parsing normalizes CR and CRLF to LF.
    
  311. // It also can turn \u0000 into \uFFFD inside attributes.
    
  312. // https://www.w3.org/TR/html5/single-page.html#preprocessing-the-input-stream
    
  313. // If we have a mismatch, it might be caused by that.
    
  314. // We will still patch up in this case but not fire the warning.
    
  315. const NORMALIZE_NEWLINES_REGEX = /\r\n?/g;
    
  316. const NORMALIZE_NULL_AND_REPLACEMENT_REGEX = /\u0000|\uFFFD/g;
    
  317. 
    
  318. function normalizeMarkupForTextOrAttribute(markup: mixed): string {
    
  319.   if (__DEV__) {
    
  320.     checkHtmlStringCoercion(markup);
    
  321.   }
    
  322.   const markupString = typeof markup === 'string' ? markup : '' + (markup: any);
    
  323.   return markupString
    
  324.     .replace(NORMALIZE_NEWLINES_REGEX, '\n')
    
  325.     .replace(NORMALIZE_NULL_AND_REPLACEMENT_REGEX, '');
    
  326. }
    
  327. 
    
  328. export function checkForUnmatchedText(
    
  329.   serverText: string,
    
  330.   clientText: string | number,
    
  331.   isConcurrentMode: boolean,
    
  332.   shouldWarnDev: boolean,
    
  333. ) {
    
  334.   const normalizedClientText = normalizeMarkupForTextOrAttribute(clientText);
    
  335.   const normalizedServerText = normalizeMarkupForTextOrAttribute(serverText);
    
  336.   if (normalizedServerText === normalizedClientText) {
    
  337.     return;
    
  338.   }
    
  339. 
    
  340.   if (shouldWarnDev) {
    
  341.     if (__DEV__) {
    
  342.       if (!didWarnInvalidHydration) {
    
  343.         didWarnInvalidHydration = true;
    
  344.         console.error(
    
  345.           'Text content did not match. Server: "%s" Client: "%s"',
    
  346.           normalizedServerText,
    
  347.           normalizedClientText,
    
  348.         );
    
  349.       }
    
  350.     }
    
  351.   }
    
  352. 
    
  353.   if (isConcurrentMode && enableClientRenderFallbackOnTextMismatch) {
    
  354.     // In concurrent roots, we throw when there's a text mismatch and revert to
    
  355.     // client rendering, up to the nearest Suspense boundary.
    
  356.     throw new Error('Text content does not match server-rendered HTML.');
    
  357.   }
    
  358. }
    
  359. 
    
  360. function noop() {}
    
  361. 
    
  362. export function trapClickOnNonInteractiveElement(node: HTMLElement) {
    
  363.   // Mobile Safari does not fire properly bubble click events on
    
  364.   // non-interactive elements, which means delegated click listeners do not
    
  365.   // fire. The workaround for this bug involves attaching an empty click
    
  366.   // listener on the target node.
    
  367.   // https://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
    
  368.   // Just set it using the onclick property so that we don't have to manage any
    
  369.   // bookkeeping for it. Not sure if we need to clear it when the listener is
    
  370.   // removed.
    
  371.   // TODO: Only do this for the relevant Safaris maybe?
    
  372.   node.onclick = noop;
    
  373. }
    
  374. 
    
  375. const xlinkNamespace = 'http://www.w3.org/1999/xlink';
    
  376. const xmlNamespace = 'http://www.w3.org/XML/1998/namespace';
    
  377. 
    
  378. function setProp(
    
  379.   domElement: Element,
    
  380.   tag: string,
    
  381.   key: string,
    
  382.   value: mixed,
    
  383.   props: any,
    
  384.   prevValue: mixed,
    
  385. ): void {
    
  386.   switch (key) {
    
  387.     case 'children': {
    
  388.       if (typeof value === 'string') {
    
  389.         if (__DEV__) {
    
  390.           validateTextNesting(value, tag);
    
  391.         }
    
  392.         // Avoid setting initial textContent when the text is empty. In IE11 setting
    
  393.         // textContent on a <textarea> will cause the placeholder to not
    
  394.         // show within the <textarea> until it has been focused and blurred again.
    
  395.         // https://github.com/facebook/react/issues/6731#issuecomment-254874553
    
  396.         const canSetTextContent =
    
  397.           (!enableHostSingletons || tag !== 'body') &&
    
  398.           (tag !== 'textarea' || value !== '');
    
  399.         if (canSetTextContent) {
    
  400.           setTextContent(domElement, value);
    
  401.         }
    
  402.       } else if (typeof value === 'number') {
    
  403.         if (__DEV__) {
    
  404.           validateTextNesting('' + value, tag);
    
  405.         }
    
  406.         const canSetTextContent = !enableHostSingletons || tag !== 'body';
    
  407.         if (canSetTextContent) {
    
  408.           setTextContent(domElement, '' + value);
    
  409.         }
    
  410.       }
    
  411.       break;
    
  412.     }
    
  413.     // These are very common props and therefore are in the beginning of the switch.
    
  414.     // TODO: aria-label is a very common prop but allows booleans so is not like the others
    
  415.     // but should ideally go in this list too.
    
  416.     case 'className':
    
  417.       setValueForKnownAttribute(domElement, 'class', value);
    
  418.       break;
    
  419.     case 'tabIndex':
    
  420.       // This has to be case sensitive in SVG.
    
  421.       setValueForKnownAttribute(domElement, 'tabindex', value);
    
  422.       break;
    
  423.     case 'dir':
    
  424.     case 'role':
    
  425.     case 'viewBox':
    
  426.     case 'width':
    
  427.     case 'height': {
    
  428.       setValueForKnownAttribute(domElement, key, value);
    
  429.       break;
    
  430.     }
    
  431.     case 'style': {
    
  432.       setValueForStyles(domElement, value, prevValue);
    
  433.       break;
    
  434.     }
    
  435.     // These attributes accept URLs. These must not allow javascript: URLS.
    
  436.     case 'src':
    
  437.     case 'href': {
    
  438.       if (enableFilterEmptyStringAttributesDOM) {
    
  439.         if (value === '') {
    
  440.           if (__DEV__) {
    
  441.             if (key === 'src') {
    
  442.               console.error(
    
  443.                 'An empty string ("") was passed to the %s attribute. ' +
    
  444.                   'This may cause the browser to download the whole page again over the network. ' +
    
  445.                   'To fix this, either do not render the element at all ' +
    
  446.                   'or pass null to %s instead of an empty string.',
    
  447.                 key,
    
  448.                 key,
    
  449.               );
    
  450.             } else {
    
  451.               console.error(
    
  452.                 'An empty string ("") was passed to the %s attribute. ' +
    
  453.                   'To fix this, either do not render the element at all ' +
    
  454.                   'or pass null to %s instead of an empty string.',
    
  455.                 key,
    
  456.                 key,
    
  457.               );
    
  458.             }
    
  459.           }
    
  460.           domElement.removeAttribute(key);
    
  461.           break;
    
  462.         }
    
  463.       }
    
  464.       if (
    
  465.         value == null ||
    
  466.         typeof value === 'function' ||
    
  467.         typeof value === 'symbol' ||
    
  468.         typeof value === 'boolean'
    
  469.       ) {
    
  470.         domElement.removeAttribute(key);
    
  471.         break;
    
  472.       }
    
  473.       // `setAttribute` with objects becomes only `[object]` in IE8/9,
    
  474.       // ('' + value) makes it output the correct toString()-value.
    
  475.       if (__DEV__) {
    
  476.         checkAttributeStringCoercion(value, key);
    
  477.       }
    
  478.       const sanitizedValue = (sanitizeURL(
    
  479.         enableTrustedTypesIntegration ? value : '' + (value: any),
    
  480.       ): any);
    
  481.       domElement.setAttribute(key, sanitizedValue);
    
  482.       break;
    
  483.     }
    
  484.     case 'action':
    
  485.     case 'formAction': {
    
  486.       // TODO: Consider moving these special cases to the form, input and button tags.
    
  487.       if (__DEV__) {
    
  488.         validateFormActionInDevelopment(tag, key, value, props);
    
  489.       }
    
  490.       if (enableFormActions) {
    
  491.         if (typeof value === 'function') {
    
  492.           // Set a javascript URL that doesn't do anything. We don't expect this to be invoked
    
  493.           // because we'll preventDefault, but it can happen if a form is manually submitted or
    
  494.           // if someone calls stopPropagation before React gets the event.
    
  495.           // If CSP is used to block javascript: URLs that's fine too. It just won't show this
    
  496.           // error message but the URL will be logged.
    
  497.           domElement.setAttribute(
    
  498.             key,
    
  499.             // eslint-disable-next-line no-script-url
    
  500.             "javascript:throw new Error('" +
    
  501.               'A React form was unexpectedly submitted. If you called form.submit() manually, ' +
    
  502.               "consider using form.requestSubmit() instead. If you\\'re trying to use " +
    
  503.               'event.stopPropagation() in a submit event handler, consider also calling ' +
    
  504.               'event.preventDefault().' +
    
  505.               "')",
    
  506.           );
    
  507.           break;
    
  508.         } else if (typeof prevValue === 'function') {
    
  509.           // When we're switching off a Server Action that was originally hydrated.
    
  510.           // The server control these fields during SSR that are now trailing.
    
  511.           // The regular diffing doesn't apply since we compare against the previous props.
    
  512.           // Instead, we need to force them to be set to whatever they should be now.
    
  513.           // This would be a lot cleaner if we did this whole fork in the per-tag approach.
    
  514.           if (key === 'formAction') {
    
  515.             if (tag !== 'input') {
    
  516.               // Setting the name here isn't completely safe for inputs if this is switching
    
  517.               // to become a radio button. In that case we let the tag based override take
    
  518.               // control.
    
  519.               setProp(domElement, tag, 'name', props.name, props, null);
    
  520.             }
    
  521.             setProp(
    
  522.               domElement,
    
  523.               tag,
    
  524.               'formEncType',
    
  525.               props.formEncType,
    
  526.               props,
    
  527.               null,
    
  528.             );
    
  529.             setProp(
    
  530.               domElement,
    
  531.               tag,
    
  532.               'formMethod',
    
  533.               props.formMethod,
    
  534.               props,
    
  535.               null,
    
  536.             );
    
  537.             setProp(
    
  538.               domElement,
    
  539.               tag,
    
  540.               'formTarget',
    
  541.               props.formTarget,
    
  542.               props,
    
  543.               null,
    
  544.             );
    
  545.           } else {
    
  546.             setProp(domElement, tag, 'encType', props.encType, props, null);
    
  547.             setProp(domElement, tag, 'method', props.method, props, null);
    
  548.             setProp(domElement, tag, 'target', props.target, props, null);
    
  549.           }
    
  550.         }
    
  551.       }
    
  552.       if (
    
  553.         value == null ||
    
  554.         (!enableFormActions && typeof value === 'function') ||
    
  555.         typeof value === 'symbol' ||
    
  556.         typeof value === 'boolean'
    
  557.       ) {
    
  558.         domElement.removeAttribute(key);
    
  559.         break;
    
  560.       }
    
  561.       // `setAttribute` with objects becomes only `[object]` in IE8/9,
    
  562.       // ('' + value) makes it output the correct toString()-value.
    
  563.       if (__DEV__) {
    
  564.         checkAttributeStringCoercion(value, key);
    
  565.       }
    
  566.       const sanitizedValue = (sanitizeURL(
    
  567.         enableTrustedTypesIntegration ? value : '' + (value: any),
    
  568.       ): any);
    
  569.       domElement.setAttribute(key, sanitizedValue);
    
  570.       break;
    
  571.     }
    
  572.     case 'onClick': {
    
  573.       // TODO: This cast may not be sound for SVG, MathML or custom elements.
    
  574.       if (value != null) {
    
  575.         if (__DEV__ && typeof value !== 'function') {
    
  576.           warnForInvalidEventListener(key, value);
    
  577.         }
    
  578.         trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
    
  579.       }
    
  580.       break;
    
  581.     }
    
  582.     case 'onScroll': {
    
  583.       if (value != null) {
    
  584.         if (__DEV__ && typeof value !== 'function') {
    
  585.           warnForInvalidEventListener(key, value);
    
  586.         }
    
  587.         listenToNonDelegatedEvent('scroll', domElement);
    
  588.       }
    
  589.       break;
    
  590.     }
    
  591.     case 'onScrollEnd': {
    
  592.       if (value != null) {
    
  593.         if (__DEV__ && typeof value !== 'function') {
    
  594.           warnForInvalidEventListener(key, value);
    
  595.         }
    
  596.         listenToNonDelegatedEvent('scrollend', domElement);
    
  597.       }
    
  598.       break;
    
  599.     }
    
  600.     case 'dangerouslySetInnerHTML': {
    
  601.       if (value != null) {
    
  602.         if (typeof value !== 'object' || !('__html' in value)) {
    
  603.           throw new Error(
    
  604.             '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
    
  605.               'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
    
  606.               'for more information.',
    
  607.           );
    
  608.         }
    
  609.         const nextHtml: any = value.__html;
    
  610.         if (nextHtml != null) {
    
  611.           if (props.children != null) {
    
  612.             throw new Error(
    
  613.               'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
    
  614.             );
    
  615.           }
    
  616.           if (disableIEWorkarounds) {
    
  617.             domElement.innerHTML = nextHtml;
    
  618.           } else {
    
  619.             setInnerHTML(domElement, nextHtml);
    
  620.           }
    
  621.         }
    
  622.       }
    
  623.       break;
    
  624.     }
    
  625.     // Note: `option.selected` is not updated if `select.multiple` is
    
  626.     // disabled with `removeAttribute`. We have special logic for handling this.
    
  627.     case 'multiple': {
    
  628.       (domElement: any).multiple =
    
  629.         value && typeof value !== 'function' && typeof value !== 'symbol';
    
  630.       break;
    
  631.     }
    
  632.     case 'muted': {
    
  633.       (domElement: any).muted =
    
  634.         value && typeof value !== 'function' && typeof value !== 'symbol';
    
  635.       break;
    
  636.     }
    
  637.     case 'suppressContentEditableWarning':
    
  638.     case 'suppressHydrationWarning':
    
  639.     case 'defaultValue': // Reserved
    
  640.     case 'defaultChecked':
    
  641.     case 'innerHTML': {
    
  642.       // Noop
    
  643.       break;
    
  644.     }
    
  645.     case 'autoFocus': {
    
  646.       // We polyfill it separately on the client during commit.
    
  647.       // We could have excluded it in the property list instead of
    
  648.       // adding a special case here, but then it wouldn't be emitted
    
  649.       // on server rendering (but we *do* want to emit it in SSR).
    
  650.       break;
    
  651.     }
    
  652.     case 'xlinkHref': {
    
  653.       if (
    
  654.         value == null ||
    
  655.         typeof value === 'function' ||
    
  656.         typeof value === 'boolean' ||
    
  657.         typeof value === 'symbol'
    
  658.       ) {
    
  659.         domElement.removeAttribute('xlink:href');
    
  660.         break;
    
  661.       }
    
  662.       // `setAttribute` with objects becomes only `[object]` in IE8/9,
    
  663.       // ('' + value) makes it output the correct toString()-value.
    
  664.       if (__DEV__) {
    
  665.         checkAttributeStringCoercion(value, key);
    
  666.       }
    
  667.       const sanitizedValue = (sanitizeURL(
    
  668.         enableTrustedTypesIntegration ? value : '' + (value: any),
    
  669.       ): any);
    
  670.       domElement.setAttributeNS(xlinkNamespace, 'xlink:href', sanitizedValue);
    
  671.       break;
    
  672.     }
    
  673.     case 'contentEditable':
    
  674.     case 'spellCheck':
    
  675.     case 'draggable':
    
  676.     case 'value':
    
  677.     case 'autoReverse':
    
  678.     case 'externalResourcesRequired':
    
  679.     case 'focusable':
    
  680.     case 'preserveAlpha': {
    
  681.       // Booleanish String
    
  682.       // These are "enumerated" attributes that accept "true" and "false".
    
  683.       // In React, we let users pass `true` and `false` even though technically
    
  684.       // these aren't boolean attributes (they are coerced to strings).
    
  685.       // The SVG attributes are case-sensitive. Since the HTML attributes are
    
  686.       // insensitive they also work even though we canonically use lower case.
    
  687.       if (
    
  688.         value != null &&
    
  689.         typeof value !== 'function' &&
    
  690.         typeof value !== 'symbol'
    
  691.       ) {
    
  692.         if (__DEV__) {
    
  693.           checkAttributeStringCoercion(value, key);
    
  694.         }
    
  695.         domElement.setAttribute(
    
  696.           key,
    
  697.           enableTrustedTypesIntegration ? (value: any) : '' + (value: any),
    
  698.         );
    
  699.       } else {
    
  700.         domElement.removeAttribute(key);
    
  701.       }
    
  702.       break;
    
  703.     }
    
  704.     // Boolean
    
  705.     case 'allowFullScreen':
    
  706.     case 'async':
    
  707.     case 'autoPlay':
    
  708.     case 'controls':
    
  709.     case 'default':
    
  710.     case 'defer':
    
  711.     case 'disabled':
    
  712.     case 'disablePictureInPicture':
    
  713.     case 'disableRemotePlayback':
    
  714.     case 'formNoValidate':
    
  715.     case 'hidden':
    
  716.     case 'loop':
    
  717.     case 'noModule':
    
  718.     case 'noValidate':
    
  719.     case 'open':
    
  720.     case 'playsInline':
    
  721.     case 'readOnly':
    
  722.     case 'required':
    
  723.     case 'reversed':
    
  724.     case 'scoped':
    
  725.     case 'seamless':
    
  726.     case 'itemScope': {
    
  727.       if (value && typeof value !== 'function' && typeof value !== 'symbol') {
    
  728.         domElement.setAttribute(key, '');
    
  729.       } else {
    
  730.         domElement.removeAttribute(key);
    
  731.       }
    
  732.       break;
    
  733.     }
    
  734.     // Overloaded Boolean
    
  735.     case 'capture':
    
  736.     case 'download': {
    
  737.       // An attribute that can be used as a flag as well as with a value.
    
  738.       // When true, it should be present (set either to an empty string or its name).
    
  739.       // When false, it should be omitted.
    
  740.       // For any other value, should be present with that value.
    
  741.       if (value === true) {
    
  742.         domElement.setAttribute(key, '');
    
  743.       } else if (
    
  744.         value !== false &&
    
  745.         value != null &&
    
  746.         typeof value !== 'function' &&
    
  747.         typeof value !== 'symbol'
    
  748.       ) {
    
  749.         if (__DEV__) {
    
  750.           checkAttributeStringCoercion(value, key);
    
  751.         }
    
  752.         domElement.setAttribute(key, (value: any));
    
  753.       } else {
    
  754.         domElement.removeAttribute(key);
    
  755.       }
    
  756.       break;
    
  757.     }
    
  758.     case 'cols':
    
  759.     case 'rows':
    
  760.     case 'size':
    
  761.     case 'span': {
    
  762.       // These are HTML attributes that must be positive numbers.
    
  763.       if (
    
  764.         value != null &&
    
  765.         typeof value !== 'function' &&
    
  766.         typeof value !== 'symbol' &&
    
  767.         !isNaN(value) &&
    
  768.         (value: any) >= 1
    
  769.       ) {
    
  770.         if (__DEV__) {
    
  771.           checkAttributeStringCoercion(value, key);
    
  772.         }
    
  773.         domElement.setAttribute(key, (value: any));
    
  774.       } else {
    
  775.         domElement.removeAttribute(key);
    
  776.       }
    
  777.       break;
    
  778.     }
    
  779.     case 'rowSpan':
    
  780.     case 'start': {
    
  781.       // These are HTML attributes that must be numbers.
    
  782.       if (
    
  783.         value != null &&
    
  784.         typeof value !== 'function' &&
    
  785.         typeof value !== 'symbol' &&
    
  786.         !isNaN(value)
    
  787.       ) {
    
  788.         if (__DEV__) {
    
  789.           checkAttributeStringCoercion(value, key);
    
  790.         }
    
  791.         domElement.setAttribute(key, (value: any));
    
  792.       } else {
    
  793.         domElement.removeAttribute(key);
    
  794.       }
    
  795.       break;
    
  796.     }
    
  797.     case 'xlinkActuate':
    
  798.       setValueForNamespacedAttribute(
    
  799.         domElement,
    
  800.         xlinkNamespace,
    
  801.         'xlink:actuate',
    
  802.         value,
    
  803.       );
    
  804.       break;
    
  805.     case 'xlinkArcrole':
    
  806.       setValueForNamespacedAttribute(
    
  807.         domElement,
    
  808.         xlinkNamespace,
    
  809.         'xlink:arcrole',
    
  810.         value,
    
  811.       );
    
  812.       break;
    
  813.     case 'xlinkRole':
    
  814.       setValueForNamespacedAttribute(
    
  815.         domElement,
    
  816.         xlinkNamespace,
    
  817.         'xlink:role',
    
  818.         value,
    
  819.       );
    
  820.       break;
    
  821.     case 'xlinkShow':
    
  822.       setValueForNamespacedAttribute(
    
  823.         domElement,
    
  824.         xlinkNamespace,
    
  825.         'xlink:show',
    
  826.         value,
    
  827.       );
    
  828.       break;
    
  829.     case 'xlinkTitle':
    
  830.       setValueForNamespacedAttribute(
    
  831.         domElement,
    
  832.         xlinkNamespace,
    
  833.         'xlink:title',
    
  834.         value,
    
  835.       );
    
  836.       break;
    
  837.     case 'xlinkType':
    
  838.       setValueForNamespacedAttribute(
    
  839.         domElement,
    
  840.         xlinkNamespace,
    
  841.         'xlink:type',
    
  842.         value,
    
  843.       );
    
  844.       break;
    
  845.     case 'xmlBase':
    
  846.       setValueForNamespacedAttribute(
    
  847.         domElement,
    
  848.         xmlNamespace,
    
  849.         'xml:base',
    
  850.         value,
    
  851.       );
    
  852.       break;
    
  853.     case 'xmlLang':
    
  854.       setValueForNamespacedAttribute(
    
  855.         domElement,
    
  856.         xmlNamespace,
    
  857.         'xml:lang',
    
  858.         value,
    
  859.       );
    
  860.       break;
    
  861.     case 'xmlSpace':
    
  862.       setValueForNamespacedAttribute(
    
  863.         domElement,
    
  864.         xmlNamespace,
    
  865.         'xml:space',
    
  866.         value,
    
  867.       );
    
  868.       break;
    
  869.     // Properties that should not be allowed on custom elements.
    
  870.     case 'is': {
    
  871.       if (__DEV__) {
    
  872.         if (prevValue != null) {
    
  873.           console.error(
    
  874.             'Cannot update the "is" prop after it has been initialized.',
    
  875.           );
    
  876.         }
    
  877.       }
    
  878.       // TODO: We shouldn't actually set this attribute, because we've already
    
  879.       // passed it to createElement. We don't also need the attribute.
    
  880.       // However, our tests currently query for it so it's plausible someone
    
  881.       // else does too so it's break.
    
  882.       setValueForAttribute(domElement, 'is', value);
    
  883.       break;
    
  884.     }
    
  885.     case 'innerText':
    
  886.     case 'textContent':
    
  887.       if (enableCustomElementPropertySupport) {
    
  888.         break;
    
  889.       }
    
  890.     // Fall through
    
  891.     default: {
    
  892.       if (
    
  893.         key.length > 2 &&
    
  894.         (key[0] === 'o' || key[0] === 'O') &&
    
  895.         (key[1] === 'n' || key[1] === 'N')
    
  896.       ) {
    
  897.         if (
    
  898.           __DEV__ &&
    
  899.           registrationNameDependencies.hasOwnProperty(key) &&
    
  900.           value != null &&
    
  901.           typeof value !== 'function'
    
  902.         ) {
    
  903.           warnForInvalidEventListener(key, value);
    
  904.         }
    
  905.       } else {
    
  906.         const attributeName = getAttributeAlias(key);
    
  907.         setValueForAttribute(domElement, attributeName, value);
    
  908.       }
    
  909.     }
    
  910.   }
    
  911. }
    
  912. 
    
  913. function setPropOnCustomElement(
    
  914.   domElement: Element,
    
  915.   tag: string,
    
  916.   key: string,
    
  917.   value: mixed,
    
  918.   props: any,
    
  919.   prevValue: mixed,
    
  920. ): void {
    
  921.   switch (key) {
    
  922.     case 'style': {
    
  923.       setValueForStyles(domElement, value, prevValue);
    
  924.       break;
    
  925.     }
    
  926.     case 'dangerouslySetInnerHTML': {
    
  927.       if (value != null) {
    
  928.         if (typeof value !== 'object' || !('__html' in value)) {
    
  929.           throw new Error(
    
  930.             '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
    
  931.               'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
    
  932.               'for more information.',
    
  933.           );
    
  934.         }
    
  935.         const nextHtml: any = value.__html;
    
  936.         if (nextHtml != null) {
    
  937.           if (props.children != null) {
    
  938.             throw new Error(
    
  939.               'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
    
  940.             );
    
  941.           }
    
  942.           if (disableIEWorkarounds) {
    
  943.             domElement.innerHTML = nextHtml;
    
  944.           } else {
    
  945.             setInnerHTML(domElement, nextHtml);
    
  946.           }
    
  947.         }
    
  948.       }
    
  949.       break;
    
  950.     }
    
  951.     case 'children': {
    
  952.       if (typeof value === 'string') {
    
  953.         setTextContent(domElement, value);
    
  954.       } else if (typeof value === 'number') {
    
  955.         setTextContent(domElement, '' + value);
    
  956.       }
    
  957.       break;
    
  958.     }
    
  959.     case 'onScroll': {
    
  960.       if (value != null) {
    
  961.         if (__DEV__ && typeof value !== 'function') {
    
  962.           warnForInvalidEventListener(key, value);
    
  963.         }
    
  964.         listenToNonDelegatedEvent('scroll', domElement);
    
  965.       }
    
  966.       break;
    
  967.     }
    
  968.     case 'onScrollEnd': {
    
  969.       if (value != null) {
    
  970.         if (__DEV__ && typeof value !== 'function') {
    
  971.           warnForInvalidEventListener(key, value);
    
  972.         }
    
  973.         listenToNonDelegatedEvent('scrollend', domElement);
    
  974.       }
    
  975.       break;
    
  976.     }
    
  977.     case 'onClick': {
    
  978.       // TODO: This cast may not be sound for SVG, MathML or custom elements.
    
  979.       if (value != null) {
    
  980.         if (__DEV__ && typeof value !== 'function') {
    
  981.           warnForInvalidEventListener(key, value);
    
  982.         }
    
  983.         trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
    
  984.       }
    
  985.       break;
    
  986.     }
    
  987.     case 'suppressContentEditableWarning':
    
  988.     case 'suppressHydrationWarning':
    
  989.     case 'innerHTML': {
    
  990.       // Noop
    
  991.       break;
    
  992.     }
    
  993.     case 'innerText': // Properties
    
  994.     case 'textContent':
    
  995.       if (enableCustomElementPropertySupport) {
    
  996.         break;
    
  997.       }
    
  998.     // Fall through
    
  999.     default: {
    
  1000.       if (registrationNameDependencies.hasOwnProperty(key)) {
    
  1001.         if (__DEV__ && value != null && typeof value !== 'function') {
    
  1002.           warnForInvalidEventListener(key, value);
    
  1003.         }
    
  1004.       } else {
    
  1005.         if (enableCustomElementPropertySupport) {
    
  1006.           setValueForPropertyOnCustomComponent(domElement, key, value);
    
  1007.         } else {
    
  1008.           if (typeof value === 'boolean') {
    
  1009.             // Special case before the new flag is on
    
  1010.             value = '' + (value: any);
    
  1011.           }
    
  1012.           setValueForAttribute(domElement, key, value);
    
  1013.         }
    
  1014.       }
    
  1015.     }
    
  1016.   }
    
  1017. }
    
  1018. 
    
  1019. export function setInitialProperties(
    
  1020.   domElement: Element,
    
  1021.   tag: string,
    
  1022.   props: Object,
    
  1023. ): void {
    
  1024.   if (__DEV__) {
    
  1025.     validatePropertiesInDevelopment(tag, props);
    
  1026.   }
    
  1027. 
    
  1028.   // TODO: Make sure that we check isMounted before firing any of these events.
    
  1029. 
    
  1030.   switch (tag) {
    
  1031.     case 'div':
    
  1032.     case 'span':
    
  1033.     case 'svg':
    
  1034.     case 'path':
    
  1035.     case 'a':
    
  1036.     case 'g':
    
  1037.     case 'p':
    
  1038.     case 'li': {
    
  1039.       // Fast track the most common tag types
    
  1040.       break;
    
  1041.     }
    
  1042.     case 'input': {
    
  1043.       if (__DEV__) {
    
  1044.         checkControlledValueProps('input', props);
    
  1045.       }
    
  1046.       // We listen to this event in case to ensure emulated bubble
    
  1047.       // listeners still fire for the invalid event.
    
  1048.       listenToNonDelegatedEvent('invalid', domElement);
    
  1049. 
    
  1050.       let name = null;
    
  1051.       let type = null;
    
  1052.       let value = null;
    
  1053.       let defaultValue = null;
    
  1054.       let checked = null;
    
  1055.       let defaultChecked = null;
    
  1056.       for (const propKey in props) {
    
  1057.         if (!props.hasOwnProperty(propKey)) {
    
  1058.           continue;
    
  1059.         }
    
  1060.         const propValue = props[propKey];
    
  1061.         if (propValue == null) {
    
  1062.           continue;
    
  1063.         }
    
  1064.         switch (propKey) {
    
  1065.           case 'name': {
    
  1066.             name = propValue;
    
  1067.             break;
    
  1068.           }
    
  1069.           case 'type': {
    
  1070.             type = propValue;
    
  1071.             break;
    
  1072.           }
    
  1073.           case 'checked': {
    
  1074.             checked = propValue;
    
  1075.             break;
    
  1076.           }
    
  1077.           case 'defaultChecked': {
    
  1078.             defaultChecked = propValue;
    
  1079.             break;
    
  1080.           }
    
  1081.           case 'value': {
    
  1082.             value = propValue;
    
  1083.             break;
    
  1084.           }
    
  1085.           case 'defaultValue': {
    
  1086.             defaultValue = propValue;
    
  1087.             break;
    
  1088.           }
    
  1089.           case 'children':
    
  1090.           case 'dangerouslySetInnerHTML': {
    
  1091.             if (propValue != null) {
    
  1092.               throw new Error(
    
  1093.                 `${tag} is a void element tag and must neither have \`children\` nor ` +
    
  1094.                   'use `dangerouslySetInnerHTML`.',
    
  1095.               );
    
  1096.             }
    
  1097.             break;
    
  1098.           }
    
  1099.           default: {
    
  1100.             setProp(domElement, tag, propKey, propValue, props, null);
    
  1101.           }
    
  1102.         }
    
  1103.       }
    
  1104.       // TODO: Make sure we check if this is still unmounted or do any clean
    
  1105.       // up necessary since we never stop tracking anymore.
    
  1106.       validateInputProps(domElement, props);
    
  1107.       initInput(
    
  1108.         domElement,
    
  1109.         value,
    
  1110.         defaultValue,
    
  1111.         checked,
    
  1112.         defaultChecked,
    
  1113.         type,
    
  1114.         name,
    
  1115.         false,
    
  1116.       );
    
  1117.       track((domElement: any));
    
  1118.       return;
    
  1119.     }
    
  1120.     case 'select': {
    
  1121.       if (__DEV__) {
    
  1122.         checkControlledValueProps('select', props);
    
  1123.       }
    
  1124.       // We listen to this event in case to ensure emulated bubble
    
  1125.       // listeners still fire for the invalid event.
    
  1126.       listenToNonDelegatedEvent('invalid', domElement);
    
  1127.       let value = null;
    
  1128.       let defaultValue = null;
    
  1129.       let multiple = null;
    
  1130.       for (const propKey in props) {
    
  1131.         if (!props.hasOwnProperty(propKey)) {
    
  1132.           continue;
    
  1133.         }
    
  1134.         const propValue = props[propKey];
    
  1135.         if (propValue == null) {
    
  1136.           continue;
    
  1137.         }
    
  1138.         switch (propKey) {
    
  1139.           case 'value': {
    
  1140.             value = propValue;
    
  1141.             // This is handled by initSelect below.
    
  1142.             break;
    
  1143.           }
    
  1144.           case 'defaultValue': {
    
  1145.             defaultValue = propValue;
    
  1146.             // This is handled by initSelect below.
    
  1147.             break;
    
  1148.           }
    
  1149.           case 'multiple': {
    
  1150.             multiple = propValue;
    
  1151.             // TODO: We don't actually have to fall through here because we set it
    
  1152.             // in initSelect anyway. We can remove the special case in setProp.
    
  1153.           }
    
  1154.           // Fallthrough
    
  1155.           default: {
    
  1156.             setProp(domElement, tag, propKey, propValue, props, null);
    
  1157.           }
    
  1158.         }
    
  1159.       }
    
  1160.       validateSelectProps(domElement, props);
    
  1161.       initSelect(domElement, value, defaultValue, multiple);
    
  1162.       return;
    
  1163.     }
    
  1164.     case 'textarea': {
    
  1165.       if (__DEV__) {
    
  1166.         checkControlledValueProps('textarea', props);
    
  1167.       }
    
  1168.       // We listen to this event in case to ensure emulated bubble
    
  1169.       // listeners still fire for the invalid event.
    
  1170.       listenToNonDelegatedEvent('invalid', domElement);
    
  1171.       let value = null;
    
  1172.       let defaultValue = null;
    
  1173.       let children = null;
    
  1174.       for (const propKey in props) {
    
  1175.         if (!props.hasOwnProperty(propKey)) {
    
  1176.           continue;
    
  1177.         }
    
  1178.         const propValue = props[propKey];
    
  1179.         if (propValue == null) {
    
  1180.           continue;
    
  1181.         }
    
  1182.         switch (propKey) {
    
  1183.           case 'value': {
    
  1184.             value = propValue;
    
  1185.             // This is handled by initTextarea below.
    
  1186.             break;
    
  1187.           }
    
  1188.           case 'defaultValue': {
    
  1189.             defaultValue = propValue;
    
  1190.             break;
    
  1191.           }
    
  1192.           case 'children': {
    
  1193.             children = propValue;
    
  1194.             // Handled by initTextarea above.
    
  1195.             break;
    
  1196.           }
    
  1197.           case 'dangerouslySetInnerHTML': {
    
  1198.             if (propValue != null) {
    
  1199.               // TODO: Do we really need a special error message for this. It's also pretty blunt.
    
  1200.               throw new Error(
    
  1201.                 '`dangerouslySetInnerHTML` does not make sense on <textarea>.',
    
  1202.               );
    
  1203.             }
    
  1204.             break;
    
  1205.           }
    
  1206.           default: {
    
  1207.             setProp(domElement, tag, propKey, propValue, props, null);
    
  1208.           }
    
  1209.         }
    
  1210.       }
    
  1211.       // TODO: Make sure we check if this is still unmounted or do any clean
    
  1212.       // up necessary since we never stop tracking anymore.
    
  1213.       validateTextareaProps(domElement, props);
    
  1214.       initTextarea(domElement, value, defaultValue, children);
    
  1215.       track((domElement: any));
    
  1216.       return;
    
  1217.     }
    
  1218.     case 'option': {
    
  1219.       validateOptionProps(domElement, props);
    
  1220.       for (const propKey in props) {
    
  1221.         if (!props.hasOwnProperty(propKey)) {
    
  1222.           continue;
    
  1223.         }
    
  1224.         const propValue = props[propKey];
    
  1225.         if (propValue == null) {
    
  1226.           continue;
    
  1227.         }
    
  1228.         switch (propKey) {
    
  1229.           case 'selected': {
    
  1230.             // TODO: Remove support for selected on option.
    
  1231.             (domElement: any).selected =
    
  1232.               propValue &&
    
  1233.               typeof propValue !== 'function' &&
    
  1234.               typeof propValue !== 'symbol';
    
  1235.             break;
    
  1236.           }
    
  1237.           default: {
    
  1238.             setProp(domElement, tag, propKey, propValue, props, null);
    
  1239.           }
    
  1240.         }
    
  1241.       }
    
  1242.       return;
    
  1243.     }
    
  1244.     case 'dialog': {
    
  1245.       listenToNonDelegatedEvent('cancel', domElement);
    
  1246.       listenToNonDelegatedEvent('close', domElement);
    
  1247.       break;
    
  1248.     }
    
  1249.     case 'iframe':
    
  1250.     case 'object': {
    
  1251.       // We listen to this event in case to ensure emulated bubble
    
  1252.       // listeners still fire for the load event.
    
  1253.       listenToNonDelegatedEvent('load', domElement);
    
  1254.       break;
    
  1255.     }
    
  1256.     case 'video':
    
  1257.     case 'audio': {
    
  1258.       // We listen to these events in case to ensure emulated bubble
    
  1259.       // listeners still fire for all the media events.
    
  1260.       for (let i = 0; i < mediaEventTypes.length; i++) {
    
  1261.         listenToNonDelegatedEvent(mediaEventTypes[i], domElement);
    
  1262.       }
    
  1263.       break;
    
  1264.     }
    
  1265.     case 'image': {
    
  1266.       // We listen to these events in case to ensure emulated bubble
    
  1267.       // listeners still fire for error and load events.
    
  1268.       listenToNonDelegatedEvent('error', domElement);
    
  1269.       listenToNonDelegatedEvent('load', domElement);
    
  1270.       break;
    
  1271.     }
    
  1272.     case 'details': {
    
  1273.       // We listen to this event in case to ensure emulated bubble
    
  1274.       // listeners still fire for the toggle event.
    
  1275.       listenToNonDelegatedEvent('toggle', domElement);
    
  1276.       break;
    
  1277.     }
    
  1278.     case 'embed':
    
  1279.     case 'source':
    
  1280.     case 'img':
    
  1281.     case 'link': {
    
  1282.       // These are void elements that also need delegated events.
    
  1283.       listenToNonDelegatedEvent('error', domElement);
    
  1284.       listenToNonDelegatedEvent('load', domElement);
    
  1285.       // We fallthrough to the return of the void elements
    
  1286.     }
    
  1287.     case 'area':
    
  1288.     case 'base':
    
  1289.     case 'br':
    
  1290.     case 'col':
    
  1291.     case 'hr':
    
  1292.     case 'keygen':
    
  1293.     case 'meta':
    
  1294.     case 'param':
    
  1295.     case 'track':
    
  1296.     case 'wbr':
    
  1297.     case 'menuitem': {
    
  1298.       // Void elements
    
  1299.       for (const propKey in props) {
    
  1300.         if (!props.hasOwnProperty(propKey)) {
    
  1301.           continue;
    
  1302.         }
    
  1303.         const propValue = props[propKey];
    
  1304.         if (propValue == null) {
    
  1305.           continue;
    
  1306.         }
    
  1307.         switch (propKey) {
    
  1308.           case 'children':
    
  1309.           case 'dangerouslySetInnerHTML': {
    
  1310.             // TODO: Can we make this a DEV warning to avoid this deny list?
    
  1311.             throw new Error(
    
  1312.               `${tag} is a void element tag and must neither have \`children\` nor ` +
    
  1313.                 'use `dangerouslySetInnerHTML`.',
    
  1314.             );
    
  1315.           }
    
  1316.           // defaultChecked and defaultValue are ignored by setProp
    
  1317.           default: {
    
  1318.             setProp(domElement, tag, propKey, propValue, props, null);
    
  1319.           }
    
  1320.         }
    
  1321.       }
    
  1322.       return;
    
  1323.     }
    
  1324.     default: {
    
  1325.       if (isCustomElement(tag, props)) {
    
  1326.         for (const propKey in props) {
    
  1327.           if (!props.hasOwnProperty(propKey)) {
    
  1328.             continue;
    
  1329.           }
    
  1330.           const propValue = props[propKey];
    
  1331.           if (propValue == null) {
    
  1332.             continue;
    
  1333.           }
    
  1334.           setPropOnCustomElement(
    
  1335.             domElement,
    
  1336.             tag,
    
  1337.             propKey,
    
  1338.             propValue,
    
  1339.             props,
    
  1340.             null,
    
  1341.           );
    
  1342.         }
    
  1343.         return;
    
  1344.       }
    
  1345.     }
    
  1346.   }
    
  1347. 
    
  1348.   for (const propKey in props) {
    
  1349.     if (!props.hasOwnProperty(propKey)) {
    
  1350.       continue;
    
  1351.     }
    
  1352.     const propValue = props[propKey];
    
  1353.     if (propValue == null) {
    
  1354.       continue;
    
  1355.     }
    
  1356.     setProp(domElement, tag, propKey, propValue, props, null);
    
  1357.   }
    
  1358. }
    
  1359. 
    
  1360. export function updateProperties(
    
  1361.   domElement: Element,
    
  1362.   tag: string,
    
  1363.   lastProps: Object,
    
  1364.   nextProps: Object,
    
  1365. ): void {
    
  1366.   if (__DEV__) {
    
  1367.     validatePropertiesInDevelopment(tag, nextProps);
    
  1368.   }
    
  1369. 
    
  1370.   switch (tag) {
    
  1371.     case 'div':
    
  1372.     case 'span':
    
  1373.     case 'svg':
    
  1374.     case 'path':
    
  1375.     case 'a':
    
  1376.     case 'g':
    
  1377.     case 'p':
    
  1378.     case 'li': {
    
  1379.       // Fast track the most common tag types
    
  1380.       break;
    
  1381.     }
    
  1382.     case 'input': {
    
  1383.       let name = null;
    
  1384.       let type = null;
    
  1385.       let value = null;
    
  1386.       let defaultValue = null;
    
  1387.       let lastDefaultValue = null;
    
  1388.       let checked = null;
    
  1389.       let defaultChecked = null;
    
  1390.       for (const propKey in lastProps) {
    
  1391.         const lastProp = lastProps[propKey];
    
  1392.         if (lastProps.hasOwnProperty(propKey) && lastProp != null) {
    
  1393.           switch (propKey) {
    
  1394.             case 'checked': {
    
  1395.               break;
    
  1396.             }
    
  1397.             case 'value': {
    
  1398.               // This is handled by updateWrapper below.
    
  1399.               break;
    
  1400.             }
    
  1401.             case 'defaultValue': {
    
  1402.               lastDefaultValue = lastProp;
    
  1403.             }
    
  1404.             // defaultChecked and defaultValue are ignored by setProp
    
  1405.             // Fallthrough
    
  1406.             default: {
    
  1407.               if (!nextProps.hasOwnProperty(propKey))
    
  1408.                 setProp(domElement, tag, propKey, null, nextProps, lastProp);
    
  1409.             }
    
  1410.           }
    
  1411.         }
    
  1412.       }
    
  1413.       for (const propKey in nextProps) {
    
  1414.         const nextProp = nextProps[propKey];
    
  1415.         const lastProp = lastProps[propKey];
    
  1416.         if (
    
  1417.           nextProps.hasOwnProperty(propKey) &&
    
  1418.           (nextProp != null || lastProp != null)
    
  1419.         ) {
    
  1420.           switch (propKey) {
    
  1421.             case 'type': {
    
  1422.               type = nextProp;
    
  1423.               break;
    
  1424.             }
    
  1425.             case 'name': {
    
  1426.               name = nextProp;
    
  1427.               break;
    
  1428.             }
    
  1429.             case 'checked': {
    
  1430.               checked = nextProp;
    
  1431.               break;
    
  1432.             }
    
  1433.             case 'defaultChecked': {
    
  1434.               defaultChecked = nextProp;
    
  1435.               break;
    
  1436.             }
    
  1437.             case 'value': {
    
  1438.               value = nextProp;
    
  1439.               break;
    
  1440.             }
    
  1441.             case 'defaultValue': {
    
  1442.               defaultValue = nextProp;
    
  1443.               break;
    
  1444.             }
    
  1445.             case 'children':
    
  1446.             case 'dangerouslySetInnerHTML': {
    
  1447.               if (nextProp != null) {
    
  1448.                 throw new Error(
    
  1449.                   `${tag} is a void element tag and must neither have \`children\` nor ` +
    
  1450.                     'use `dangerouslySetInnerHTML`.',
    
  1451.                 );
    
  1452.               }
    
  1453.               break;
    
  1454.             }
    
  1455.             default: {
    
  1456.               if (nextProp !== lastProp)
    
  1457.                 setProp(
    
  1458.                   domElement,
    
  1459.                   tag,
    
  1460.                   propKey,
    
  1461.                   nextProp,
    
  1462.                   nextProps,
    
  1463.                   lastProp,
    
  1464.                 );
    
  1465.             }
    
  1466.           }
    
  1467.         }
    
  1468.       }
    
  1469. 
    
  1470.       if (__DEV__) {
    
  1471.         const wasControlled =
    
  1472.           lastProps.type === 'checkbox' || lastProps.type === 'radio'
    
  1473.             ? lastProps.checked != null
    
  1474.             : lastProps.value != null;
    
  1475.         const isControlled =
    
  1476.           nextProps.type === 'checkbox' || nextProps.type === 'radio'
    
  1477.             ? nextProps.checked != null
    
  1478.             : nextProps.value != null;
    
  1479. 
    
  1480.         if (
    
  1481.           !wasControlled &&
    
  1482.           isControlled &&
    
  1483.           !didWarnUncontrolledToControlled
    
  1484.         ) {
    
  1485.           console.error(
    
  1486.             'A component is changing an uncontrolled input to be controlled. ' +
    
  1487.               'This is likely caused by the value changing from undefined to ' +
    
  1488.               'a defined value, which should not happen. ' +
    
  1489.               'Decide between using a controlled or uncontrolled input ' +
    
  1490.               'element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components',
    
  1491.           );
    
  1492.           didWarnUncontrolledToControlled = true;
    
  1493.         }
    
  1494.         if (
    
  1495.           wasControlled &&
    
  1496.           !isControlled &&
    
  1497.           !didWarnControlledToUncontrolled
    
  1498.         ) {
    
  1499.           console.error(
    
  1500.             'A component is changing a controlled input to be uncontrolled. ' +
    
  1501.               'This is likely caused by the value changing from a defined to ' +
    
  1502.               'undefined, which should not happen. ' +
    
  1503.               'Decide between using a controlled or uncontrolled input ' +
    
  1504.               'element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components',
    
  1505.           );
    
  1506.           didWarnControlledToUncontrolled = true;
    
  1507.         }
    
  1508.       }
    
  1509. 
    
  1510.       // Update the wrapper around inputs *after* updating props. This has to
    
  1511.       // happen after updating the rest of props. Otherwise HTML5 input validations
    
  1512.       // raise warnings and prevent the new value from being assigned.
    
  1513.       updateInput(
    
  1514.         domElement,
    
  1515.         value,
    
  1516.         defaultValue,
    
  1517.         lastDefaultValue,
    
  1518.         checked,
    
  1519.         defaultChecked,
    
  1520.         type,
    
  1521.         name,
    
  1522.       );
    
  1523.       return;
    
  1524.     }
    
  1525.     case 'select': {
    
  1526.       let value = null;
    
  1527.       let defaultValue = null;
    
  1528.       let multiple = null;
    
  1529.       let wasMultiple = null;
    
  1530.       for (const propKey in lastProps) {
    
  1531.         const lastProp = lastProps[propKey];
    
  1532.         if (lastProps.hasOwnProperty(propKey) && lastProp != null) {
    
  1533.           switch (propKey) {
    
  1534.             case 'value': {
    
  1535.               // This is handled by updateWrapper below.
    
  1536.               break;
    
  1537.             }
    
  1538.             // defaultValue are ignored by setProp
    
  1539.             case 'multiple': {
    
  1540.               wasMultiple = lastProp;
    
  1541.               // TODO: Move special case in here from setProp.
    
  1542.             }
    
  1543.             // Fallthrough
    
  1544.             default: {
    
  1545.               if (!nextProps.hasOwnProperty(propKey))
    
  1546.                 setProp(domElement, tag, propKey, null, nextProps, lastProp);
    
  1547.             }
    
  1548.           }
    
  1549.         }
    
  1550.       }
    
  1551.       for (const propKey in nextProps) {
    
  1552.         const nextProp = nextProps[propKey];
    
  1553.         const lastProp = lastProps[propKey];
    
  1554.         if (
    
  1555.           nextProps.hasOwnProperty(propKey) &&
    
  1556.           (nextProp != null || lastProp != null)
    
  1557.         ) {
    
  1558.           switch (propKey) {
    
  1559.             case 'value': {
    
  1560.               value = nextProp;
    
  1561.               // This is handled by updateSelect below.
    
  1562.               break;
    
  1563.             }
    
  1564.             case 'defaultValue': {
    
  1565.               defaultValue = nextProp;
    
  1566.               break;
    
  1567.             }
    
  1568.             case 'multiple': {
    
  1569.               multiple = nextProp;
    
  1570.               // TODO: Just move the special case in here from setProp.
    
  1571.             }
    
  1572.             // Fallthrough
    
  1573.             default: {
    
  1574.               if (nextProp !== lastProp)
    
  1575.                 setProp(
    
  1576.                   domElement,
    
  1577.                   tag,
    
  1578.                   propKey,
    
  1579.                   nextProp,
    
  1580.                   nextProps,
    
  1581.                   lastProp,
    
  1582.                 );
    
  1583.             }
    
  1584.           }
    
  1585.         }
    
  1586.       }
    
  1587.       // <select> value update needs to occur after <option> children
    
  1588.       // reconciliation
    
  1589.       updateSelect(domElement, value, defaultValue, multiple, wasMultiple);
    
  1590.       return;
    
  1591.     }
    
  1592.     case 'textarea': {
    
  1593.       let value = null;
    
  1594.       let defaultValue = null;
    
  1595.       for (const propKey in lastProps) {
    
  1596.         const lastProp = lastProps[propKey];
    
  1597.         if (
    
  1598.           lastProps.hasOwnProperty(propKey) &&
    
  1599.           lastProp != null &&
    
  1600.           !nextProps.hasOwnProperty(propKey)
    
  1601.         ) {
    
  1602.           switch (propKey) {
    
  1603.             case 'value': {
    
  1604.               // This is handled by updateTextarea below.
    
  1605.               break;
    
  1606.             }
    
  1607.             case 'children': {
    
  1608.               // TODO: This doesn't actually do anything if it updates.
    
  1609.               break;
    
  1610.             }
    
  1611.             // defaultValue is ignored by setProp
    
  1612.             default: {
    
  1613.               setProp(domElement, tag, propKey, null, nextProps, lastProp);
    
  1614.             }
    
  1615.           }
    
  1616.         }
    
  1617.       }
    
  1618.       for (const propKey in nextProps) {
    
  1619.         const nextProp = nextProps[propKey];
    
  1620.         const lastProp = lastProps[propKey];
    
  1621.         if (
    
  1622.           nextProps.hasOwnProperty(propKey) &&
    
  1623.           (nextProp != null || lastProp != null)
    
  1624.         ) {
    
  1625.           switch (propKey) {
    
  1626.             case 'value': {
    
  1627.               value = nextProp;
    
  1628.               // This is handled by updateTextarea below.
    
  1629.               break;
    
  1630.             }
    
  1631.             case 'defaultValue': {
    
  1632.               defaultValue = nextProp;
    
  1633.               break;
    
  1634.             }
    
  1635.             case 'children': {
    
  1636.               // TODO: This doesn't actually do anything if it updates.
    
  1637.               break;
    
  1638.             }
    
  1639.             case 'dangerouslySetInnerHTML': {
    
  1640.               if (nextProp != null) {
    
  1641.                 // TODO: Do we really need a special error message for this. It's also pretty blunt.
    
  1642.                 throw new Error(
    
  1643.                   '`dangerouslySetInnerHTML` does not make sense on <textarea>.',
    
  1644.                 );
    
  1645.               }
    
  1646.               break;
    
  1647.             }
    
  1648.             default: {
    
  1649.               if (nextProp !== lastProp)
    
  1650.                 setProp(
    
  1651.                   domElement,
    
  1652.                   tag,
    
  1653.                   propKey,
    
  1654.                   nextProp,
    
  1655.                   nextProps,
    
  1656.                   lastProp,
    
  1657.                 );
    
  1658.             }
    
  1659.           }
    
  1660.         }
    
  1661.       }
    
  1662.       updateTextarea(domElement, value, defaultValue);
    
  1663.       return;
    
  1664.     }
    
  1665.     case 'option': {
    
  1666.       for (const propKey in lastProps) {
    
  1667.         const lastProp = lastProps[propKey];
    
  1668.         if (
    
  1669.           lastProps.hasOwnProperty(propKey) &&
    
  1670.           lastProp != null &&
    
  1671.           !nextProps.hasOwnProperty(propKey)
    
  1672.         ) {
    
  1673.           switch (propKey) {
    
  1674.             case 'selected': {
    
  1675.               // TODO: Remove support for selected on option.
    
  1676.               (domElement: any).selected = false;
    
  1677.               break;
    
  1678.             }
    
  1679.             default: {
    
  1680.               setProp(domElement, tag, propKey, null, nextProps, lastProp);
    
  1681.             }
    
  1682.           }
    
  1683.         }
    
  1684.       }
    
  1685.       for (const propKey in nextProps) {
    
  1686.         const nextProp = nextProps[propKey];
    
  1687.         const lastProp = lastProps[propKey];
    
  1688.         if (
    
  1689.           nextProps.hasOwnProperty(propKey) &&
    
  1690.           nextProp !== lastProp &&
    
  1691.           (nextProp != null || lastProp != null)
    
  1692.         ) {
    
  1693.           switch (propKey) {
    
  1694.             case 'selected': {
    
  1695.               // TODO: Remove support for selected on option.
    
  1696.               (domElement: any).selected =
    
  1697.                 nextProp &&
    
  1698.                 typeof nextProp !== 'function' &&
    
  1699.                 typeof nextProp !== 'symbol';
    
  1700.               break;
    
  1701.             }
    
  1702.             default: {
    
  1703.               setProp(domElement, tag, propKey, nextProp, nextProps, lastProp);
    
  1704.             }
    
  1705.           }
    
  1706.         }
    
  1707.       }
    
  1708.       return;
    
  1709.     }
    
  1710.     case 'img':
    
  1711.     case 'link':
    
  1712.     case 'area':
    
  1713.     case 'base':
    
  1714.     case 'br':
    
  1715.     case 'col':
    
  1716.     case 'embed':
    
  1717.     case 'hr':
    
  1718.     case 'keygen':
    
  1719.     case 'meta':
    
  1720.     case 'param':
    
  1721.     case 'source':
    
  1722.     case 'track':
    
  1723.     case 'wbr':
    
  1724.     case 'menuitem': {
    
  1725.       // Void elements
    
  1726.       for (const propKey in lastProps) {
    
  1727.         const lastProp = lastProps[propKey];
    
  1728.         if (
    
  1729.           lastProps.hasOwnProperty(propKey) &&
    
  1730.           lastProp != null &&
    
  1731.           !nextProps.hasOwnProperty(propKey)
    
  1732.         ) {
    
  1733.           setProp(domElement, tag, propKey, null, nextProps, lastProp);
    
  1734.         }
    
  1735.       }
    
  1736.       for (const propKey in nextProps) {
    
  1737.         const nextProp = nextProps[propKey];
    
  1738.         const lastProp = lastProps[propKey];
    
  1739.         if (
    
  1740.           nextProps.hasOwnProperty(propKey) &&
    
  1741.           nextProp !== lastProp &&
    
  1742.           (nextProp != null || lastProp != null)
    
  1743.         ) {
    
  1744.           switch (propKey) {
    
  1745.             case 'children':
    
  1746.             case 'dangerouslySetInnerHTML': {
    
  1747.               if (nextProp != null) {
    
  1748.                 // TODO: Can we make this a DEV warning to avoid this deny list?
    
  1749.                 throw new Error(
    
  1750.                   `${tag} is a void element tag and must neither have \`children\` nor ` +
    
  1751.                     'use `dangerouslySetInnerHTML`.',
    
  1752.                 );
    
  1753.               }
    
  1754.               break;
    
  1755.             }
    
  1756.             // defaultChecked and defaultValue are ignored by setProp
    
  1757.             default: {
    
  1758.               setProp(domElement, tag, propKey, nextProp, nextProps, lastProp);
    
  1759.             }
    
  1760.           }
    
  1761.         }
    
  1762.       }
    
  1763.       return;
    
  1764.     }
    
  1765.     default: {
    
  1766.       if (isCustomElement(tag, nextProps)) {
    
  1767.         for (const propKey in lastProps) {
    
  1768.           const lastProp = lastProps[propKey];
    
  1769.           if (
    
  1770.             lastProps.hasOwnProperty(propKey) &&
    
  1771.             lastProp != null &&
    
  1772.             !nextProps.hasOwnProperty(propKey)
    
  1773.           ) {
    
  1774.             setPropOnCustomElement(
    
  1775.               domElement,
    
  1776.               tag,
    
  1777.               propKey,
    
  1778.               null,
    
  1779.               nextProps,
    
  1780.               lastProp,
    
  1781.             );
    
  1782.           }
    
  1783.         }
    
  1784.         for (const propKey in nextProps) {
    
  1785.           const nextProp = nextProps[propKey];
    
  1786.           const lastProp = lastProps[propKey];
    
  1787.           if (
    
  1788.             nextProps.hasOwnProperty(propKey) &&
    
  1789.             nextProp !== lastProp &&
    
  1790.             (nextProp != null || lastProp != null)
    
  1791.           ) {
    
  1792.             setPropOnCustomElement(
    
  1793.               domElement,
    
  1794.               tag,
    
  1795.               propKey,
    
  1796.               nextProp,
    
  1797.               nextProps,
    
  1798.               lastProp,
    
  1799.             );
    
  1800.           }
    
  1801.         }
    
  1802.         return;
    
  1803.       }
    
  1804.     }
    
  1805.   }
    
  1806. 
    
  1807.   for (const propKey in lastProps) {
    
  1808.     const lastProp = lastProps[propKey];
    
  1809.     if (
    
  1810.       lastProps.hasOwnProperty(propKey) &&
    
  1811.       lastProp != null &&
    
  1812.       !nextProps.hasOwnProperty(propKey)
    
  1813.     ) {
    
  1814.       setProp(domElement, tag, propKey, null, nextProps, lastProp);
    
  1815.     }
    
  1816.   }
    
  1817.   for (const propKey in nextProps) {
    
  1818.     const nextProp = nextProps[propKey];
    
  1819.     const lastProp = lastProps[propKey];
    
  1820.     if (
    
  1821.       nextProps.hasOwnProperty(propKey) &&
    
  1822.       nextProp !== lastProp &&
    
  1823.       (nextProp != null || lastProp != null)
    
  1824.     ) {
    
  1825.       setProp(domElement, tag, propKey, nextProp, nextProps, lastProp);
    
  1826.     }
    
  1827.   }
    
  1828. }
    
  1829. 
    
  1830. function getPossibleStandardName(propName: string): string | null {
    
  1831.   if (__DEV__) {
    
  1832.     const lowerCasedName = propName.toLowerCase();
    
  1833.     if (!possibleStandardNames.hasOwnProperty(lowerCasedName)) {
    
  1834.       return null;
    
  1835.     }
    
  1836.     return possibleStandardNames[lowerCasedName] || null;
    
  1837.   }
    
  1838.   return null;
    
  1839. }
    
  1840. 
    
  1841. function diffHydratedStyles(domElement: Element, value: mixed) {
    
  1842.   if (value != null && typeof value !== 'object') {
    
  1843.     throw new Error(
    
  1844.       'The `style` prop expects a mapping from style properties to values, ' +
    
  1845.         "not a string. For example, style={{marginRight: spacing + 'em'}} when " +
    
  1846.         'using JSX.',
    
  1847.     );
    
  1848.   }
    
  1849.   if (canDiffStyleForHydrationWarning) {
    
  1850.     const expectedStyle = createDangerousStringForStyles(value);
    
  1851.     const serverValue = domElement.getAttribute('style');
    
  1852.     warnForPropDifference('style', serverValue, expectedStyle);
    
  1853.   }
    
  1854. }
    
  1855. 
    
  1856. function hydrateAttribute(
    
  1857.   domElement: Element,
    
  1858.   propKey: string,
    
  1859.   attributeName: string,
    
  1860.   value: any,
    
  1861.   extraAttributes: Set<string>,
    
  1862. ): void {
    
  1863.   extraAttributes.delete(attributeName);
    
  1864.   const serverValue = domElement.getAttribute(attributeName);
    
  1865.   if (serverValue === null) {
    
  1866.     switch (typeof value) {
    
  1867.       case 'undefined':
    
  1868.       case 'function':
    
  1869.       case 'symbol':
    
  1870.       case 'boolean':
    
  1871.         return;
    
  1872.     }
    
  1873.   } else {
    
  1874.     if (value == null) {
    
  1875.       // We had an attribute but shouldn't have had one, so read it
    
  1876.       // for the error message.
    
  1877.     } else {
    
  1878.       switch (typeof value) {
    
  1879.         case 'function':
    
  1880.         case 'symbol':
    
  1881.         case 'boolean':
    
  1882.           break;
    
  1883.         default: {
    
  1884.           if (__DEV__) {
    
  1885.             checkAttributeStringCoercion(value, propKey);
    
  1886.           }
    
  1887.           if (serverValue === '' + value) {
    
  1888.             return;
    
  1889.           }
    
  1890.         }
    
  1891.       }
    
  1892.     }
    
  1893.   }
    
  1894.   warnForPropDifference(propKey, serverValue, value);
    
  1895. }
    
  1896. 
    
  1897. function hydrateBooleanAttribute(
    
  1898.   domElement: Element,
    
  1899.   propKey: string,
    
  1900.   attributeName: string,
    
  1901.   value: any,
    
  1902.   extraAttributes: Set<string>,
    
  1903. ): void {
    
  1904.   extraAttributes.delete(attributeName);
    
  1905.   const serverValue = domElement.getAttribute(attributeName);
    
  1906.   if (serverValue === null) {
    
  1907.     switch (typeof value) {
    
  1908.       case 'function':
    
  1909.       case 'symbol':
    
  1910.         return;
    
  1911.     }
    
  1912.     if (!value) {
    
  1913.       return;
    
  1914.     }
    
  1915.   } else {
    
  1916.     switch (typeof value) {
    
  1917.       case 'function':
    
  1918.       case 'symbol':
    
  1919.         break;
    
  1920.       default: {
    
  1921.         if (value) {
    
  1922.           // If this was a boolean, it doesn't matter what the value is
    
  1923.           // the fact that we have it is the same as the expected.
    
  1924.           // As long as it's positive.
    
  1925.           return;
    
  1926.         }
    
  1927.       }
    
  1928.     }
    
  1929.   }
    
  1930.   warnForPropDifference(propKey, serverValue, value);
    
  1931. }
    
  1932. 
    
  1933. function hydrateOverloadedBooleanAttribute(
    
  1934.   domElement: Element,
    
  1935.   propKey: string,
    
  1936.   attributeName: string,
    
  1937.   value: any,
    
  1938.   extraAttributes: Set<string>,
    
  1939. ): void {
    
  1940.   extraAttributes.delete(attributeName);
    
  1941.   const serverValue = domElement.getAttribute(attributeName);
    
  1942.   if (serverValue === null) {
    
  1943.     switch (typeof value) {
    
  1944.       case 'undefined':
    
  1945.       case 'function':
    
  1946.       case 'symbol':
    
  1947.         return;
    
  1948.       default:
    
  1949.         if (value === false) {
    
  1950.           return;
    
  1951.         }
    
  1952.     }
    
  1953.   } else {
    
  1954.     if (value == null) {
    
  1955.       // We had an attribute but shouldn't have had one, so read it
    
  1956.       // for the error message.
    
  1957.     } else {
    
  1958.       switch (typeof value) {
    
  1959.         case 'function':
    
  1960.         case 'symbol':
    
  1961.           break;
    
  1962.         case 'boolean':
    
  1963.           if (value === true && serverValue === '') {
    
  1964.             return;
    
  1965.           }
    
  1966.           break;
    
  1967.         default: {
    
  1968.           if (__DEV__) {
    
  1969.             checkAttributeStringCoercion(value, propKey);
    
  1970.           }
    
  1971.           if (serverValue === '' + value) {
    
  1972.             return;
    
  1973.           }
    
  1974.         }
    
  1975.       }
    
  1976.     }
    
  1977.   }
    
  1978.   warnForPropDifference(propKey, serverValue, value);
    
  1979. }
    
  1980. 
    
  1981. function hydrateBooleanishAttribute(
    
  1982.   domElement: Element,
    
  1983.   propKey: string,
    
  1984.   attributeName: string,
    
  1985.   value: any,
    
  1986.   extraAttributes: Set<string>,
    
  1987. ): void {
    
  1988.   extraAttributes.delete(attributeName);
    
  1989.   const serverValue = domElement.getAttribute(attributeName);
    
  1990.   if (serverValue === null) {
    
  1991.     switch (typeof value) {
    
  1992.       case 'undefined':
    
  1993.       case 'function':
    
  1994.       case 'symbol':
    
  1995.         return;
    
  1996.     }
    
  1997.   } else {
    
  1998.     if (value == null) {
    
  1999.       // We had an attribute but shouldn't have had one, so read it
    
  2000.       // for the error message.
    
  2001.     } else {
    
  2002.       switch (typeof value) {
    
  2003.         case 'function':
    
  2004.         case 'symbol':
    
  2005.           break;
    
  2006.         default: {
    
  2007.           if (__DEV__) {
    
  2008.             checkAttributeStringCoercion(value, attributeName);
    
  2009.           }
    
  2010.           if (serverValue === '' + (value: any)) {
    
  2011.             return;
    
  2012.           }
    
  2013.         }
    
  2014.       }
    
  2015.     }
    
  2016.   }
    
  2017.   warnForPropDifference(propKey, serverValue, value);
    
  2018. }
    
  2019. 
    
  2020. function hydrateNumericAttribute(
    
  2021.   domElement: Element,
    
  2022.   propKey: string,
    
  2023.   attributeName: string,
    
  2024.   value: any,
    
  2025.   extraAttributes: Set<string>,
    
  2026. ): void {
    
  2027.   extraAttributes.delete(attributeName);
    
  2028.   const serverValue = domElement.getAttribute(attributeName);
    
  2029.   if (serverValue === null) {
    
  2030.     switch (typeof value) {
    
  2031.       case 'undefined':
    
  2032.       case 'function':
    
  2033.       case 'symbol':
    
  2034.       case 'boolean':
    
  2035.         return;
    
  2036.       default:
    
  2037.         if (isNaN(value)) {
    
  2038.           return;
    
  2039.         }
    
  2040.     }
    
  2041.   } else {
    
  2042.     if (value == null) {
    
  2043.       // We had an attribute but shouldn't have had one, so read it
    
  2044.       // for the error message.
    
  2045.     } else {
    
  2046.       switch (typeof value) {
    
  2047.         case 'function':
    
  2048.         case 'symbol':
    
  2049.         case 'boolean':
    
  2050.           break;
    
  2051.         default: {
    
  2052.           if (isNaN(value)) {
    
  2053.             // We had an attribute but shouldn't have had one, so read it
    
  2054.             // for the error message.
    
  2055.             break;
    
  2056.           }
    
  2057.           if (__DEV__) {
    
  2058.             checkAttributeStringCoercion(value, propKey);
    
  2059.           }
    
  2060.           if (serverValue === '' + value) {
    
  2061.             return;
    
  2062.           }
    
  2063.         }
    
  2064.       }
    
  2065.     }
    
  2066.   }
    
  2067.   warnForPropDifference(propKey, serverValue, value);
    
  2068. }
    
  2069. 
    
  2070. function hydratePositiveNumericAttribute(
    
  2071.   domElement: Element,
    
  2072.   propKey: string,
    
  2073.   attributeName: string,
    
  2074.   value: any,
    
  2075.   extraAttributes: Set<string>,
    
  2076. ): void {
    
  2077.   extraAttributes.delete(attributeName);
    
  2078.   const serverValue = domElement.getAttribute(attributeName);
    
  2079.   if (serverValue === null) {
    
  2080.     switch (typeof value) {
    
  2081.       case 'undefined':
    
  2082.       case 'function':
    
  2083.       case 'symbol':
    
  2084.       case 'boolean':
    
  2085.         return;
    
  2086.       default:
    
  2087.         if (isNaN(value) || value < 1) {
    
  2088.           return;
    
  2089.         }
    
  2090.     }
    
  2091.   } else {
    
  2092.     if (value == null) {
    
  2093.       // We had an attribute but shouldn't have had one, so read it
    
  2094.       // for the error message.
    
  2095.     } else {
    
  2096.       switch (typeof value) {
    
  2097.         case 'function':
    
  2098.         case 'symbol':
    
  2099.         case 'boolean':
    
  2100.           break;
    
  2101.         default: {
    
  2102.           if (isNaN(value) || value < 1) {
    
  2103.             // We had an attribute but shouldn't have had one, so read it
    
  2104.             // for the error message.
    
  2105.             break;
    
  2106.           }
    
  2107.           if (__DEV__) {
    
  2108.             checkAttributeStringCoercion(value, propKey);
    
  2109.           }
    
  2110.           if (serverValue === '' + value) {
    
  2111.             return;
    
  2112.           }
    
  2113.         }
    
  2114.       }
    
  2115.     }
    
  2116.   }
    
  2117.   warnForPropDifference(propKey, serverValue, value);
    
  2118. }
    
  2119. 
    
  2120. function hydrateSanitizedAttribute(
    
  2121.   domElement: Element,
    
  2122.   propKey: string,
    
  2123.   attributeName: string,
    
  2124.   value: any,
    
  2125.   extraAttributes: Set<string>,
    
  2126. ): void {
    
  2127.   extraAttributes.delete(attributeName);
    
  2128.   const serverValue = domElement.getAttribute(attributeName);
    
  2129.   if (serverValue === null) {
    
  2130.     switch (typeof value) {
    
  2131.       case 'undefined':
    
  2132.       case 'function':
    
  2133.       case 'symbol':
    
  2134.       case 'boolean':
    
  2135.         return;
    
  2136.     }
    
  2137.   } else {
    
  2138.     if (value == null) {
    
  2139.       // We had an attribute but shouldn't have had one, so read it
    
  2140.       // for the error message.
    
  2141.     } else {
    
  2142.       switch (typeof value) {
    
  2143.         case 'function':
    
  2144.         case 'symbol':
    
  2145.         case 'boolean':
    
  2146.           break;
    
  2147.         default: {
    
  2148.           if (__DEV__) {
    
  2149.             checkAttributeStringCoercion(value, propKey);
    
  2150.           }
    
  2151.           const sanitizedValue = sanitizeURL('' + value);
    
  2152.           if (serverValue === sanitizedValue) {
    
  2153.             return;
    
  2154.           }
    
  2155.         }
    
  2156.       }
    
  2157.     }
    
  2158.   }
    
  2159.   warnForPropDifference(propKey, serverValue, value);
    
  2160. }
    
  2161. 
    
  2162. function diffHydratedCustomComponent(
    
  2163.   domElement: Element,
    
  2164.   tag: string,
    
  2165.   props: Object,
    
  2166.   hostContext: HostContext,
    
  2167.   extraAttributes: Set<string>,
    
  2168. ) {
    
  2169.   for (const propKey in props) {
    
  2170.     if (!props.hasOwnProperty(propKey)) {
    
  2171.       continue;
    
  2172.     }
    
  2173.     const value = props[propKey];
    
  2174.     if (value == null) {
    
  2175.       continue;
    
  2176.     }
    
  2177.     if (registrationNameDependencies.hasOwnProperty(propKey)) {
    
  2178.       if (typeof value !== 'function') {
    
  2179.         warnForInvalidEventListener(propKey, value);
    
  2180.       }
    
  2181.       continue;
    
  2182.     }
    
  2183.     if (props.suppressHydrationWarning === true) {
    
  2184.       // Don't bother comparing. We're ignoring all these warnings.
    
  2185.       continue;
    
  2186.     }
    
  2187.     // Validate that the properties correspond to their expected values.
    
  2188.     switch (propKey) {
    
  2189.       case 'children': // Checked above already
    
  2190.       case 'suppressContentEditableWarning':
    
  2191.       case 'suppressHydrationWarning':
    
  2192.       case 'defaultValue':
    
  2193.       case 'defaultChecked':
    
  2194.       case 'innerHTML':
    
  2195.         // Noop
    
  2196.         continue;
    
  2197.       case 'dangerouslySetInnerHTML':
    
  2198.         const serverHTML = domElement.innerHTML;
    
  2199.         const nextHtml = value ? value.__html : undefined;
    
  2200.         if (nextHtml != null) {
    
  2201.           const expectedHTML = normalizeHTML(domElement, nextHtml);
    
  2202.           warnForPropDifference(propKey, serverHTML, expectedHTML);
    
  2203.         }
    
  2204.         continue;
    
  2205.       case 'style':
    
  2206.         extraAttributes.delete(propKey);
    
  2207.         diffHydratedStyles(domElement, value);
    
  2208.         continue;
    
  2209.       case 'offsetParent':
    
  2210.       case 'offsetTop':
    
  2211.       case 'offsetLeft':
    
  2212.       case 'offsetWidth':
    
  2213.       case 'offsetHeight':
    
  2214.       case 'isContentEditable':
    
  2215.       case 'outerText':
    
  2216.       case 'outerHTML':
    
  2217.         if (enableCustomElementPropertySupport) {
    
  2218.           extraAttributes.delete(propKey.toLowerCase());
    
  2219.           if (__DEV__) {
    
  2220.             console.error(
    
  2221.               'Assignment to read-only property will result in a no-op: `%s`',
    
  2222.               propKey,
    
  2223.             );
    
  2224.           }
    
  2225.           continue;
    
  2226.         }
    
  2227.       // Fall through
    
  2228.       case 'className':
    
  2229.         if (enableCustomElementPropertySupport) {
    
  2230.           // className is a special cased property on the server to render as an attribute.
    
  2231.           extraAttributes.delete('class');
    
  2232.           const serverValue = getValueForAttributeOnCustomComponent(
    
  2233.             domElement,
    
  2234.             'class',
    
  2235.             value,
    
  2236.           );
    
  2237.           warnForPropDifference('className', serverValue, value);
    
  2238.           continue;
    
  2239.         }
    
  2240.       // Fall through
    
  2241.       default: {
    
  2242.         // This is a DEV-only path
    
  2243.         const hostContextDev: HostContextDev = (hostContext: any);
    
  2244.         const hostContextProd = hostContextDev.context;
    
  2245.         if (
    
  2246.           hostContextProd === HostContextNamespaceNone &&
    
  2247.           tag !== 'svg' &&
    
  2248.           tag !== 'math'
    
  2249.         ) {
    
  2250.           extraAttributes.delete(propKey.toLowerCase());
    
  2251.         } else {
    
  2252.           extraAttributes.delete(propKey);
    
  2253.         }
    
  2254.         const serverValue = getValueForAttributeOnCustomComponent(
    
  2255.           domElement,
    
  2256.           propKey,
    
  2257.           value,
    
  2258.         );
    
  2259.         warnForPropDifference(propKey, serverValue, value);
    
  2260.       }
    
  2261.     }
    
  2262.   }
    
  2263. }
    
  2264. 
    
  2265. // This is the exact URL string we expect that Fizz renders if we provide a function action.
    
  2266. // We use this for hydration warnings. It needs to be in sync with Fizz. Maybe makes sense
    
  2267. // as a shared module for that reason.
    
  2268. const EXPECTED_FORM_ACTION_URL =
    
  2269.   // eslint-disable-next-line no-script-url
    
  2270.   "javascript:throw new Error('A React form was unexpectedly submitted.')";
    
  2271. 
    
  2272. function diffHydratedGenericElement(
    
  2273.   domElement: Element,
    
  2274.   tag: string,
    
  2275.   props: Object,
    
  2276.   hostContext: HostContext,
    
  2277.   extraAttributes: Set<string>,
    
  2278. ) {
    
  2279.   for (const propKey in props) {
    
  2280.     if (!props.hasOwnProperty(propKey)) {
    
  2281.       continue;
    
  2282.     }
    
  2283.     const value = props[propKey];
    
  2284.     if (value == null) {
    
  2285.       continue;
    
  2286.     }
    
  2287.     if (registrationNameDependencies.hasOwnProperty(propKey)) {
    
  2288.       if (typeof value !== 'function') {
    
  2289.         warnForInvalidEventListener(propKey, value);
    
  2290.       }
    
  2291.       continue;
    
  2292.     }
    
  2293.     if (props.suppressHydrationWarning === true) {
    
  2294.       // Don't bother comparing. We're ignoring all these warnings.
    
  2295.       continue;
    
  2296.     }
    
  2297.     // Validate that the properties correspond to their expected values.
    
  2298.     switch (propKey) {
    
  2299.       case 'children': // Checked above already
    
  2300.       case 'suppressContentEditableWarning':
    
  2301.       case 'suppressHydrationWarning':
    
  2302.       case 'value': // Controlled attributes are not validated
    
  2303.       case 'checked': // TODO: Only ignore them on controlled tags.
    
  2304.       case 'selected':
    
  2305.       case 'defaultValue':
    
  2306.       case 'defaultChecked':
    
  2307.       case 'innerHTML':
    
  2308.         // Noop
    
  2309.         continue;
    
  2310.       case 'dangerouslySetInnerHTML':
    
  2311.         const serverHTML = domElement.innerHTML;
    
  2312.         const nextHtml = value ? value.__html : undefined;
    
  2313.         if (nextHtml != null) {
    
  2314.           const expectedHTML = normalizeHTML(domElement, nextHtml);
    
  2315.           warnForPropDifference(propKey, serverHTML, expectedHTML);
    
  2316.         }
    
  2317.         continue;
    
  2318.       case 'className':
    
  2319.         hydrateAttribute(domElement, propKey, 'class', value, extraAttributes);
    
  2320.         continue;
    
  2321.       case 'tabIndex':
    
  2322.         hydrateAttribute(
    
  2323.           domElement,
    
  2324.           propKey,
    
  2325.           'tabindex',
    
  2326.           value,
    
  2327.           extraAttributes,
    
  2328.         );
    
  2329.         continue;
    
  2330.       case 'style':
    
  2331.         extraAttributes.delete(propKey);
    
  2332.         diffHydratedStyles(domElement, value);
    
  2333.         continue;
    
  2334.       case 'multiple': {
    
  2335.         extraAttributes.delete(propKey);
    
  2336.         const serverValue = (domElement: any).multiple;
    
  2337.         warnForPropDifference(propKey, serverValue, value);
    
  2338.         continue;
    
  2339.       }
    
  2340.       case 'muted': {
    
  2341.         extraAttributes.delete(propKey);
    
  2342.         const serverValue = (domElement: any).muted;
    
  2343.         warnForPropDifference(propKey, serverValue, value);
    
  2344.         continue;
    
  2345.       }
    
  2346.       case 'autoFocus': {
    
  2347.         extraAttributes.delete('autofocus');
    
  2348.         const serverValue = (domElement: any).autofocus;
    
  2349.         warnForPropDifference(propKey, serverValue, value);
    
  2350.         continue;
    
  2351.       }
    
  2352.       case 'src':
    
  2353.       case 'href':
    
  2354.         if (enableFilterEmptyStringAttributesDOM) {
    
  2355.           if (value === '') {
    
  2356.             if (__DEV__) {
    
  2357.               if (propKey === 'src') {
    
  2358.                 console.error(
    
  2359.                   'An empty string ("") was passed to the %s attribute. ' +
    
  2360.                     'This may cause the browser to download the whole page again over the network. ' +
    
  2361.                     'To fix this, either do not render the element at all ' +
    
  2362.                     'or pass null to %s instead of an empty string.',
    
  2363.                   propKey,
    
  2364.                   propKey,
    
  2365.                 );
    
  2366.               } else {
    
  2367.                 console.error(
    
  2368.                   'An empty string ("") was passed to the %s attribute. ' +
    
  2369.                     'To fix this, either do not render the element at all ' +
    
  2370.                     'or pass null to %s instead of an empty string.',
    
  2371.                   propKey,
    
  2372.                   propKey,
    
  2373.                 );
    
  2374.               }
    
  2375.             }
    
  2376.             hydrateSanitizedAttribute(
    
  2377.               domElement,
    
  2378.               propKey,
    
  2379.               propKey,
    
  2380.               null,
    
  2381.               extraAttributes,
    
  2382.             );
    
  2383.             continue;
    
  2384.           }
    
  2385.         }
    
  2386.         hydrateSanitizedAttribute(
    
  2387.           domElement,
    
  2388.           propKey,
    
  2389.           propKey,
    
  2390.           value,
    
  2391.           extraAttributes,
    
  2392.         );
    
  2393.         continue;
    
  2394.       case 'action':
    
  2395.       case 'formAction':
    
  2396.         if (enableFormActions) {
    
  2397.           const serverValue = domElement.getAttribute(propKey);
    
  2398.           if (typeof value === 'function') {
    
  2399.             extraAttributes.delete(propKey.toLowerCase());
    
  2400.             // The server can set these extra properties to implement actions.
    
  2401.             // So we remove them from the extra attributes warnings.
    
  2402.             if (propKey === 'formAction') {
    
  2403.               extraAttributes.delete('name');
    
  2404.               extraAttributes.delete('formenctype');
    
  2405.               extraAttributes.delete('formmethod');
    
  2406.               extraAttributes.delete('formtarget');
    
  2407.             } else {
    
  2408.               extraAttributes.delete('enctype');
    
  2409.               extraAttributes.delete('method');
    
  2410.               extraAttributes.delete('target');
    
  2411.             }
    
  2412.             // Ideally we should be able to warn if the server value was not a function
    
  2413.             // however since the function can return any of these attributes any way it
    
  2414.             // wants as a custom progressive enhancement, there's nothing to compare to.
    
  2415.             // We can check if the function has the $FORM_ACTION property on the client
    
  2416.             // and if it's not, warn, but that's an unnecessary constraint that they
    
  2417.             // have to have the extra extension that doesn't do anything on the client.
    
  2418.             continue;
    
  2419.           } else if (serverValue === EXPECTED_FORM_ACTION_URL) {
    
  2420.             extraAttributes.delete(propKey.toLowerCase());
    
  2421.             warnForPropDifference(propKey, 'function', value);
    
  2422.             continue;
    
  2423.           }
    
  2424.         }
    
  2425.         hydrateSanitizedAttribute(
    
  2426.           domElement,
    
  2427.           propKey,
    
  2428.           propKey.toLowerCase(),
    
  2429.           value,
    
  2430.           extraAttributes,
    
  2431.         );
    
  2432.         continue;
    
  2433.       case 'xlinkHref':
    
  2434.         hydrateSanitizedAttribute(
    
  2435.           domElement,
    
  2436.           propKey,
    
  2437.           'xlink:href',
    
  2438.           value,
    
  2439.           extraAttributes,
    
  2440.         );
    
  2441.         continue;
    
  2442.       case 'contentEditable': {
    
  2443.         // Lower-case Booleanish String
    
  2444.         hydrateBooleanishAttribute(
    
  2445.           domElement,
    
  2446.           propKey,
    
  2447.           'contenteditable',
    
  2448.           value,
    
  2449.           extraAttributes,
    
  2450.         );
    
  2451.         continue;
    
  2452.       }
    
  2453.       case 'spellCheck': {
    
  2454.         // Lower-case Booleanish String
    
  2455.         hydrateBooleanishAttribute(
    
  2456.           domElement,
    
  2457.           propKey,
    
  2458.           'spellcheck',
    
  2459.           value,
    
  2460.           extraAttributes,
    
  2461.         );
    
  2462.         continue;
    
  2463.       }
    
  2464.       case 'draggable':
    
  2465.       case 'autoReverse':
    
  2466.       case 'externalResourcesRequired':
    
  2467.       case 'focusable':
    
  2468.       case 'preserveAlpha': {
    
  2469.         // Case-sensitive Booleanish String
    
  2470.         hydrateBooleanishAttribute(
    
  2471.           domElement,
    
  2472.           propKey,
    
  2473.           propKey,
    
  2474.           value,
    
  2475.           extraAttributes,
    
  2476.         );
    
  2477.         continue;
    
  2478.       }
    
  2479.       case 'allowFullScreen':
    
  2480.       case 'async':
    
  2481.       case 'autoPlay':
    
  2482.       case 'controls':
    
  2483.       case 'default':
    
  2484.       case 'defer':
    
  2485.       case 'disabled':
    
  2486.       case 'disablePictureInPicture':
    
  2487.       case 'disableRemotePlayback':
    
  2488.       case 'formNoValidate':
    
  2489.       case 'hidden':
    
  2490.       case 'loop':
    
  2491.       case 'noModule':
    
  2492.       case 'noValidate':
    
  2493.       case 'open':
    
  2494.       case 'playsInline':
    
  2495.       case 'readOnly':
    
  2496.       case 'required':
    
  2497.       case 'reversed':
    
  2498.       case 'scoped':
    
  2499.       case 'seamless':
    
  2500.       case 'itemScope': {
    
  2501.         // Some of these need to be lower case to remove them from the extraAttributes list.
    
  2502.         hydrateBooleanAttribute(
    
  2503.           domElement,
    
  2504.           propKey,
    
  2505.           propKey.toLowerCase(),
    
  2506.           value,
    
  2507.           extraAttributes,
    
  2508.         );
    
  2509.         continue;
    
  2510.       }
    
  2511.       case 'capture':
    
  2512.       case 'download': {
    
  2513.         hydrateOverloadedBooleanAttribute(
    
  2514.           domElement,
    
  2515.           propKey,
    
  2516.           propKey,
    
  2517.           value,
    
  2518.           extraAttributes,
    
  2519.         );
    
  2520.         continue;
    
  2521.       }
    
  2522.       case 'cols':
    
  2523.       case 'rows':
    
  2524.       case 'size':
    
  2525.       case 'span': {
    
  2526.         hydratePositiveNumericAttribute(
    
  2527.           domElement,
    
  2528.           propKey,
    
  2529.           propKey,
    
  2530.           value,
    
  2531.           extraAttributes,
    
  2532.         );
    
  2533.         continue;
    
  2534.       }
    
  2535.       case 'rowSpan': {
    
  2536.         hydrateNumericAttribute(
    
  2537.           domElement,
    
  2538.           propKey,
    
  2539.           'rowspan',
    
  2540.           value,
    
  2541.           extraAttributes,
    
  2542.         );
    
  2543.         continue;
    
  2544.       }
    
  2545.       case 'start': {
    
  2546.         hydrateNumericAttribute(
    
  2547.           domElement,
    
  2548.           propKey,
    
  2549.           propKey,
    
  2550.           value,
    
  2551.           extraAttributes,
    
  2552.         );
    
  2553.         continue;
    
  2554.       }
    
  2555.       case 'xHeight':
    
  2556.         hydrateAttribute(
    
  2557.           domElement,
    
  2558.           propKey,
    
  2559.           'x-height',
    
  2560.           value,
    
  2561.           extraAttributes,
    
  2562.         );
    
  2563.         continue;
    
  2564.       case 'xlinkActuate':
    
  2565.         hydrateAttribute(
    
  2566.           domElement,
    
  2567.           propKey,
    
  2568.           'xlink:actuate',
    
  2569.           value,
    
  2570.           extraAttributes,
    
  2571.         );
    
  2572.         continue;
    
  2573.       case 'xlinkArcrole':
    
  2574.         hydrateAttribute(
    
  2575.           domElement,
    
  2576.           propKey,
    
  2577.           'xlink:arcrole',
    
  2578.           value,
    
  2579.           extraAttributes,
    
  2580.         );
    
  2581.         continue;
    
  2582.       case 'xlinkRole':
    
  2583.         hydrateAttribute(
    
  2584.           domElement,
    
  2585.           propKey,
    
  2586.           'xlink:role',
    
  2587.           value,
    
  2588.           extraAttributes,
    
  2589.         );
    
  2590.         continue;
    
  2591.       case 'xlinkShow':
    
  2592.         hydrateAttribute(
    
  2593.           domElement,
    
  2594.           propKey,
    
  2595.           'xlink:show',
    
  2596.           value,
    
  2597.           extraAttributes,
    
  2598.         );
    
  2599.         continue;
    
  2600.       case 'xlinkTitle':
    
  2601.         hydrateAttribute(
    
  2602.           domElement,
    
  2603.           propKey,
    
  2604.           'xlink:title',
    
  2605.           value,
    
  2606.           extraAttributes,
    
  2607.         );
    
  2608.         continue;
    
  2609.       case 'xlinkType':
    
  2610.         hydrateAttribute(
    
  2611.           domElement,
    
  2612.           propKey,
    
  2613.           'xlink:type',
    
  2614.           value,
    
  2615.           extraAttributes,
    
  2616.         );
    
  2617.         continue;
    
  2618.       case 'xmlBase':
    
  2619.         hydrateAttribute(
    
  2620.           domElement,
    
  2621.           propKey,
    
  2622.           'xml:base',
    
  2623.           value,
    
  2624.           extraAttributes,
    
  2625.         );
    
  2626.         continue;
    
  2627.       case 'xmlLang':
    
  2628.         hydrateAttribute(
    
  2629.           domElement,
    
  2630.           propKey,
    
  2631.           'xml:lang',
    
  2632.           value,
    
  2633.           extraAttributes,
    
  2634.         );
    
  2635.         continue;
    
  2636.       case 'xmlSpace':
    
  2637.         hydrateAttribute(
    
  2638.           domElement,
    
  2639.           propKey,
    
  2640.           'xml:space',
    
  2641.           value,
    
  2642.           extraAttributes,
    
  2643.         );
    
  2644.         continue;
    
  2645.       default: {
    
  2646.         if (
    
  2647.           // shouldIgnoreAttribute
    
  2648.           // We have already filtered out null/undefined and reserved words.
    
  2649.           propKey.length > 2 &&
    
  2650.           (propKey[0] === 'o' || propKey[0] === 'O') &&
    
  2651.           (propKey[1] === 'n' || propKey[1] === 'N')
    
  2652.         ) {
    
  2653.           continue;
    
  2654.         }
    
  2655.         const attributeName = getAttributeAlias(propKey);
    
  2656.         let isMismatchDueToBadCasing = false;
    
  2657. 
    
  2658.         // This is a DEV-only path
    
  2659.         const hostContextDev: HostContextDev = (hostContext: any);
    
  2660.         const hostContextProd = hostContextDev.context;
    
  2661. 
    
  2662.         if (
    
  2663.           hostContextProd === HostContextNamespaceNone &&
    
  2664.           tag !== 'svg' &&
    
  2665.           tag !== 'math'
    
  2666.         ) {
    
  2667.           extraAttributes.delete(attributeName.toLowerCase());
    
  2668.         } else {
    
  2669.           const standardName = getPossibleStandardName(propKey);
    
  2670.           if (standardName !== null && standardName !== propKey) {
    
  2671.             // If an SVG prop is supplied with bad casing, it will
    
  2672.             // be successfully parsed from HTML, but will produce a mismatch
    
  2673.             // (and would be incorrectly rendered on the client).
    
  2674.             // However, we already warn about bad casing elsewhere.
    
  2675.             // So we'll skip the misleading extra mismatch warning in this case.
    
  2676.             isMismatchDueToBadCasing = true;
    
  2677.             extraAttributes.delete(standardName);
    
  2678.           }
    
  2679.           extraAttributes.delete(attributeName);
    
  2680.         }
    
  2681.         const serverValue = getValueForAttribute(
    
  2682.           domElement,
    
  2683.           attributeName,
    
  2684.           value,
    
  2685.         );
    
  2686.         if (!isMismatchDueToBadCasing) {
    
  2687.           warnForPropDifference(propKey, serverValue, value);
    
  2688.         }
    
  2689.       }
    
  2690.     }
    
  2691.   }
    
  2692. }
    
  2693. 
    
  2694. export function diffHydratedProperties(
    
  2695.   domElement: Element,
    
  2696.   tag: string,
    
  2697.   props: Object,
    
  2698.   isConcurrentMode: boolean,
    
  2699.   shouldWarnDev: boolean,
    
  2700.   hostContext: HostContext,
    
  2701. ): void {
    
  2702.   if (__DEV__) {
    
  2703.     validatePropertiesInDevelopment(tag, props);
    
  2704.   }
    
  2705. 
    
  2706.   // TODO: Make sure that we check isMounted before firing any of these events.
    
  2707.   switch (tag) {
    
  2708.     case 'dialog':
    
  2709.       listenToNonDelegatedEvent('cancel', domElement);
    
  2710.       listenToNonDelegatedEvent('close', domElement);
    
  2711.       break;
    
  2712.     case 'iframe':
    
  2713.     case 'object':
    
  2714.     case 'embed':
    
  2715.       // We listen to this event in case to ensure emulated bubble
    
  2716.       // listeners still fire for the load event.
    
  2717.       listenToNonDelegatedEvent('load', domElement);
    
  2718.       break;
    
  2719.     case 'video':
    
  2720.     case 'audio':
    
  2721.       // We listen to these events in case to ensure emulated bubble
    
  2722.       // listeners still fire for all the media events.
    
  2723.       for (let i = 0; i < mediaEventTypes.length; i++) {
    
  2724.         listenToNonDelegatedEvent(mediaEventTypes[i], domElement);
    
  2725.       }
    
  2726.       break;
    
  2727.     case 'source':
    
  2728.       // We listen to this event in case to ensure emulated bubble
    
  2729.       // listeners still fire for the error event.
    
  2730.       listenToNonDelegatedEvent('error', domElement);
    
  2731.       break;
    
  2732.     case 'img':
    
  2733.     case 'image':
    
  2734.     case 'link':
    
  2735.       // We listen to these events in case to ensure emulated bubble
    
  2736.       // listeners still fire for error and load events.
    
  2737.       listenToNonDelegatedEvent('error', domElement);
    
  2738.       listenToNonDelegatedEvent('load', domElement);
    
  2739.       break;
    
  2740.     case 'details':
    
  2741.       // We listen to this event in case to ensure emulated bubble
    
  2742.       // listeners still fire for the toggle event.
    
  2743.       listenToNonDelegatedEvent('toggle', domElement);
    
  2744.       break;
    
  2745.     case 'input':
    
  2746.       if (__DEV__) {
    
  2747.         checkControlledValueProps('input', props);
    
  2748.       }
    
  2749.       // We listen to this event in case to ensure emulated bubble
    
  2750.       // listeners still fire for the invalid event.
    
  2751.       listenToNonDelegatedEvent('invalid', domElement);
    
  2752.       // TODO: Make sure we check if this is still unmounted or do any clean
    
  2753.       // up necessary since we never stop tracking anymore.
    
  2754.       validateInputProps(domElement, props);
    
  2755.       // For input and textarea we current always set the value property at
    
  2756.       // post mount to force it to diverge from attributes. However, for
    
  2757.       // option and select we don't quite do the same thing and select
    
  2758.       // is not resilient to the DOM state changing so we don't do that here.
    
  2759.       // TODO: Consider not doing this for input and textarea.
    
  2760.       initInput(
    
  2761.         domElement,
    
  2762.         props.value,
    
  2763.         props.defaultValue,
    
  2764.         props.checked,
    
  2765.         props.defaultChecked,
    
  2766.         props.type,
    
  2767.         props.name,
    
  2768.         true,
    
  2769.       );
    
  2770.       track((domElement: any));
    
  2771.       break;
    
  2772.     case 'option':
    
  2773.       validateOptionProps(domElement, props);
    
  2774.       break;
    
  2775.     case 'select':
    
  2776.       if (__DEV__) {
    
  2777.         checkControlledValueProps('select', props);
    
  2778.       }
    
  2779.       // We listen to this event in case to ensure emulated bubble
    
  2780.       // listeners still fire for the invalid event.
    
  2781.       listenToNonDelegatedEvent('invalid', domElement);
    
  2782.       validateSelectProps(domElement, props);
    
  2783.       break;
    
  2784.     case 'textarea':
    
  2785.       if (__DEV__) {
    
  2786.         checkControlledValueProps('textarea', props);
    
  2787.       }
    
  2788.       // We listen to this event in case to ensure emulated bubble
    
  2789.       // listeners still fire for the invalid event.
    
  2790.       listenToNonDelegatedEvent('invalid', domElement);
    
  2791.       // TODO: Make sure we check if this is still unmounted or do any clean
    
  2792.       // up necessary since we never stop tracking anymore.
    
  2793.       validateTextareaProps(domElement, props);
    
  2794.       initTextarea(domElement, props.value, props.defaultValue, props.children);
    
  2795.       track((domElement: any));
    
  2796.       break;
    
  2797.   }
    
  2798. 
    
  2799.   const children = props.children;
    
  2800.   // For text content children we compare against textContent. This
    
  2801.   // might match additional HTML that is hidden when we read it using
    
  2802.   // textContent. E.g. "foo" will match "f<span>oo</span>" but that still
    
  2803.   // satisfies our requirement. Our requirement is not to produce perfect
    
  2804.   // HTML and attributes. Ideally we should preserve structure but it's
    
  2805.   // ok not to if the visible content is still enough to indicate what
    
  2806.   // even listeners these nodes might be wired up to.
    
  2807.   // TODO: Warn if there is more than a single textNode as a child.
    
  2808.   // TODO: Should we use domElement.firstChild.nodeValue to compare?
    
  2809.   if (typeof children === 'string' || typeof children === 'number') {
    
  2810.     if (domElement.textContent !== '' + children) {
    
  2811.       if (props.suppressHydrationWarning !== true) {
    
  2812.         checkForUnmatchedText(
    
  2813.           domElement.textContent,
    
  2814.           children,
    
  2815.           isConcurrentMode,
    
  2816.           shouldWarnDev,
    
  2817.         );
    
  2818.       }
    
  2819.       if (!isConcurrentMode || !enableClientRenderFallbackOnTextMismatch) {
    
  2820.         // We really should be patching this in the commit phase but since
    
  2821.         // this only affects legacy mode hydration which is deprecated anyway
    
  2822.         // we can get away with it.
    
  2823.         // Host singletons get their children appended and don't use the text
    
  2824.         // content mechanism.
    
  2825.         if (!enableHostSingletons || tag !== 'body') {
    
  2826.           domElement.textContent = (children: any);
    
  2827.         }
    
  2828.       }
    
  2829.     }
    
  2830.   }
    
  2831. 
    
  2832.   if (props.onScroll != null) {
    
  2833.     listenToNonDelegatedEvent('scroll', domElement);
    
  2834.   }
    
  2835. 
    
  2836.   if (props.onScrollEnd != null) {
    
  2837.     listenToNonDelegatedEvent('scrollend', domElement);
    
  2838.   }
    
  2839. 
    
  2840.   if (props.onClick != null) {
    
  2841.     // TODO: This cast may not be sound for SVG, MathML or custom elements.
    
  2842.     trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
    
  2843.   }
    
  2844. 
    
  2845.   if (__DEV__ && shouldWarnDev) {
    
  2846.     const extraAttributes: Set<string> = new Set();
    
  2847.     const attributes = domElement.attributes;
    
  2848.     for (let i = 0; i < attributes.length; i++) {
    
  2849.       const name = attributes[i].name.toLowerCase();
    
  2850.       switch (name) {
    
  2851.         // Controlled attributes are not validated
    
  2852.         // TODO: Only ignore them on controlled tags.
    
  2853.         case 'value':
    
  2854.           break;
    
  2855.         case 'checked':
    
  2856.           break;
    
  2857.         case 'selected':
    
  2858.           break;
    
  2859.         default:
    
  2860.           // Intentionally use the original name.
    
  2861.           // See discussion in https://github.com/facebook/react/pull/10676.
    
  2862.           extraAttributes.add(attributes[i].name);
    
  2863.       }
    
  2864.     }
    
  2865.     if (isCustomElement(tag, props)) {
    
  2866.       diffHydratedCustomComponent(
    
  2867.         domElement,
    
  2868.         tag,
    
  2869.         props,
    
  2870.         hostContext,
    
  2871.         extraAttributes,
    
  2872.       );
    
  2873.     } else {
    
  2874.       diffHydratedGenericElement(
    
  2875.         domElement,
    
  2876.         tag,
    
  2877.         props,
    
  2878.         hostContext,
    
  2879.         extraAttributes,
    
  2880.       );
    
  2881.     }
    
  2882.     if (extraAttributes.size > 0 && props.suppressHydrationWarning !== true) {
    
  2883.       warnForExtraAttributes(extraAttributes);
    
  2884.     }
    
  2885.   }
    
  2886. }
    
  2887. 
    
  2888. export function diffHydratedText(
    
  2889.   textNode: Text,
    
  2890.   text: string,
    
  2891.   isConcurrentMode: boolean,
    
  2892. ): boolean {
    
  2893.   const isDifferent = textNode.nodeValue !== text;
    
  2894.   return isDifferent;
    
  2895. }
    
  2896. 
    
  2897. export function warnForDeletedHydratableElement(
    
  2898.   parentNode: Element | Document | DocumentFragment,
    
  2899.   child: Element,
    
  2900. ) {
    
  2901.   if (__DEV__) {
    
  2902.     if (didWarnInvalidHydration) {
    
  2903.       return;
    
  2904.     }
    
  2905.     didWarnInvalidHydration = true;
    
  2906.     console.error(
    
  2907.       'Did not expect server HTML to contain a <%s> in <%s>.',
    
  2908.       child.nodeName.toLowerCase(),
    
  2909.       parentNode.nodeName.toLowerCase(),
    
  2910.     );
    
  2911.   }
    
  2912. }
    
  2913. 
    
  2914. export function warnForDeletedHydratableText(
    
  2915.   parentNode: Element | Document | DocumentFragment,
    
  2916.   child: Text,
    
  2917. ) {
    
  2918.   if (__DEV__) {
    
  2919.     if (didWarnInvalidHydration) {
    
  2920.       return;
    
  2921.     }
    
  2922.     didWarnInvalidHydration = true;
    
  2923.     console.error(
    
  2924.       'Did not expect server HTML to contain the text node "%s" in <%s>.',
    
  2925.       child.nodeValue,
    
  2926.       parentNode.nodeName.toLowerCase(),
    
  2927.     );
    
  2928.   }
    
  2929. }
    
  2930. 
    
  2931. export function warnForInsertedHydratedElement(
    
  2932.   parentNode: Element | Document | DocumentFragment,
    
  2933.   tag: string,
    
  2934.   props: Object,
    
  2935. ) {
    
  2936.   if (__DEV__) {
    
  2937.     if (didWarnInvalidHydration) {
    
  2938.       return;
    
  2939.     }
    
  2940.     didWarnInvalidHydration = true;
    
  2941.     console.error(
    
  2942.       'Expected server HTML to contain a matching <%s> in <%s>.',
    
  2943.       tag,
    
  2944.       parentNode.nodeName.toLowerCase(),
    
  2945.     );
    
  2946.   }
    
  2947. }
    
  2948. 
    
  2949. export function warnForInsertedHydratedText(
    
  2950.   parentNode: Element | Document | DocumentFragment,
    
  2951.   text: string,
    
  2952. ) {
    
  2953.   if (__DEV__) {
    
  2954.     if (text === '') {
    
  2955.       // We expect to insert empty text nodes since they're not represented in
    
  2956.       // the HTML.
    
  2957.       // TODO: Remove this special case if we can just avoid inserting empty
    
  2958.       // text nodes.
    
  2959.       return;
    
  2960.     }
    
  2961.     if (didWarnInvalidHydration) {
    
  2962.       return;
    
  2963.     }
    
  2964.     didWarnInvalidHydration = true;
    
  2965.     console.error(
    
  2966.       'Expected server HTML to contain a matching text node for "%s" in <%s>.',
    
  2967.       text,
    
  2968.       parentNode.nodeName.toLowerCase(),
    
  2969.     );
    
  2970.   }
    
  2971. }
    
  2972. 
    
  2973. export function restoreControlledState(
    
  2974.   domElement: Element,
    
  2975.   tag: string,
    
  2976.   props: Object,
    
  2977. ): void {
    
  2978.   switch (tag) {
    
  2979.     case 'input':
    
  2980.       restoreControlledInputState(domElement, props);
    
  2981.       return;
    
  2982.     case 'textarea':
    
  2983.       restoreControlledTextareaState(domElement, props);
    
  2984.       return;
    
  2985.     case 'select':
    
  2986.       restoreControlledSelectState(domElement, props);
    
  2987.       return;
    
  2988.   }
    
  2989. }