@samitouri / QOS-React-2 / commits / c15579631f

Put common aliases in Map/Set instead of switch over strings (#26551)

This is a follow up to https://github.com/facebook/react/pull/26546 This is strictly a perf optimization since we know that switches over strings aren't optimally implemented in current engines. Basically they're a sequence of ifs. As a result, we're better off putting the unusual cases in a Map and the very common cases in the beginning of the switch. We might be better off putting very common cases in explicit ifs - just in case the engine does optimize switches to a hash table which is potentially worse. --------- Co-authored-by: Sophie Alpert <git@sophiebits.com>

Sebastian Markbåge committed Apr 4, 2023 at 18:06 UTC c15579631ff4d387401d57d9006d849ca1d5cd71
5 files changed +492 -1447
packages/react-dom-bindings/src/client/DOMPropertyOperations.js
+27
@@ -140,6 +140,33 @@ export function setValueForAttribute(
140 }
141 }
142
143 +export function setValueForKnownAttribute(
144 + node: Element,
145 + name: string,
146 + value: mixed,
147 +) {
148 + if (value === null) {
149 + node.removeAttribute(name);
150 + return;
151 + }
152 + switch (typeof value) {
153 + case 'undefined':
154 + case 'function':
155 + case 'symbol':
156 + case 'boolean': {
157 + node.removeAttribute(name);
158 + return;
159 + }
160 + }
161 + if (__DEV__) {
162 + checkAttributeStringCoercion(value, name);
163 + }
164 + node.setAttribute(
165 + name,
166 + enableTrustedTypesIntegration ? (value: any) : '' + (value: any),
167 + );
168 +}
169 +
170 export function setValueForNamespacedAttribute(
171 node: Element,
172 namespace: string,
packages/react-dom-bindings/src/client/ReactDOMComponent.js
+236 -1097
@@ -22,6 +22,7 @@ import {
22 getValueForAttribute,
23 getValueForAttributeOnCustomComponent,
24 setValueForPropertyOnCustomComponent,
25 + setValueForKnownAttribute,
26 setValueForAttribute,
27 setValueForNamespacedAttribute,
28 } from './DOMPropertyOperations';
@@ -58,6 +59,7 @@ import {
59 } from './CSSPropertyOperations';
60 import {HTML_NAMESPACE, getIntrinsicNamespace} from './DOMNamespaces';
61 import isCustomElement from '../shared/isCustomElement';
62 +import getAttributeAlias from '../shared/getAttributeAlias';
63 import possibleStandardNames from '../shared/possibleStandardNames';
64 import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook';
65 import {validateProperties as validateInputProperties} from '../shared/ReactDOMNullInputValuePropHook';
@@ -275,35 +277,6 @@ function setProp(
277 props: any,
278 ): void {
279 switch (key) {
278 - case 'style': {
279 - setValueForStyles(domElement, value);
280 - break;
281 - }
282 - case 'dangerouslySetInnerHTML': {
283 - if (value != null) {
284 - if (typeof value !== 'object' || !('__html' in value)) {
285 - throw new Error(
286 - '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
287 - 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
288 - 'for more information.',
289 - );
290 - }
291 - const nextHtml: any = value.__html;
292 - if (nextHtml != null) {
293 - if (props.children != null) {
294 - throw new Error(
295 - 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
296 - );
297 - }
298 - if (disableIEWorkarounds) {
299 - domElement.innerHTML = nextHtml;
300 - } else {
301 - setInnerHTML(domElement, nextHtml);
302 - }
303 - }
304 - }
305 - break;
306 - }
280 case 'children': {
281 if (typeof value === 'string') {
282 // Avoid setting initial textContent when the text is empty. In IE11 setting
@@ -324,50 +297,26 @@ function setProp(
297 }
298 break;
299 }
327 - case 'onScroll': {
328 - if (value != null) {
329 - if (__DEV__ && typeof value !== 'function') {
330 - warnForInvalidEventListener(key, value);
331 - }
332 - listenToNonDelegatedEvent('scroll', domElement);
333 - }
334 - break;
335 - }
336 - case 'onClick': {
337 - // TODO: This cast may not be sound for SVG, MathML or custom elements.
338 - if (value != null) {
339 - if (__DEV__ && typeof value !== 'function') {
340 - warnForInvalidEventListener(key, value);
341 - }
342 - trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
343 - }
344 - break;
345 - }
346 - // Note: `option.selected` is not updated if `select.multiple` is
347 - // disabled with `removeAttribute`. We have special logic for handling this.
348 - case 'multiple': {
349 - (domElement: any).multiple =
350 - value && typeof value !== 'function' && typeof value !== 'symbol';
300 + // These are very common props and therefore are in the beginning of the switch.
301 + // TODO: aria-label is a very common prop but allows booleans so is not like the others
302 + // but should ideally go in this list too.
303 + case 'className':
304 + setValueForKnownAttribute(domElement, 'class', value);
305 break;
352 - }
353 - case 'muted': {
354 - (domElement: any).muted =
355 - value && typeof value !== 'function' && typeof value !== 'symbol';
306 + case 'tabIndex':
307 + // This has to be case sensitive in SVG.
308 + setValueForKnownAttribute(domElement, 'tabindex', value);
309 break;
357 - }
358 - case 'suppressContentEditableWarning':
359 - case 'suppressHydrationWarning':
360 - case 'defaultValue': // Reserved
361 - case 'defaultChecked':
362 - case 'innerHTML': {
363 - // Noop
310 + case 'dir':
311 + case 'role':
312 + case 'viewBox':
313 + case 'width':
314 + case 'height': {
315 + setValueForKnownAttribute(domElement, key, value);
316 break;
317 }
366 - case 'autoFocus': {
367 - // We polyfill it separately on the client during commit.
368 - // We could have excluded it in the property list instead of
369 - // adding a special case here, but then it wouldn't be emitted
370 - // on server rendering (but we *do* want to emit it in SSR).
318 + case 'style': {
319 + setValueForStyles(domElement, value);
320 break;
321 }
322 // These attributes accept URLs. These must not allow javascript: URLS.
@@ -423,6 +372,77 @@ function setProp(
372 domElement.setAttribute(key, sanitizedValue);
373 break;
374 }
375 + case 'onClick': {
376 + // TODO: This cast may not be sound for SVG, MathML or custom elements.
377 + if (value != null) {
378 + if (__DEV__ && typeof value !== 'function') {
379 + warnForInvalidEventListener(key, value);
380 + }
381 + trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
382 + }
383 + break;
384 + }
385 + case 'onScroll': {
386 + if (value != null) {
387 + if (__DEV__ && typeof value !== 'function') {
388 + warnForInvalidEventListener(key, value);
389 + }
390 + listenToNonDelegatedEvent('scroll', domElement);
391 + }
392 + break;
393 + }
394 + case 'dangerouslySetInnerHTML': {
395 + if (value != null) {
396 + if (typeof value !== 'object' || !('__html' in value)) {
397 + throw new Error(
398 + '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
399 + 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
400 + 'for more information.',
401 + );
402 + }
403 + const nextHtml: any = value.__html;
404 + if (nextHtml != null) {
405 + if (props.children != null) {
406 + throw new Error(
407 + 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
408 + );
409 + }
410 + if (disableIEWorkarounds) {
411 + domElement.innerHTML = nextHtml;
412 + } else {
413 + setInnerHTML(domElement, nextHtml);
414 + }
415 + }
416 + }
417 + break;
418 + }
419 + // Note: `option.selected` is not updated if `select.multiple` is
420 + // disabled with `removeAttribute`. We have special logic for handling this.
421 + case 'multiple': {
422 + (domElement: any).multiple =
423 + value && typeof value !== 'function' && typeof value !== 'symbol';
424 + break;
425 + }
426 + case 'muted': {
427 + (domElement: any).muted =
428 + value && typeof value !== 'function' && typeof value !== 'symbol';
429 + break;
430 + }
431 + case 'suppressContentEditableWarning':
432 + case 'suppressHydrationWarning':
433 + case 'defaultValue': // Reserved
434 + case 'defaultChecked':
435 + case 'innerHTML': {
436 + // Noop
437 + break;
438 + }
439 + case 'autoFocus': {
440 + // We polyfill it separately on the client during commit.
441 + // We could have excluded it in the property list instead of
442 + // adding a special case here, but then it wouldn't be emitted
443 + // on server rendering (but we *do* want to emit it in SSR).
444 + break;
445 + }
446 case 'xlinkHref': {
447 if (
448 value == null ||
@@ -565,251 +585,6 @@ function setProp(
585 }
586 break;
587 }
568 - // A few React string attributes have a different name.
569 - // This is a mapping from React prop names to the attribute names.
570 - case 'acceptCharset':
571 - setValueForAttribute(domElement, 'accept-charset', value);
572 - break;
573 - case 'className':
574 - setValueForAttribute(domElement, 'class', value);
575 - break;
576 - case 'htmlFor':
577 - setValueForAttribute(domElement, 'for', value);
578 - break;
579 - case 'httpEquiv':
580 - setValueForAttribute(domElement, 'http-equiv', value);
581 - break;
582 - // HTML and SVG attributes, but the SVG attribute is case sensitive.
583 - case 'tabIndex':
584 - setValueForAttribute(domElement, 'tabindex', value);
585 - break;
586 - case 'crossOrigin':
587 - setValueForAttribute(domElement, 'crossorigin', value);
588 - break;
589 - // This is a list of all SVG attributes that need special casing.
590 - // Regular attributes that just accept strings.
591 - case 'accentHeight':
592 - setValueForAttribute(domElement, 'accent-height', value);
593 - break;
594 - case 'alignmentBaseline':
595 - setValueForAttribute(domElement, 'alignment-baseline', value);
596 - break;
597 - case 'arabicForm':
598 - setValueForAttribute(domElement, 'arabic-form', value);
599 - break;
600 - case 'baselineShift':
601 - setValueForAttribute(domElement, 'baseline-shift', value);
602 - break;
603 - case 'capHeight':
604 - setValueForAttribute(domElement, 'cap-height', value);
605 - break;
606 - case 'clipPath':
607 - setValueForAttribute(domElement, 'clip-path', value);
608 - break;
609 - case 'clipRule':
610 - setValueForAttribute(domElement, 'clip-rule', value);
611 - break;
612 - case 'colorInterpolation':
613 - setValueForAttribute(domElement, 'color-interpolation', value);
614 - break;
615 - case 'colorInterpolationFilters':
616 - setValueForAttribute(domElement, 'color-interpolation-filters', value);
617 - break;
618 - case 'colorProfile':
619 - setValueForAttribute(domElement, 'color-profile', value);
620 - break;
621 - case 'colorRendering':
622 - setValueForAttribute(domElement, 'color-rendering', value);
623 - break;
624 - case 'dominantBaseline':
625 - setValueForAttribute(domElement, 'dominant-baseline', value);
626 - break;
627 - case 'enableBackground':
628 - setValueForAttribute(domElement, 'enable-background', value);
629 - break;
630 - case 'fillOpacity':
631 - setValueForAttribute(domElement, 'fill-opacity', value);
632 - break;
633 - case 'fillRule':
634 - setValueForAttribute(domElement, 'fill-rule', value);
635 - break;
636 - case 'floodColor':
637 - setValueForAttribute(domElement, 'flood-color', value);
638 - break;
639 - case 'floodOpacity':
640 - setValueForAttribute(domElement, 'flood-opacity', value);
641 - break;
642 - case 'fontFamily':
643 - setValueForAttribute(domElement, 'font-family', value);
644 - break;
645 - case 'fontSize':
646 - setValueForAttribute(domElement, 'font-size', value);
647 - break;
648 - case 'fontSizeAdjust':
649 - setValueForAttribute(domElement, 'font-size-adjust', value);
650 - break;
651 - case 'fontStretch':
652 - setValueForAttribute(domElement, 'font-stretch', value);
653 - break;
654 - case 'fontStyle':
655 - setValueForAttribute(domElement, 'font-style', value);
656 - break;
657 - case 'fontVariant':
658 - setValueForAttribute(domElement, 'font-variant', value);
659 - break;
660 - case 'fontWeight':
661 - setValueForAttribute(domElement, 'font-weight', value);
662 - break;
663 - case 'glyphName':
664 - setValueForAttribute(domElement, 'glyph-name', value);
665 - break;
666 - case 'glyphOrientationHorizontal':
667 - setValueForAttribute(domElement, 'glyph-orientation-horizontal', value);
668 - break;
669 - case 'glyphOrientationVertical':
670 - setValueForAttribute(domElement, 'glyph-orientation-vertical', value);
671 - break;
672 - case 'horizAdvX':
673 - setValueForAttribute(domElement, 'horiz-adv-x', value);
674 - break;
675 - case 'horizOriginX':
676 - setValueForAttribute(domElement, 'horiz-origin-x', value);
677 - break;
678 - case 'imageRendering':
679 - setValueForAttribute(domElement, 'image-rendering', value);
680 - break;
681 - case 'letterSpacing':
682 - setValueForAttribute(domElement, 'letter-spacing', value);
683 - break;
684 - case 'lightingColor':
685 - setValueForAttribute(domElement, 'lighting-color', value);
686 - break;
687 - case 'markerEnd':
688 - setValueForAttribute(domElement, 'marker-end', value);
689 - break;
690 - case 'markerMid':
691 - setValueForAttribute(domElement, 'marker-mid', value);
692 - break;
693 - case 'markerStart':
694 - setValueForAttribute(domElement, 'marker-start', value);
695 - break;
696 - case 'overlinePosition':
697 - setValueForAttribute(domElement, 'overline-position', value);
698 - break;
699 - case 'overlineThickness':
700 - setValueForAttribute(domElement, 'overline-thickness', value);
701 - break;
702 - case 'paintOrder':
703 - setValueForAttribute(domElement, 'paint-order', value);
704 - break;
705 - case 'panose-1':
706 - setValueForAttribute(domElement, 'panose-1', value);
707 - break;
708 - case 'pointerEvents':
709 - setValueForAttribute(domElement, 'pointer-events', value);
710 - break;
711 - case 'renderingIntent':
712 - setValueForAttribute(domElement, 'rendering-intent', value);
713 - break;
714 - case 'shapeRendering':
715 - setValueForAttribute(domElement, 'shape-rendering', value);
716 - break;
717 - case 'stopColor':
718 - setValueForAttribute(domElement, 'stop-color', value);
719 - break;
720 - case 'stopOpacity':
721 - setValueForAttribute(domElement, 'stop-opacity', value);
722 - break;
723 - case 'strikethroughPosition':
724 - setValueForAttribute(domElement, 'strikethrough-position', value);
725 - break;
726 - case 'strikethroughThickness':
727 - setValueForAttribute(domElement, 'strikethrough-thickness', value);
728 - break;
729 - case 'strokeDasharray':
730 - setValueForAttribute(domElement, 'stroke-dasharray', value);
731 - break;
732 - case 'strokeDashoffset':
733 - setValueForAttribute(domElement, 'stroke-dashoffset', value);
734 - break;
735 - case 'strokeLinecap':
736 - setValueForAttribute(domElement, 'stroke-linecap', value);
737 - break;
738 - case 'strokeLinejoin':
739 - setValueForAttribute(domElement, 'stroke-linejoin', value);
740 - break;
741 - case 'strokeMiterlimit':
742 - setValueForAttribute(domElement, 'stroke-miterlimit', value);
743 - break;
744 - case 'strokeOpacity':
745 - setValueForAttribute(domElement, 'stroke-opacity', value);
746 - break;
747 - case 'strokeWidth':
748 - setValueForAttribute(domElement, 'stroke-width', value);
749 - break;
750 - case 'textAnchor':
751 - setValueForAttribute(domElement, 'text-anchor', value);
752 - break;
753 - case 'textDecoration':
754 - setValueForAttribute(domElement, 'text-decoration', value);
755 - break;
756 - case 'textRendering':
757 - setValueForAttribute(domElement, 'text-rendering', value);
758 - break;
759 - case 'transformOrigin':
760 - setValueForAttribute(domElement, 'transform-origin', value);
761 - break;
762 - case 'underlinePosition':
763 - setValueForAttribute(domElement, 'underline-position', value);
764 - break;
765 - case 'underlineThickness':
766 - setValueForAttribute(domElement, 'underline-thickness', value);
767 - break;
768 - case 'unicodeBidi':
769 - setValueForAttribute(domElement, 'unicode-bidi', value);
770 - break;
771 - case 'unicodeRange':
772 - setValueForAttribute(domElement, 'unicode-range', value);
773 - break;
774 - case 'unitsPerEm':
775 - setValueForAttribute(domElement, 'units-per-em', value);
776 - break;
777 - case 'vAlphabetic':
778 - setValueForAttribute(domElement, 'v-alphabetic', value);
779 - break;
780 - case 'vHanging':
781 - setValueForAttribute(domElement, 'v-hanging', value);
782 - break;
783 - case 'vIdeographic':
784 - setValueForAttribute(domElement, 'v-ideographic', value);
785 - break;
786 - case 'vMathematical':
787 - setValueForAttribute(domElement, 'v-mathematical', value);
788 - break;
789 - case 'vectorEffect':
790 - setValueForAttribute(domElement, 'vector-effect', value);
791 - break;
792 - case 'vertAdvY':
793 - setValueForAttribute(domElement, 'vert-adv-y', value);
794 - break;
795 - case 'vertOriginX':
796 - setValueForAttribute(domElement, 'vert-origin-x', value);
797 - break;
798 - case 'vertOriginY':
799 - setValueForAttribute(domElement, 'vert-origin-y', value);
800 - break;
801 - case 'wordSpacing':
802 - setValueForAttribute(domElement, 'word-spacing', value);
803 - break;
804 - case 'writingMode':
805 - setValueForAttribute(domElement, 'writing-mode', value);
806 - break;
807 - case 'xmlnsXlink':
808 - setValueForAttribute(domElement, 'xmlns:xlink', value);
809 - break;
810 - case 'xHeight':
811 - setValueForAttribute(domElement, 'x-height', value);
812 - break;
588 case 'xlinkActuate':
589 setValueForNamespacedAttribute(
590 domElement,
@@ -904,7 +679,8 @@ function setProp(
679 warnForInvalidEventListener(key, value);
680 }
681 } else {
907 - setValueForAttribute(domElement, key, value);
682 + const attributeName = getAttributeAlias(key);
683 + setValueForAttribute(domElement, attributeName, value);
684 }
685 }
686 }
@@ -1018,6 +794,17 @@ export function setInitialProperties(
794 // TODO: Make sure that we check isMounted before firing any of these events.
795
796 switch (tag) {
797 + case 'div':
798 + case 'span':
799 + case 'svg':
800 + case 'path':
801 + case 'a':
802 + case 'g':
803 + case 'p':
804 + case 'li': {
805 + // Fast track the most common tag types
806 + break;
807 + }
808 case 'input': {
809 ReactDOMInputInitWrapperState(domElement, props);
810 // We listen to this event in case to ensure emulated bubble
@@ -1032,6 +819,21 @@ export function setInitialProperties(
819 continue;
820 }
821 switch (propKey) {
822 + case 'type': {
823 + // Fast path since 'type' is very common on inputs
824 + if (
825 + propValue != null &&
826 + typeof propValue !== 'function' &&
827 + typeof propValue !== 'symbol' &&
828 + typeof propValue !== 'boolean'
829 + ) {
830 + if (__DEV__) {
831 + checkAttributeStringCoercion(propValue, propKey);
832 + }
833 + domElement.setAttribute(propKey, propValue);
834 + }
835 + break;
836 + }
837 case 'checked': {
838 const node = ((domElement: any): InputWithWrapperState);
839 const checked =
@@ -1246,30 +1048,32 @@ export function setInitialProperties(
1048 }
1049 return;
1050 }
1051 + default: {
1052 + if (isCustomElement(tag, props)) {
1053 + for (const propKey in props) {
1054 + if (!props.hasOwnProperty(propKey)) {
1055 + continue;
1056 + }
1057 + const propValue = props[propKey];
1058 + if (propValue == null) {
1059 + continue;
1060 + }
1061 + setPropOnCustomElement(domElement, tag, propKey, propValue, props);
1062 + }
1063 + return;
1064 + }
1065 + }
1066 }
1067
1251 - if (isCustomElement(tag, props)) {
1252 - for (const propKey in props) {
1253 - if (!props.hasOwnProperty(propKey)) {
1254 - continue;
1255 - }
1256 - const propValue = props[propKey];
1257 - if (propValue == null) {
1258 - continue;
1259 - }
1260 - setPropOnCustomElement(domElement, tag, propKey, propValue, props);
1068 + for (const propKey in props) {
1069 + if (!props.hasOwnProperty(propKey)) {
1070 + continue;
1071 }
1262 - } else {
1263 - for (const propKey in props) {
1264 - if (!props.hasOwnProperty(propKey)) {
1265 - continue;
1266 - }
1267 - const propValue = props[propKey];
1268 - if (propValue == null) {
1269 - continue;
1270 - }
1271 - setProp(domElement, tag, propKey, propValue, props);
1072 + const propValue = props[propKey];
1073 + if (propValue == null) {
1074 + continue;
1075 }
1076 + setProp(domElement, tag, propKey, propValue, props);
1077 }
1078 }
1079
@@ -1395,7 +1199,18 @@ export function updateProperties(
1199 nextProps: Object,
1200 ): void {
1201 switch (tag) {
1398 - case 'input': {
1202 + case 'div':
1203 + case 'span':
1204 + case 'svg':
1205 + case 'path':
1206 + case 'a':
1207 + case 'g':
1208 + case 'p':
1209 + case 'li': {
1210 + // Fast track the most common tag types
1211 + break;
1212 + }
1213 + case 'input': {
1214 // Update checked *before* name.
1215 // In the middle of an update, it is possible to have multiple checked.
1216 // When a checked radio tries to change name, browser makes another radio's checked false.
@@ -1552,21 +1367,29 @@ export function updateProperties(
1367 }
1368 return;
1369 }
1370 + default: {
1371 + if (isCustomElement(tag, nextProps)) {
1372 + for (let i = 0; i < updatePayload.length; i += 2) {
1373 + const propKey = updatePayload[i];
1374 + const propValue = updatePayload[i + 1];
1375 + setPropOnCustomElement(
1376 + domElement,
1377 + tag,
1378 + propKey,
1379 + propValue,
1380 + nextProps,
1381 + );
1382 + }
1383 + return;
1384 + }
1385 + }
1386 }
1387
1388 // Apply the diff.
1558 - if (isCustomElement(tag, nextProps)) {
1559 - for (let i = 0; i < updatePayload.length; i += 2) {
1560 - const propKey = updatePayload[i];
1561 - const propValue = updatePayload[i + 1];
1562 - setPropOnCustomElement(domElement, tag, propKey, propValue, nextProps);
1563 - }
1564 - } else {
1565 - for (let i = 0; i < updatePayload.length; i += 2) {
1566 - const propKey = updatePayload[i];
1567 - const propValue = updatePayload[i + 1];
1568 - setProp(domElement, tag, propKey, propValue, nextProps);
1569 - }
1389 + for (let i = 0; i < updatePayload.length; i += 2) {
1390 + const propKey = updatePayload[i];
1391 + const propValue = updatePayload[i + 1];
1392 + setProp(domElement, tag, propKey, propValue, nextProps);
1393 }
1394 }
1395
@@ -2048,6 +1871,18 @@ function diffHydratedGenericElement(
1871 warnForPropDifference(propKey, serverHTML, expectedHTML);
1872 }
1873 continue;
1874 + case 'className':
1875 + hydrateAttribute(domElement, propKey, 'class', value, extraAttributes);
1876 + continue;
1877 + case 'tabIndex':
1878 + hydrateAttribute(
1879 + domElement,
1880 + propKey,
1881 + 'tabindex',
1882 + value,
1883 + extraAttributes,
1884 + );
1885 + continue;
1886 case 'style':
1887 extraAttributes.delete(propKey);
1888 diffHydratedStyles(domElement, value);
@@ -2244,828 +2079,132 @@ function diffHydratedGenericElement(
2079 );
2080 continue;
2081 }
2247 - // A few React string attributes have a different name.
2248 - // This is a mapping from React prop names to the attribute names.
2249 - case 'acceptCharset':
2250 - hydrateAttribute(
2251 - domElement,
2252 - propKey,
2253 - 'accept-charset',
2254 - value,
2255 - extraAttributes,
2256 - );
2257 - continue;
2258 - case 'className':
2259 - hydrateAttribute(domElement, propKey, 'class', value, extraAttributes);
2260 - continue;
2261 - case 'htmlFor':
2262 - hydrateAttribute(domElement, propKey, 'for', value, extraAttributes);
2263 - continue;
2264 - case 'httpEquiv':
2265 - hydrateAttribute(
2266 - domElement,
2267 - propKey,
2268 - 'http-equiv',
2269 - value,
2270 - extraAttributes,
2271 - );
2272 - continue;
2273 - case 'tabIndex':
2274 - hydrateAttribute(
2275 - domElement,
2276 - propKey,
2277 - 'tabindex',
2278 - value,
2279 - extraAttributes,
2280 - );
2281 - continue;
2282 - case 'crossOrigin':
2283 - hydrateAttribute(
2284 - domElement,
2285 - propKey,
2286 - 'crossorigin',
2287 - value,
2288 - extraAttributes,
2289 - );
2290 - continue;
2291 - case 'accentHeight':
2292 - hydrateAttribute(
2293 - domElement,
2294 - propKey,
2295 - 'accent-height',
2296 - value,
2297 - extraAttributes,
2298 - );
2299 - continue;
2300 - case 'alignmentBaseline':
2301 - hydrateAttribute(
2302 - domElement,
2303 - propKey,
2304 - 'alignment-baseline',
2305 - value,
2306 - extraAttributes,
2307 - );
2308 - continue;
2309 - case 'arabicForm':
2310 - hydrateAttribute(
2311 - domElement,
2312 - propKey,
2313 - 'arabic-form',
2314 - value,
2315 - extraAttributes,
2316 - );
2317 - continue;
2318 - case 'baselineShift':
2319 - hydrateAttribute(
2320 - domElement,
2321 - propKey,
2322 - 'baseline-shift',
2323 - value,
2324 - extraAttributes,
2325 - );
2326 - continue;
2327 - case 'capHeight':
2328 - hydrateAttribute(
2329 - domElement,
2330 - propKey,
2331 - 'cap-height',
2332 - value,
2333 - extraAttributes,
2334 - );
2335 - continue;
2336 - case 'clipPath':
2337 - hydrateAttribute(
2338 - domElement,
2339 - propKey,
2340 - 'clip-path',
2341 - value,
2342 - extraAttributes,
2343 - );
2344 - continue;
2345 - case 'clipRule':
2346 - hydrateAttribute(
2347 - domElement,
2348 - propKey,
2349 - 'clip-rule',
2350 - value,
2351 - extraAttributes,
2352 - );
2353 - continue;
2354 - case 'colorInterpolation':
2355 - hydrateAttribute(
2356 - domElement,
2357 - propKey,
2358 - 'color-interpolation',
2359 - value,
2360 - extraAttributes,
2361 - );
2362 - continue;
2363 - case 'colorInterpolationFilters':
2364 - hydrateAttribute(
2365 - domElement,
2366 - propKey,
2367 - 'color-interpolation-filters',
2368 - value,
2369 - extraAttributes,
2370 - );
2371 - continue;
2372 - case 'colorProfile':
2373 - hydrateAttribute(
2374 - domElement,
2375 - propKey,
2376 - 'color-profile',
2377 - value,
2378 - extraAttributes,
2379 - );
2380 - continue;
2381 - case 'colorRendering':
2382 - hydrateAttribute(
2383 - domElement,
2384 - propKey,
2385 - 'color-rendering',
2386 - value,
2387 - extraAttributes,
2388 - );
2389 - continue;
2390 - case 'dominantBaseline':
2391 - hydrateAttribute(
2392 - domElement,
2393 - propKey,
2394 - 'dominant-baseline',
2395 - value,
2396 - extraAttributes,
2397 - );
2398 - continue;
2399 - case 'enableBackground':
2400 - hydrateAttribute(
2401 - domElement,
2402 - propKey,
2403 - 'enable-background',
2404 - value,
2405 - extraAttributes,
2406 - );
2407 - continue;
2408 - case 'fillOpacity':
2409 - hydrateAttribute(
2410 - domElement,
2411 - propKey,
2412 - 'fill-opacity',
2413 - value,
2414 - extraAttributes,
2415 - );
2416 - continue;
2417 - case 'fillRule':
2418 - hydrateAttribute(
2419 - domElement,
2420 - propKey,
2421 - 'fill-rule',
2422 - value,
2423 - extraAttributes,
2424 - );
2425 - continue;
2426 - case 'floodColor':
2427 - hydrateAttribute(
2428 - domElement,
2429 - propKey,
2430 - 'flood-color',
2431 - value,
2432 - extraAttributes,
2433 - );
2434 - continue;
2435 - case 'floodOpacity':
2436 - hydrateAttribute(
2437 - domElement,
2438 - propKey,
2439 - 'flood-opacity',
2440 - value,
2441 - extraAttributes,
2442 - );
2443 - continue;
2444 - case 'fontFamily':
2445 - hydrateAttribute(
2446 - domElement,
2447 - propKey,
2448 - 'font-family',
2449 - value,
2450 - extraAttributes,
2451 - );
2452 - continue;
2453 - case 'fontSize':
2454 - hydrateAttribute(
2455 - domElement,
2456 - propKey,
2457 - 'font-size',
2458 - value,
2459 - extraAttributes,
2460 - );
2461 - continue;
2462 - case 'fontSizeAdjust':
2463 - hydrateAttribute(
2464 - domElement,
2465 - propKey,
2466 - 'font-size-adjust',
2467 - value,
2468 - extraAttributes,
2469 - );
2470 - continue;
2471 - case 'fontStretch':
2082 + case 'xHeight':
2083 hydrateAttribute(
2084 domElement,
2085 propKey,
2475 - 'font-stretch',
2086 + 'x-height',
2087 value,
2088 extraAttributes,
2089 );
2090 continue;
2480 - case 'fontStyle':
2091 + case 'xlinkActuate':
2092 hydrateAttribute(
2093 domElement,
2094 propKey,
2484 - 'font-style',
2095 + 'xlink:actuate',
2096 value,
2097 extraAttributes,
2098 );
2099 continue;
2489 - case 'fontVariant':
2100 + case 'xlinkArcrole':
2101 hydrateAttribute(
2102 domElement,
2103 propKey,
2493 - 'font-variant',
2104 + 'xlink:arcrole',
2105 value,
2106 extraAttributes,
2107 );
2108 continue;
2498 - case 'fontWeight':
2109 + case 'xlinkRole':
2110 hydrateAttribute(
2111 domElement,
2112 propKey,
2502 - 'font-weight',
2113 + 'xlink:role',
2114 value,
2115 extraAttributes,
2116 );
2117 continue;
2507 - case 'glyphName':
2118 + case 'xlinkShow':
2119 hydrateAttribute(
2120 domElement,
2121 propKey,
2511 - 'glyph-name',
2122 + 'xlink:show',
2123 value,
2124 extraAttributes,
2125 );
2126 continue;
2516 - case 'glyphOrientationHorizontal':
2127 + case 'xlinkTitle':
2128 hydrateAttribute(
2129 domElement,
2130 propKey,
2520 - 'glyph-orientation-horizontal',
2131 + 'xlink:title',
2132 value,
2133 extraAttributes,
2134 );
2135 continue;
2525 - case 'glyphOrientationVertical':
2136 + case 'xlinkType':
2137 hydrateAttribute(
2138 domElement,
2139 propKey,
2529 - 'glyph-orientation-vertical',
2140 + 'xlink:type',
2141 value,
2142 extraAttributes,
2143 );
2144 continue;
2534 - case 'horizAdvX':
2145 + case 'xmlBase':
2146 hydrateAttribute(
2147 domElement,
2148 propKey,
2538 - 'horiz-adv-x',
2149 + 'xml:base',
2150 value,
2151 extraAttributes,
2152 );
2153 continue;
2543 - case 'horizOriginX':
2154 + case 'xmlLang':
2155 hydrateAttribute(
2156 domElement,
2157 propKey,
2547 - 'horiz-origin-x',
2158 + 'xml:lang',
2159 value,
2160 extraAttributes,
2161 );
2162 continue;
2552 - case 'imageRendering':
2163 + case 'xmlSpace':
2164 hydrateAttribute(
2165 domElement,
2166 propKey,
2556 - 'image-rendering',
2167 + 'xml:space',
2168 value,
2169 extraAttributes,
2170 );
2171 continue;
2561 - case 'letterSpacing':
2562 - hydrateAttribute(
2172 + default: {
2173 + if (
2174 + // shouldIgnoreAttribute
2175 + // We have already filtered out null/undefined and reserved words.
2176 + propKey.length > 2 &&
2177 + (propKey[0] === 'o' || propKey[0] === 'O') &&
2178 + (propKey[1] === 'n' || propKey[1] === 'N')
2179 + ) {
2180 + continue;
2181 + }
2182 + const attributeName = getAttributeAlias(propKey);
2183 + let isMismatchDueToBadCasing = false;
2184 + let ownNamespaceDev = parentNamespaceDev;
2185 + if (ownNamespaceDev === HTML_NAMESPACE) {
2186 + ownNamespaceDev = getIntrinsicNamespace(tag);
2187 + }
2188 + if (ownNamespaceDev === HTML_NAMESPACE) {
2189 + extraAttributes.delete(attributeName.toLowerCase());
2190 + } else {
2191 + const standardName = getPossibleStandardName(propKey);
2192 + if (standardName !== null && standardName !== propKey) {
2193 + // If an SVG prop is supplied with bad casing, it will
2194 + // be successfully parsed from HTML, but will produce a mismatch
2195 + // (and would be incorrectly rendered on the client).
2196 + // However, we already warn about bad casing elsewhere.
2197 + // So we'll skip the misleading extra mismatch warning in this case.
2198 + isMismatchDueToBadCasing = true;
2199 + extraAttributes.delete(standardName);
2200 + }
2201 + extraAttributes.delete(attributeName);
2202 + }
2203 + const serverValue = getValueForAttribute(
2204 domElement,
2564 - propKey,
2565 - 'letter-spacing',
2205 + attributeName,
2206 value,
2567 - extraAttributes,
2207 );
2569 - continue;
2570 - case 'lightingColor':
2571 - hydrateAttribute(
2572 - domElement,
2573 - propKey,
2574 - 'lighting-color',
2575 - value,
2576 - extraAttributes,
2577 - );
2578 - continue;
2579 - case 'markerEnd':
2580 - hydrateAttribute(
2581 - domElement,
2582 - propKey,
2583 - 'marker-end',
2584 - value,
2585 - extraAttributes,
2586 - );
2587 - continue;
2588 - case 'markerMid':
2589 - hydrateAttribute(
2590 - domElement,
2591 - propKey,
2592 - 'marker-mid',
2593 - value,
2594 - extraAttributes,
2595 - );
2596 - continue;
2597 - case 'markerStart':
2598 - hydrateAttribute(
2599 - domElement,
2600 - propKey,
2601 - 'marker-start',
2602 - value,
2603 - extraAttributes,
2604 - );
2605 - continue;
2606 - case 'overlinePosition':
2607 - hydrateAttribute(
2608 - domElement,
2609 - propKey,
2610 - 'overline-position',
2611 - value,
2612 - extraAttributes,
2613 - );
2614 - continue;
2615 - case 'overlineThickness':
2616 - hydrateAttribute(
2617 - domElement,
2618 - propKey,
2619 - 'overline-thickness',
2620 - value,
2621 - extraAttributes,
2622 - );
2623 - continue;
2624 - case 'paintOrder':
2625 - hydrateAttribute(
2626 - domElement,
2627 - propKey,
2628 - 'paint-order',
2629 - value,
2630 - extraAttributes,
2631 - );
2632 - continue;
2633 - case 'panose-1':
2634 - hydrateAttribute(
2635 - domElement,
2636 - propKey,
2637 - 'panose-1',
2638 - value,
2639 - extraAttributes,
2640 - );
2641 - continue;
2642 - case 'pointerEvents':
2643 - hydrateAttribute(
2644 - domElement,
2645 - propKey,
2646 - 'pointer-events',
2647 - value,
2648 - extraAttributes,
2649 - );
2650 - continue;
2651 - case 'renderingIntent':
2652 - hydrateAttribute(
2653 - domElement,
2654 - propKey,
2655 - 'rendering-intent',
2656 - value,
2657 - extraAttributes,
2658 - );
2659 - continue;
2660 - case 'shapeRendering':
2661 - hydrateAttribute(
2662 - domElement,
2663 - propKey,
2664 - 'shape-rendering',
2665 - value,
2666 - extraAttributes,
2667 - );
2668 - continue;
2669 - case 'stopColor':
2670 - hydrateAttribute(
2671 - domElement,
2672 - propKey,
2673 - 'stop-color',
2674 - value,
2675 - extraAttributes,
2676 - );
2677 - continue;
2678 - case 'stopOpacity':
2679 - hydrateAttribute(
2680 - domElement,
2681 - propKey,
2682 - 'stop-opacity',
2683 - value,
2684 - extraAttributes,
2685 - );
2686 - continue;
2687 - case 'strikethroughPosition':
2688 - hydrateAttribute(
2689 - domElement,
2690 - propKey,
2691 - 'strikethrough-position',
2692 - value,
2693 - extraAttributes,
2694 - );
2695 - continue;
2696 - case 'strikethroughThickness':
2697 - hydrateAttribute(
2698 - domElement,
2699 - propKey,
2700 - 'strikethrough-thickness',
2701 - value,
2702 - extraAttributes,
2703 - );
2704 - continue;
2705 - case 'strokeDasharray':
2706 - hydrateAttribute(
2707 - domElement,
2708 - propKey,
2709 - 'stroke-dasharray',
2710 - value,
2711 - extraAttributes,
2712 - );
2713 - continue;
2714 - case 'strokeDashoffset':
2715 - hydrateAttribute(
2716 - domElement,
2717 - propKey,
2718 - 'stroke-dashoffset',
2719 - value,
2720 - extraAttributes,
2721 - );
2722 - continue;
2723 - case 'strokeLinecap':
2724 - hydrateAttribute(
2725 - domElement,
2726 - propKey,
2727 - 'stroke-linecap',
2728 - value,
2729 - extraAttributes,
2730 - );
2731 - continue;
2732 - case 'strokeLinejoin':
2733 - hydrateAttribute(
2734 - domElement,
2735 - propKey,
2736 - 'stroke-linejoin',
2737 - value,
2738 - extraAttributes,
2739 - );
2740 - continue;
2741 - case 'strokeMiterlimit':
2742 - hydrateAttribute(
2743 - domElement,
2744 - propKey,
2745 - 'stroke-miterlimit',
2746 - value,
2747 - extraAttributes,
2748 - );
2749 - continue;
2750 - case 'strokeOpacity':
2751 - hydrateAttribute(
2752 - domElement,
2753 - propKey,
2754 - 'stroke-opacity',
2755 - value,
2756 - extraAttributes,
2757 - );
2758 - continue;
2759 - case 'strokeWidth':
2760 - hydrateAttribute(
2761 - domElement,
2762 - propKey,
2763 - 'stroke-width',
2764 - value,
2765 - extraAttributes,
2766 - );
2767 - continue;
2768 - case 'textAnchor':
2769 - hydrateAttribute(
2770 - domElement,
2771 - propKey,
2772 - 'text-anchor',
2773 - value,
2774 - extraAttributes,
2775 - );
2776 - continue;
2777 - case 'textDecoration':
2778 - hydrateAttribute(
2779 - domElement,
2780 - propKey,
2781 - 'text-decoration',
2782 - value,
2783 - extraAttributes,
2784 - );
2785 - continue;
2786 - case 'textRendering':
2787 - hydrateAttribute(
2788 - domElement,
2789 - propKey,
2790 - 'text-rendering',
2791 - value,
2792 - extraAttributes,
2793 - );
2794 - continue;
2795 - case 'transformOrigin':
2796 - hydrateAttribute(
2797 - domElement,
2798 - propKey,
2799 - 'transform-origin',
2800 - value,
2801 - extraAttributes,
2802 - );
2803 - continue;
2804 - case 'underlinePosition':
2805 - hydrateAttribute(
2806 - domElement,
2807 - propKey,
2808 - 'underline-position',
2809 - value,
2810 - extraAttributes,
2811 - );
2812 - continue;
2813 - case 'underlineThickness':
2814 - hydrateAttribute(
2815 - domElement,
2816 - propKey,
2817 - 'underline-thickness',
2818 - value,
2819 - extraAttributes,
2820 - );
2821 - continue;
2822 - case 'unicodeBidi':
2823 - hydrateAttribute(
2824 - domElement,
2825 - propKey,
2826 - 'unicode-bidi',
2827 - value,
2828 - extraAttributes,
2829 - );
2830 - continue;
2831 - case 'unicodeRange':
2832 - hydrateAttribute(
2833 - domElement,
2834 - propKey,
2835 - 'unicode-range',
2836 - value,
2837 - extraAttributes,
2838 - );
2839 - continue;
2840 - case 'unitsPerEm':
2841 - hydrateAttribute(
2842 - domElement,
2843 - propKey,
2844 - 'units-per-em',
2845 - value,
2846 - extraAttributes,
2847 - );
2848 - continue;
2849 - case 'vAlphabetic':
2850 - hydrateAttribute(
2851 - domElement,
2852 - propKey,
2853 - 'v-alphabetic',
2854 - value,
2855 - extraAttributes,
2856 - );
2857 - continue;
2858 - case 'vHanging':
2859 - hydrateAttribute(
2860 - domElement,
2861 - propKey,
2862 - 'v-hanging',
2863 - value,
2864 - extraAttributes,
2865 - );
2866 - continue;
2867 - case 'vIdeographic':
2868 - hydrateAttribute(
2869 - domElement,
2870 - propKey,
2871 - 'v-ideographic',
2872 - value,
2873 - extraAttributes,
2874 - );
2875 - continue;
2876 - case 'vMathematical':
2877 - hydrateAttribute(
2878 - domElement,
2879 - propKey,
2880 - 'v-mathematical',
2881 - value,
2882 - extraAttributes,
2883 - );
2884 - continue;
2885 - case 'vectorEffect':
2886 - hydrateAttribute(
2887 - domElement,
2888 - propKey,
2889 - 'vector-effect',
2890 - value,
2891 - extraAttributes,
2892 - );
2893 - continue;
2894 - case 'vertAdvY':
2895 - hydrateAttribute(
2896 - domElement,
2897 - propKey,
2898 - 'vert-adv-y',
2899 - value,
2900 - extraAttributes,
2901 - );
2902 - continue;
2903 - case 'vertOriginX':
2904 - hydrateAttribute(
2905 - domElement,
2906 - propKey,
2907 - 'vert-origin-x',
2908 - value,
2909 - extraAttributes,
2910 - );
2911 - continue;
2912 - case 'vertOriginY':
2913 - hydrateAttribute(
2914 - domElement,
2915 - propKey,
2916 - 'vert-origin-y',
2917 - value,
2918 - extraAttributes,
2919 - );
2920 - continue;
2921 - case 'wordSpacing':
2922 - hydrateAttribute(
2923 - domElement,
2924 - propKey,
2925 - 'word-spacing',
2926 - value,
2927 - extraAttributes,
2928 - );
2929 - continue;
2930 - case 'writingMode':
2931 - hydrateAttribute(
2932 - domElement,
2933 - propKey,
2934 - 'writing-mode',
2935 - value,
2936 - extraAttributes,
2937 - );
2938 - continue;
2939 - case 'xmlnsXlink':
2940 - hydrateAttribute(
2941 - domElement,
2942 - propKey,
2943 - 'xmlns:xlink',
2944 - value,
2945 - extraAttributes,
2946 - );
2947 - continue;
2948 - case 'xHeight':
2949 - hydrateAttribute(
2950 - domElement,
2951 - propKey,
2952 - 'x-height',
2953 - value,
2954 - extraAttributes,
2955 - );
2956 - continue;
2957 - case 'xlinkActuate':
2958 - hydrateAttribute(
2959 - domElement,
2960 - propKey,
2961 - 'xlink:actuate',
2962 - value,
2963 - extraAttributes,
2964 - );
2965 - continue;
2966 - case 'xlinkArcrole':
2967 - hydrateAttribute(
2968 - domElement,
2969 - propKey,
2970 - 'xlink:arcrole',
2971 - value,
2972 - extraAttributes,
2973 - );
2974 - continue;
2975 - case 'xlinkRole':
2976 - hydrateAttribute(
2977 - domElement,
2978 - propKey,
2979 - 'xlink:role',
2980 - value,
2981 - extraAttributes,
2982 - );
2983 - continue;
2984 - case 'xlinkShow':
2985 - hydrateAttribute(
2986 - domElement,
2987 - propKey,
2988 - 'xlink:show',
2989 - value,
2990 - extraAttributes,
2991 - );
2992 - continue;
2993 - case 'xlinkTitle':
2994 - hydrateAttribute(
2995 - domElement,
2996 - propKey,
2997 - 'xlink:title',
2998 - value,
2999 - extraAttributes,
3000 - );
3001 - continue;
3002 - case 'xlinkType':
3003 - hydrateAttribute(
3004 - domElement,
3005 - propKey,
3006 - 'xlink:type',
3007 - value,
3008 - extraAttributes,
3009 - );
3010 - continue;
3011 - case 'xmlBase':
3012 - hydrateAttribute(
3013 - domElement,
3014 - propKey,
3015 - 'xml:base',
3016 - value,
3017 - extraAttributes,
3018 - );
3019 - continue;
3020 - case 'xmlLang':
3021 - hydrateAttribute(
3022 - domElement,
3023 - propKey,
3024 - 'xml:lang',
3025 - value,
3026 - extraAttributes,
3027 - );
3028 - continue;
3029 - case 'xmlSpace':
3030 - hydrateAttribute(
3031 - domElement,
3032 - propKey,
3033 - 'xml:space',
3034 - value,
3035 - extraAttributes,
3036 - );
3037 - continue;
3038 - default: {
3039 - if (
3040 - // shouldIgnoreAttribute
3041 - // We have already filtered out null/undefined and reserved words.
3042 - propKey.length > 2 &&
3043 - (propKey[0] === 'o' || propKey[0] === 'O') &&
3044 - (propKey[1] === 'n' || propKey[1] === 'N')
3045 - ) {
3046 - continue;
3047 - }
3048 - let isMismatchDueToBadCasing = false;
3049 - let ownNamespaceDev = parentNamespaceDev;
3050 - if (ownNamespaceDev === HTML_NAMESPACE) {
3051 - ownNamespaceDev = getIntrinsicNamespace(tag);
3052 - }
3053 - if (ownNamespaceDev === HTML_NAMESPACE) {
3054 - extraAttributes.delete(propKey.toLowerCase());
3055 - } else {
3056 - const standardName = getPossibleStandardName(propKey);
3057 - if (standardName !== null && standardName !== propKey) {
3058 - // If an SVG prop is supplied with bad casing, it will
3059 - // be successfully parsed from HTML, but will produce a mismatch
3060 - // (and would be incorrectly rendered on the client).
3061 - // However, we already warn about bad casing elsewhere.
3062 - // So we'll skip the misleading extra mismatch warning in this case.
3063 - isMismatchDueToBadCasing = true;
3064 - extraAttributes.delete(standardName);
3065 - }
3066 - extraAttributes.delete(propKey);
3067 - }
3068 - const serverValue = getValueForAttribute(domElement, propKey, value);
2208 if (!isMismatchDueToBadCasing) {
2209 warnForPropDifference(propKey, serverValue, value);
2210 }
packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js
+59 -274
@@ -40,6 +40,7 @@ import {
40
41 import isAttributeNameSafe from '../shared/isAttributeNameSafe';
42 import isUnitlessNumber from '../shared/isUnitlessNumber';
43 +import getAttributeAlias from '../shared/getAttributeAlias';
44
45 import {checkControlledValueProps} from '../shared/ReactControlledValuePropTypes';
46 import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook';
@@ -640,23 +641,29 @@ function pushAttribute(
641 value: string | boolean | number | Function | Object, // not null or undefined
642 ): void {
643 switch (name) {
644 + // These are very common props and therefore are in the beginning of the switch.
645 + // TODO: aria-label is a very common prop but allows booleans so is not like the others
646 + // but should ideally go in this list too.
647 + case 'className': {
648 + pushStringAttribute(target, 'class', value);
649 + break;
650 + }
651 + case 'tabIndex': {
652 + pushStringAttribute(target, 'tabindex', value);
653 + break;
654 + }
655 + case 'dir':
656 + case 'role':
657 + case 'viewBox':
658 + case 'width':
659 + case 'height': {
660 + pushStringAttribute(target, name, value);
661 + break;
662 + }
663 case 'style': {
664 pushStyleAttribute(target, value);
665 return;
666 }
647 - case 'defaultValue':
648 - case 'defaultChecked': // These shouldn't be set as attributes on generic HTML elements.
649 - case 'innerHTML': // Must use dangerouslySetInnerHTML instead.
650 - case 'suppressContentEditableWarning':
651 - case 'suppressHydrationWarning':
652 - // Ignored. These are built-in to React on the client.
653 - return;
654 - case 'autoFocus':
655 - case 'multiple':
656 - case 'muted': {
657 - pushBooleanAttribute(target, name.toLowerCase(), value);
658 - return;
659 - }
667 case 'src':
668 case 'href':
669 case 'action':
@@ -709,6 +716,19 @@ function pushAttribute(
716 );
717 return;
718 }
719 + case 'defaultValue':
720 + case 'defaultChecked': // These shouldn't be set as attributes on generic HTML elements.
721 + case 'innerHTML': // Must use dangerouslySetInnerHTML instead.
722 + case 'suppressContentEditableWarning':
723 + case 'suppressHydrationWarning':
724 + // Ignored. These are built-in to React on the client.
725 + return;
726 + case 'autoFocus':
727 + case 'multiple':
728 + case 'muted': {
729 + pushBooleanAttribute(target, name.toLowerCase(), value);
730 + return;
731 + }
732 case 'xlinkHref': {
733 if (
734 typeof value === 'function' ||
@@ -846,278 +866,33 @@ function pushAttribute(
866 }
867 return;
868 }
849 - // A few React string attributes have a different name.
850 - // This is a mapping from React prop names to the attribute names.
851 - case 'acceptCharset':
852 - pushStringAttribute(target, 'accept-charset', value);
853 - return;
854 - case 'className':
855 - pushStringAttribute(target, 'class', value);
856 - return;
857 - case 'htmlFor':
858 - pushStringAttribute(target, 'for', value);
859 - return;
860 - case 'httpEquiv':
861 - pushStringAttribute(target, 'http-equiv', value);
862 - return;
863 - // HTML and SVG attributes, but the SVG attribute is case sensitive.
864 - case 'tabIndex':
865 - pushStringAttribute(target, 'tabindex', value);
866 - return;
867 - case 'crossOrigin':
868 - pushStringAttribute(target, 'crossorigin', value);
869 - return;
870 - // This is a list of all SVG attributes that need special casing.
871 - // Regular attributes that just accept strings.
872 - case 'accentHeight':
873 - pushStringAttribute(target, 'accent-height', value);
874 - return;
875 - case 'alignmentBaseline':
876 - pushStringAttribute(target, 'alignment-baseline', value);
877 - return;
878 - case 'arabicForm':
879 - pushStringAttribute(target, 'arabic-form', value);
880 - return;
881 - case 'baselineShift':
882 - pushStringAttribute(target, 'baseline-shift', value);
883 - return;
884 - case 'capHeight':
885 - pushStringAttribute(target, 'cap-height', value);
886 - return;
887 - case 'clipPath':
888 - pushStringAttribute(target, 'clip-path', value);
889 - return;
890 - case 'clipRule':
891 - pushStringAttribute(target, 'clip-rule', value);
892 - return;
893 - case 'colorInterpolation':
894 - pushStringAttribute(target, 'color-interpolation', value);
895 - return;
896 - case 'colorInterpolationFilters':
897 - pushStringAttribute(target, 'color-interpolation-filters', value);
898 - return;
899 - case 'colorProfile':
900 - pushStringAttribute(target, 'color-profile', value);
901 - return;
902 - case 'colorRendering':
903 - pushStringAttribute(target, 'color-rendering', value);
904 - return;
905 - case 'dominantBaseline':
906 - pushStringAttribute(target, 'dominant-baseline', value);
907 - return;
908 - case 'enableBackground':
909 - pushStringAttribute(target, 'enable-background', value);
910 - return;
911 - case 'fillOpacity':
912 - pushStringAttribute(target, 'fill-opacity', value);
913 - return;
914 - case 'fillRule':
915 - pushStringAttribute(target, 'fill-rule', value);
916 - return;
917 - case 'floodColor':
918 - pushStringAttribute(target, 'flood-color', value);
919 - return;
920 - case 'floodOpacity':
921 - pushStringAttribute(target, 'flood-opacity', value);
922 - return;
923 - case 'fontFamily':
924 - pushStringAttribute(target, 'font-family', value);
925 - return;
926 - case 'fontSize':
927 - pushStringAttribute(target, 'font-size', value);
928 - return;
929 - case 'fontSizeAdjust':
930 - pushStringAttribute(target, 'font-size-adjust', value);
931 - return;
932 - case 'fontStretch':
933 - pushStringAttribute(target, 'font-stretch', value);
934 - return;
935 - case 'fontStyle':
936 - pushStringAttribute(target, 'font-style', value);
937 - return;
938 - case 'fontVariant':
939 - pushStringAttribute(target, 'font-variant', value);
940 - return;
941 - case 'fontWeight':
942 - pushStringAttribute(target, 'font-weight', value);
943 - return;
944 - case 'glyphName':
945 - pushStringAttribute(target, 'glyph-name', value);
946 - return;
947 - case 'glyphOrientationHorizontal':
948 - pushStringAttribute(target, 'glyph-orientation-horizontal', value);
949 - return;
950 - case 'glyphOrientationVertical':
951 - pushStringAttribute(target, 'glyph-orientation-vertical', value);
952 - return;
953 - case 'horizAdvX':
954 - pushStringAttribute(target, 'horiz-adv-x', value);
955 - return;
956 - case 'horizOriginX':
957 - pushStringAttribute(target, 'horiz-origin-x', value);
958 - return;
959 - case 'imageRendering':
960 - pushStringAttribute(target, 'image-rendering', value);
961 - return;
962 - case 'letterSpacing':
963 - pushStringAttribute(target, 'letter-spacing', value);
964 - return;
965 - case 'lightingColor':
966 - pushStringAttribute(target, 'lighting-color', value);
967 - return;
968 - case 'markerEnd':
969 - pushStringAttribute(target, 'marker-end', value);
970 - return;
971 - case 'markerMid':
972 - pushStringAttribute(target, 'marker-mid', value);
973 - return;
974 - case 'markerStart':
975 - pushStringAttribute(target, 'marker-start', value);
976 - return;
977 - case 'overlinePosition':
978 - pushStringAttribute(target, 'overline-position', value);
979 - return;
980 - case 'overlineThickness':
981 - pushStringAttribute(target, 'overline-thickness', value);
982 - return;
983 - case 'paintOrder':
984 - pushStringAttribute(target, 'paint-order', value);
985 - return;
986 - case 'panose-1':
987 - pushStringAttribute(target, 'panose-1', value);
988 - return;
989 - case 'pointerEvents':
990 - pushStringAttribute(target, 'pointer-events', value);
991 - return;
992 - case 'renderingIntent':
993 - pushStringAttribute(target, 'rendering-intent', value);
994 - return;
995 - case 'shapeRendering':
996 - pushStringAttribute(target, 'shape-rendering', value);
997 - return;
998 - case 'stopColor':
999 - pushStringAttribute(target, 'stop-color', value);
1000 - return;
1001 - case 'stopOpacity':
1002 - pushStringAttribute(target, 'stop-opacity', value);
1003 - return;
1004 - case 'strikethroughPosition':
1005 - pushStringAttribute(target, 'strikethrough-position', value);
1006 - return;
1007 - case 'strikethroughThickness':
1008 - pushStringAttribute(target, 'strikethrough-thickness', value);
1009 - return;
1010 - case 'strokeDasharray':
1011 - pushStringAttribute(target, 'stroke-dasharray', value);
1012 - return;
1013 - case 'strokeDashoffset':
1014 - pushStringAttribute(target, 'stroke-dashoffset', value);
1015 - return;
1016 - case 'strokeLinecap':
1017 - pushStringAttribute(target, 'stroke-linecap', value);
1018 - return;
1019 - case 'strokeLinejoin':
1020 - pushStringAttribute(target, 'stroke-linejoin', value);
1021 - return;
1022 - case 'strokeMiterlimit':
1023 - pushStringAttribute(target, 'stroke-miterlimit', value);
1024 - return;
1025 - case 'strokeOpacity':
1026 - pushStringAttribute(target, 'stroke-opacity', value);
1027 - return;
1028 - case 'strokeWidth':
1029 - pushStringAttribute(target, 'stroke-width', value);
1030 - return;
1031 - case 'textAnchor':
1032 - pushStringAttribute(target, 'text-anchor', value);
1033 - return;
1034 - case 'textDecoration':
1035 - pushStringAttribute(target, 'text-decoration', value);
1036 - return;
1037 - case 'textRendering':
1038 - pushStringAttribute(target, 'text-rendering', value);
1039 - return;
1040 - case 'transformOrigin':
1041 - pushStringAttribute(target, 'transform-origin', value);
1042 - return;
1043 - case 'underlinePosition':
1044 - pushStringAttribute(target, 'underline-position', value);
1045 - return;
1046 - case 'underlineThickness':
1047 - pushStringAttribute(target, 'underline-thickness', value);
1048 - return;
1049 - case 'unicodeBidi':
1050 - pushStringAttribute(target, 'unicode-bidi', value);
1051 - return;
1052 - case 'unicodeRange':
1053 - pushStringAttribute(target, 'unicode-range', value);
1054 - return;
1055 - case 'unitsPerEm':
1056 - pushStringAttribute(target, 'units-per-em', value);
1057 - return;
1058 - case 'vAlphabetic':
1059 - pushStringAttribute(target, 'v-alphabetic', value);
1060 - return;
1061 - case 'vHanging':
1062 - pushStringAttribute(target, 'v-hanging', value);
1063 - return;
1064 - case 'vIdeographic':
1065 - pushStringAttribute(target, 'v-ideographic', value);
1066 - return;
1067 - case 'vMathematical':
1068 - pushStringAttribute(target, 'v-mathematical', value);
1069 - return;
1070 - case 'vectorEffect':
1071 - pushStringAttribute(target, 'vector-effect', value);
1072 - return;
1073 - case 'vertAdvY':
1074 - pushStringAttribute(target, 'vert-adv-y', value);
1075 - return;
1076 - case 'vertOriginX':
1077 - pushStringAttribute(target, 'vert-origin-x', value);
1078 - return;
1079 - case 'vertOriginY':
1080 - pushStringAttribute(target, 'vert-origin-y', value);
1081 - return;
1082 - case 'wordSpacing':
1083 - pushStringAttribute(target, 'word-spacing', value);
1084 - return;
1085 - case 'writingMode':
1086 - pushStringAttribute(target, 'writing-mode', value);
1087 - return;
1088 - case 'xmlnsXlink':
1089 - pushStringAttribute(target, 'xmlns:xlink', value);
1090 - return;
1091 - case 'xHeight':
1092 - pushStringAttribute(target, 'x-height', value);
1093 - return;
869 case 'xlinkActuate':
870 pushStringAttribute(target, 'xlink:actuate', value);
1096 - break;
871 + return;
872 case 'xlinkArcrole':
873 pushStringAttribute(target, 'xlink:arcrole', value);
1099 - break;
874 + return;
875 case 'xlinkRole':
876 pushStringAttribute(target, 'xlink:role', value);
1102 - break;
877 + return;
878 case 'xlinkShow':
879 pushStringAttribute(target, 'xlink:show', value);
1105 - break;
880 + return;
881 case 'xlinkTitle':
882 pushStringAttribute(target, 'xlink:title', value);
1108 - break;
883 + return;
884 case 'xlinkType':
885 pushStringAttribute(target, 'xlink:type', value);
1111 - break;
886 + return;
887 case 'xmlBase':
888 pushStringAttribute(target, 'xml:base', value);
1114 - break;
889 + return;
890 case 'xmlLang':
891 pushStringAttribute(target, 'xml:lang', value);
1117 - break;
892 + return;
893 case 'xmlSpace':
894 pushStringAttribute(target, 'xml:space', value);
1120 - break;
895 + return;
896 default:
897 if (
898 // shouldIgnoreAttribute
@@ -1129,14 +904,15 @@ function pushAttribute(
904 return;
905 }
906
1132 - if (isAttributeNameSafe(name)) {
907 + const attributeName = getAttributeAlias(name);
908 + if (isAttributeNameSafe(attributeName)) {
909 // shouldRemoveAttribute
910 switch (typeof value) {
911 case 'function':
912 case 'symbol': // eslint-disable-line
913 return;
914 case 'boolean': {
1139 - const prefix = name.toLowerCase().slice(0, 5);
915 + const prefix = attributeName.toLowerCase().slice(0, 5);
916 if (prefix !== 'data-' && prefix !== 'aria-') {
917 return;
918 }
@@ -1144,7 +920,7 @@ function pushAttribute(
920 }
921 target.push(
922 attributeSeparator,
1147 - stringToChunk(name),
923 + stringToChunk(attributeName),
924 attributeAssign,
925 stringToChunk(escapeTextForBrowser(value)),
926 attributeEnd,
@@ -2862,6 +2638,16 @@ export function pushStartInstance(
2638 }
2639
2640 switch (type) {
2641 + case 'div':
2642 + case 'span':
2643 + case 'svg':
2644 + case 'path':
2645 + case 'a':
2646 + case 'g':
2647 + case 'p':
2648 + case 'li':
2649 + // Fast track very common tags
2650 + break;
2651 // Special tags
2652 case 'select':
2653 return pushStartSelect(target, props);
@@ -2971,15 +2757,14 @@ export function pushStartInstance(
2757 );
2758 }
2759 default: {
2974 - if (type.indexOf('-') === -1) {
2975 - // Generic element
2976 - return pushStartGenericElement(target, props, type);
2977 - } else {
2760 + if (type.indexOf('-') !== -1) {
2761 // Custom element
2762 return pushStartCustomElement(target, props, type);
2763 }
2764 }
2765 }
2766 + // Generic element
2767 + return pushStartGenericElement(target, props, type);
2768 }
2769
2770 const endTag1 = stringToPrecomputedChunk('</');
packages/react-dom-bindings/src/shared/getAttributeAlias.js new
+96
@@ -0,0 +1,96 @@
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 +const aliases = new Map([
11 + ['acceptCharset', 'accept-charset'],
12 + ['htmlFor', 'for'],
13 + ['httpEquiv', 'http-equiv'],
14 + // HTML and SVG attributes, but the SVG attribute is case sensitive.],
15 + ['crossOrigin', 'crossorigin'],
16 + // This is a list of all SVG attributes that need special casing.
17 + // Regular attributes that just accept strings.],
18 + ['accentHeight', 'accent-height'],
19 + ['alignmentBaseline', 'alignment-baseline'],
20 + ['arabicForm', 'arabic-form'],
21 + ['baselineShift', 'baseline-shift'],
22 + ['capHeight', 'cap-height'],
23 + ['clipPath', 'clip-path'],
24 + ['clipRule', 'clip-rule'],
25 + ['colorInterpolation', 'color-interpolation'],
26 + ['colorInterpolationFilters', 'color-interpolation-filters'],
27 + ['colorProfile', 'color-profile'],
28 + ['colorRendering', 'color-rendering'],
29 + ['dominantBaseline', 'dominant-baseline'],
30 + ['enableBackground', 'enable-background'],
31 + ['fillOpacity', 'fill-opacity'],
32 + ['fillRule', 'fill-rule'],
33 + ['floodColor', 'flood-color'],
34 + ['floodOpacity', 'flood-opacity'],
35 + ['fontFamily', 'font-family'],
36 + ['fontSize', 'font-size'],
37 + ['fontSizeAdjust', 'font-size-adjust'],
38 + ['fontStretch', 'font-stretch'],
39 + ['fontStyle', 'font-style'],
40 + ['fontVariant', 'font-variant'],
41 + ['fontWeight', 'font-weight'],
42 + ['glyphName', 'glyph-name'],
43 + ['glyphOrientationHorizontal', 'glyph-orientation-horizontal'],
44 + ['glyphOrientationVertical', 'glyph-orientation-vertical'],
45 + ['horizAdvX', 'horiz-adv-x'],
46 + ['horizOriginX', 'horiz-origin-x'],
47 + ['imageRendering', 'image-rendering'],
48 + ['letterSpacing', 'letter-spacing'],
49 + ['lightingColor', 'lighting-color'],
50 + ['markerEnd', 'marker-end'],
51 + ['markerMid', 'marker-mid'],
52 + ['markerStart', 'marker-start'],
53 + ['overlinePosition', 'overline-position'],
54 + ['overlineThickness', 'overline-thickness'],
55 + ['paintOrder', 'paint-order'],
56 + ['panose-1', 'panose-1'],
57 + ['pointerEvents', 'pointer-events'],
58 + ['renderingIntent', 'rendering-intent'],
59 + ['shapeRendering', 'shape-rendering'],
60 + ['stopColor', 'stop-color'],
61 + ['stopOpacity', 'stop-opacity'],
62 + ['strikethroughPosition', 'strikethrough-position'],
63 + ['strikethroughThickness', 'strikethrough-thickness'],
64 + ['strokeDasharray', 'stroke-dasharray'],
65 + ['strokeDashoffset', 'stroke-dashoffset'],
66 + ['strokeLinecap', 'stroke-linecap'],
67 + ['strokeLinejoin', 'stroke-linejoin'],
68 + ['strokeMiterlimit', 'stroke-miterlimit'],
69 + ['strokeOpacity', 'stroke-opacity'],
70 + ['strokeWidth', 'stroke-width'],
71 + ['textAnchor', 'text-anchor'],
72 + ['textDecoration', 'text-decoration'],
73 + ['textRendering', 'text-rendering'],
74 + ['transformOrigin', 'transform-origin'],
75 + ['underlinePosition', 'underline-position'],
76 + ['underlineThickness', 'underline-thickness'],
77 + ['unicodeBidi', 'unicode-bidi'],
78 + ['unicodeRange', 'unicode-range'],
79 + ['unitsPerEm', 'units-per-em'],
80 + ['vAlphabetic', 'v-alphabetic'],
81 + ['vHanging', 'v-hanging'],
82 + ['vIdeographic', 'v-ideographic'],
83 + ['vMathematical', 'v-mathematical'],
84 + ['vectorEffect', 'vector-effect'],
85 + ['vertAdvY', 'vert-adv-y'],
86 + ['vertOriginX', 'vert-origin-x'],
87 + ['vertOriginY', 'vert-origin-y'],
88 + ['wordSpacing', 'word-spacing'],
89 + ['writingMode', 'writing-mode'],
90 + ['xmlnsXlink', 'xmlns:xlink'],
91 + ['xHeight', 'x-height'],
92 +]);
93 +
94 +export default function (name: string): string {
95 + return aliases.get(name) || name;
96 +}
packages/react-dom-bindings/src/shared/isUnitlessNumber.js
+74 -76
@@ -10,81 +10,79 @@
10 /**
11 * CSS properties which accept numbers but are not in units of "px".
12 */
13 +const unitlessNumbers = new Set([
14 + 'animationIterationCount',
15 + 'aspectRatio',
16 + 'borderImageOutset',
17 + 'borderImageSlice',
18 + 'borderImageWidth',
19 + 'boxFlex',
20 + 'boxFlexGroup',
21 + 'boxOrdinalGroup',
22 + 'columnCount',
23 + 'columns',
24 + 'flex',
25 + 'flexGrow',
26 + 'flexPositive',
27 + 'flexShrink',
28 + 'flexNegative',
29 + 'flexOrder',
30 + 'gridArea',
31 + 'gridRow',
32 + 'gridRowEnd',
33 + 'gridRowSpan',
34 + 'gridRowStart',
35 + 'gridColumn',
36 + 'gridColumnEnd',
37 + 'gridColumnSpan',
38 + 'gridColumnStart',
39 + 'fontWeight',
40 + 'lineClamp',
41 + 'lineHeight',
42 + 'opacity',
43 + 'order',
44 + 'orphans',
45 + 'scale',
46 + 'tabSize',
47 + 'widows',
48 + 'zIndex',
49 + 'zoom',
50 + 'fillOpacity', // SVG-related properties
51 + 'floodOpacity',
52 + 'stopOpacity',
53 + 'strokeDasharray',
54 + 'strokeDashoffset',
55 + 'strokeMiterlimit',
56 + 'strokeOpacity',
57 + 'strokeWidth',
58 + 'MozAnimationIterationCount', // Known Prefixed Properties
59 + 'MozBoxFlex', // TODO: Remove these since they shouldn't be used in modern code
60 + 'MozBoxFlexGroup',
61 + 'MozLineClamp',
62 + 'msAnimationIterationCount',
63 + 'msFlex',
64 + 'msZoom',
65 + 'msFlexGrow',
66 + 'msFlexNegative',
67 + 'msFlexOrder',
68 + 'msFlexPositive',
69 + 'msFlexShrink',
70 + 'msGridColumn',
71 + 'msGridColumnSpan',
72 + 'msGridRow',
73 + 'msGridRowSpan',
74 + 'WebkitAnimationIterationCount',
75 + 'WebkitBoxFlex',
76 + 'WebKitBoxFlexGroup',
77 + 'WebkitBoxOrdinalGroup',
78 + 'WebkitColumnCount',
79 + 'WebkitColumns',
80 + 'WebkitFlex',
81 + 'WebkitFlexGrow',
82 + 'WebkitFlexPositive',
83 + 'WebkitFlexShrink',
84 + 'WebkitLineClamp',
85 +]);
86 export default function (name: string): boolean {
14 - switch (name) {
15 - case 'animationIterationCount':
16 - case 'aspectRatio':
17 - case 'borderImageOutset':
18 - case 'borderImageSlice':
19 - case 'borderImageWidth':
20 - case 'boxFlex':
21 - case 'boxFlexGroup':
22 - case 'boxOrdinalGroup':
23 - case 'columnCount':
24 - case 'columns':
25 - case 'flex':
26 - case 'flexGrow':
27 - case 'flexPositive':
28 - case 'flexShrink':
29 - case 'flexNegative':
30 - case 'flexOrder':
31 - case 'gridArea':
32 - case 'gridRow':
33 - case 'gridRowEnd':
34 - case 'gridRowSpan':
35 - case 'gridRowStart':
36 - case 'gridColumn':
37 - case 'gridColumnEnd':
38 - case 'gridColumnSpan':
39 - case 'gridColumnStart':
40 - case 'fontWeight':
41 - case 'lineClamp':
42 - case 'lineHeight':
43 - case 'opacity':
44 - case 'order':
45 - case 'orphans':
46 - case 'scale':
47 - case 'tabSize':
48 - case 'widows':
49 - case 'zIndex':
50 - case 'zoom':
51 - case 'fillOpacity': // SVG-related properties
52 - case 'floodOpacity':
53 - case 'stopOpacity':
54 - case 'strokeDasharray':
55 - case 'strokeDashoffset':
56 - case 'strokeMiterlimit':
57 - case 'strokeOpacity':
58 - case 'strokeWidth':
59 - case 'MozAnimationIterationCount': // Known Prefixed Properties
60 - case 'MozBoxFlex': // TODO: Remove these since they shouldn't be used in modern code
61 - case 'MozBoxFlexGroup':
62 - case 'MozLineClamp':
63 - case 'msAnimationIterationCount':
64 - case 'msFlex':
65 - case 'msZoom':
66 - case 'msFlexGrow':
67 - case 'msFlexNegative':
68 - case 'msFlexOrder':
69 - case 'msFlexPositive':
70 - case 'msFlexShrink':
71 - case 'msGridColumn':
72 - case 'msGridColumnSpan':
73 - case 'msGridRow':
74 - case 'msGridRowSpan':
75 - case 'WebkitAnimationIterationCount':
76 - case 'WebkitBoxFlex':
77 - case 'WebKitBoxFlexGroup':
78 - case 'WebkitBoxOrdinalGroup':
79 - case 'WebkitColumnCount':
80 - case 'WebkitColumns':
81 - case 'WebkitFlex':
82 - case 'WebkitFlexGrow':
83 - case 'WebkitFlexPositive':
84 - case 'WebkitFlexShrink':
85 - case 'WebkitLineClamp':
86 - return true;
87 - default:
88 - return false;
89 - }
87 + return unitlessNumbers.has(name);
88 }