16
17
import {canUseDOM} from 'shared/ExecutionEnvironment';
18
import {checkHtmlStringCoercion} from 'shared/CheckStringCoercion';
19
+import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion';
20
21
import {
22
getValueForAttribute,
23
getValueForAttributeOnCustomComponent,
23
- getValueForProperty,
24
- setValueForProperty,
24
setValueForPropertyOnCustomComponent,
25
setValueForAttribute,
26
+ setValueForNamespacedAttribute,
27
} from './DOMPropertyOperations';
28
import {
29
initWrapperState as ReactDOMInputInitWrapperState,
57
validateShorthandPropertyCollisionInDev,
58
} from './CSSPropertyOperations';
59
import {HTML_NAMESPACE, getIntrinsicNamespace} from './DOMNamespaces';
60
-import {getPropertyInfo} from '../shared/DOMProperty';
60
import isCustomElement from '../shared/isCustomElement';
61
import possibleStandardNames from '../shared/possibleStandardNames';
62
import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook';
63
import {validateProperties as validateInputProperties} from '../shared/ReactDOMNullInputValuePropHook';
64
import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook';
65
+import sanitizeURL from '../shared/sanitizeURL';
66
67
import {
68
enableCustomElementPropertySupport,
69
enableClientRenderFallbackOnTextMismatch,
70
enableHostSingletons,
71
disableIEWorkarounds,
72
+ enableTrustedTypesIntegration,
73
+ enableFilterEmptyStringAttributesDOM,
74
} from 'shared/ReactFeatureFlags';
75
import {
76
mediaEventTypes,
124
if (didWarnInvalidHydration) {
125
return;
126
}
127
+ if (serverValue === clientValue) {
128
+ return;
129
+ }
130
const normalizedClientValue =
131
normalizeMarkupForTextOrAttribute(clientValue);
132
const normalizedServerValue =
264
node.onclick = noop;
265
}
266
267
+const xlinkNamespace = 'http://www.w3.org/1999/xlink';
268
+const xmlNamespace = 'http://www.w3.org/XML/1998/namespace';
269
+
270
function setProp(
271
domElement: Element,
272
tag: string,
273
key: string,
274
value: mixed,
267
- isCustomElementTag: boolean,
275
props: any,
276
): void {
277
switch (key) {
278
case 'style': {
272
- if (value != null && typeof value !== 'object') {
273
- throw new Error(
274
- 'The `style` prop expects a mapping from style properties to values, ' +
275
- "not a string. For example, style={{marginRight: spacing + 'em'}} when " +
276
- 'using JSX.',
277
- );
278
- }
279
- if (__DEV__) {
280
- if (value) {
281
- // Freeze the next style object so that we can assume it won't be
282
- // mutated. We have already warned for this in the past.
283
- Object.freeze(value);
284
- }
285
- }
286
- // Relies on `updateStylesByID` not mutating `styleUpdates`.
279
setValueForStyles(domElement, value);
280
break;
281
}
370
// on server rendering (but we *do* want to emit it in SSR).
371
break;
372
}
373
+ // These attributes accept URLs. These must not allow javascript: URLS.
374
+ case 'src':
375
+ case 'href':
376
+ case 'action':
377
+ if (enableFilterEmptyStringAttributesDOM) {
378
+ if (value === '') {
379
+ if (__DEV__) {
380
+ if (key === 'src') {
381
+ console.error(
382
+ 'An empty string ("") was passed to the %s attribute. ' +
383
+ 'This may cause the browser to download the whole page again over the network. ' +
384
+ 'To fix this, either do not render the element at all ' +
385
+ 'or pass null to %s instead of an empty string.',
386
+ key,
387
+ key,
388
+ );
389
+ } else {
390
+ console.error(
391
+ 'An empty string ("") was passed to the %s attribute. ' +
392
+ 'To fix this, either do not render the element at all ' +
393
+ 'or pass null to %s instead of an empty string.',
394
+ key,
395
+ key,
396
+ );
397
+ }
398
+ }
399
+ domElement.removeAttribute(key);
400
+ break;
401
+ }
402
+ }
403
+ // Fall through to the last case which shouldn't remove empty strings.
404
+ // eslint-disable-next-line no-fallthrough
405
+ case 'formAction': {
406
+ if (
407
+ value == null ||
408
+ typeof value === 'function' ||
409
+ typeof value === 'symbol' ||
410
+ typeof value === 'boolean'
411
+ ) {
412
+ domElement.removeAttribute(key);
413
+ break;
414
+ }
415
+ // `setAttribute` with objects becomes only `[object]` in IE8/9,
416
+ // ('' + value) makes it output the correct toString()-value.
417
+ if (__DEV__) {
418
+ checkAttributeStringCoercion(value, key);
419
+ }
420
+ const sanitizedValue = (sanitizeURL(
421
+ enableTrustedTypesIntegration ? value : '' + (value: any),
422
+ ): any);
423
+ domElement.setAttribute(key, sanitizedValue);
424
+ break;
425
+ }
426
+ case 'xlinkHref': {
427
+ if (
428
+ value == null ||
429
+ typeof value === 'function' ||
430
+ typeof value === 'boolean' ||
431
+ typeof value === 'symbol'
432
+ ) {
433
+ domElement.removeAttribute('xlink:href');
434
+ break;
435
+ }
436
+ // `setAttribute` with objects becomes only `[object]` in IE8/9,
437
+ // ('' + value) makes it output the correct toString()-value.
438
+ if (__DEV__) {
439
+ checkAttributeStringCoercion(value, key);
440
+ }
441
+ const sanitizedValue = (sanitizeURL(
442
+ enableTrustedTypesIntegration ? value : '' + (value: any),
443
+ ): any);
444
+ domElement.setAttributeNS(xlinkNamespace, 'xlink:href', sanitizedValue);
445
+ break;
446
+ }
447
+ case 'contentEditable':
448
+ case 'spellCheck':
449
+ case 'draggable':
450
+ case 'value':
451
+ case 'autoReverse':
452
+ case 'externalResourcesRequired':
453
+ case 'focusable':
454
+ case 'preserveAlpha': {
455
+ // Booleanish String
456
+ // These are "enumerated" attributes that accept "true" and "false".
457
+ // In React, we let users pass `true` and `false` even though technically
458
+ // these aren't boolean attributes (they are coerced to strings).
459
+ // The SVG attributes are case-sensitive. Since the HTML attributes are
460
+ // insensitive they also work even though we canonically use lower case.
461
+ if (
462
+ value != null &&
463
+ typeof value !== 'function' &&
464
+ typeof value !== 'symbol'
465
+ ) {
466
+ if (__DEV__) {
467
+ checkAttributeStringCoercion(value, key);
468
+ }
469
+ domElement.setAttribute(key, (value: any));
470
+ } else {
471
+ domElement.removeAttribute(key);
472
+ }
473
+ break;
474
+ }
475
+ // Boolean
476
+ case 'allowFullScreen':
477
+ case 'async':
478
+ case 'autoPlay':
479
+ case 'controls':
480
+ case 'default':
481
+ case 'defer':
482
+ case 'disabled':
483
+ case 'disablePictureInPicture':
484
+ case 'disableRemotePlayback':
485
+ case 'formNoValidate':
486
+ case 'hidden':
487
+ case 'loop':
488
+ case 'noModule':
489
+ case 'noValidate':
490
+ case 'open':
491
+ case 'playsInline':
492
+ case 'readOnly':
493
+ case 'required':
494
+ case 'reversed':
495
+ case 'scoped':
496
+ case 'seamless':
497
+ case 'itemScope': {
498
+ if (value && typeof value !== 'function' && typeof value !== 'symbol') {
499
+ domElement.setAttribute(key, '');
500
+ } else {
501
+ domElement.removeAttribute(key);
502
+ }
503
+ break;
504
+ }
505
+ // Overloaded Boolean
506
+ case 'capture':
507
+ case 'download': {
508
+ // An attribute that can be used as a flag as well as with a value.
509
+ // When true, it should be present (set either to an empty string or its name).
510
+ // When false, it should be omitted.
511
+ // For any other value, should be present with that value.
512
+ if (value === true) {
513
+ domElement.setAttribute(key, '');
514
+ } else if (
515
+ value !== false &&
516
+ value != null &&
517
+ typeof value !== 'function' &&
518
+ typeof value !== 'symbol'
519
+ ) {
520
+ if (__DEV__) {
521
+ checkAttributeStringCoercion(value, key);
522
+ }
523
+ domElement.setAttribute(key, (value: any));
524
+ } else {
525
+ domElement.removeAttribute(key);
526
+ }
527
+ break;
528
+ }
529
+ case 'cols':
530
+ case 'rows':
531
+ case 'size':
532
+ case 'span': {
533
+ // These are HTML attributes that must be positive numbers.
534
+ if (
535
+ value != null &&
536
+ typeof value !== 'function' &&
537
+ typeof value !== 'symbol' &&
538
+ !isNaN(value) &&
539
+ (value: any) >= 1
540
+ ) {
541
+ if (__DEV__) {
542
+ checkAttributeStringCoercion(value, key);
543
+ }
544
+ domElement.setAttribute(key, (value: any));
545
+ } else {
546
+ domElement.removeAttribute(key);
547
+ }
548
+ break;
549
+ }
550
+ case 'rowSpan':
551
+ case 'start': {
552
+ // These are HTML attributes that must be numbers.
553
+ if (
554
+ value != null &&
555
+ typeof value !== 'function' &&
556
+ typeof value !== 'symbol' &&
557
+ !isNaN(value)
558
+ ) {
559
+ if (__DEV__) {
560
+ checkAttributeStringCoercion(value, key);
561
+ }
562
+ domElement.setAttribute(key, (value: any));
563
+ } else {
564
+ domElement.removeAttribute(key);
565
+ }
566
+ break;
567
+ }
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;
813
+ case 'xlinkActuate':
814
+ setValueForNamespacedAttribute(
815
+ domElement,
816
+ xlinkNamespace,
817
+ 'xlink:actuate',
818
+ value,
819
+ );
820
+ break;
821
+ case 'xlinkArcrole':
822
+ setValueForNamespacedAttribute(
823
+ domElement,
824
+ xlinkNamespace,
825
+ 'xlink:arcrole',
826
+ value,
827
+ );
828
+ break;
829
+ case 'xlinkRole':
830
+ setValueForNamespacedAttribute(
831
+ domElement,
832
+ xlinkNamespace,
833
+ 'xlink:role',
834
+ value,
835
+ );
836
+ break;
837
+ case 'xlinkShow':
838
+ setValueForNamespacedAttribute(
839
+ domElement,
840
+ xlinkNamespace,
841
+ 'xlink:show',
842
+ value,
843
+ );
844
+ break;
845
+ case 'xlinkTitle':
846
+ setValueForNamespacedAttribute(
847
+ domElement,
848
+ xlinkNamespace,
849
+ 'xlink:title',
850
+ value,
851
+ );
852
+ break;
853
+ case 'xlinkType':
854
+ setValueForNamespacedAttribute(
855
+ domElement,
856
+ xlinkNamespace,
857
+ 'xlink:type',
858
+ value,
859
+ );
860
+ break;
861
+ case 'xmlBase':
862
+ setValueForNamespacedAttribute(
863
+ domElement,
864
+ xmlNamespace,
865
+ 'xml:base',
866
+ value,
867
+ );
868
+ break;
869
+ case 'xmlLang':
870
+ setValueForNamespacedAttribute(
871
+ domElement,
872
+ xmlNamespace,
873
+ 'xml:lang',
874
+ value,
875
+ );
876
+ break;
877
+ case 'xmlSpace':
878
+ setValueForNamespacedAttribute(
879
+ domElement,
880
+ xmlNamespace,
881
+ 'xml:space',
882
+ value,
883
+ );
884
+ break;
885
+ // Properties that should not be allowed on custom elements.
886
+ case 'innerText':
887
+ case 'textContent':
888
+ if (enableCustomElementPropertySupport) {
889
+ break;
890
+ }
891
+ // eslint-disable-next-line no-fallthrough
892
+ default: {
893
+ if (
894
+ key.length > 2 &&
895
+ (key[0] === 'o' || key[0] === 'O') &&
896
+ (key[1] === 'n' || key[1] === 'N')
897
+ ) {
898
+ if (
899
+ __DEV__ &&
900
+ registrationNameDependencies.hasOwnProperty(key) &&
901
+ value != null &&
902
+ typeof value !== 'function'
903
+ ) {
904
+ warnForInvalidEventListener(key, value);
905
+ }
906
+ } else {
907
+ setValueForAttribute(domElement, key, 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
+): void {
920
+ switch (key) {
921
+ case 'style': {
922
+ setValueForStyles(domElement, value);
923
+ break;
924
+ }
925
+ case 'dangerouslySetInnerHTML': {
926
+ if (value != null) {
927
+ if (typeof value !== 'object' || !('__html' in value)) {
928
+ throw new Error(
929
+ '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
930
+ 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
931
+ 'for more information.',
932
+ );
933
+ }
934
+ const nextHtml: any = value.__html;
935
+ if (nextHtml != null) {
936
+ if (props.children != null) {
937
+ throw new Error(
938
+ 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
939
+ );
940
+ }
941
+ if (disableIEWorkarounds) {
942
+ domElement.innerHTML = nextHtml;
943
+ } else {
944
+ setInnerHTML(domElement, nextHtml);
945
+ }
946
+ }
947
+ }
948
+ break;
949
+ }
950
+ case 'children': {
951
+ if (typeof value === 'string') {
952
+ setTextContent(domElement, value);
953
+ } else if (typeof value === 'number') {
954
+ setTextContent(domElement, '' + value);
955
+ }
956
+ break;
957
+ }
958
+ case 'onScroll': {
959
+ if (value != null) {
960
+ if (__DEV__ && typeof value !== 'function') {
961
+ warnForInvalidEventListener(key, value);
962
+ }
963
+ listenToNonDelegatedEvent('scroll', domElement);
964
+ }
965
+ break;
966
+ }
967
+ case 'onClick': {
968
+ // TODO: This cast may not be sound for SVG, MathML or custom elements.
969
+ if (value != null) {
970
+ if (__DEV__ && typeof value !== 'function') {
971
+ warnForInvalidEventListener(key, value);
972
+ }
973
+ trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
974
+ }
975
+ break;
976
+ }
977
+ case 'suppressContentEditableWarning':
978
+ case 'suppressHydrationWarning':
979
+ case 'innerHTML': {
980
+ // Noop
981
+ break;
982
+ }
983
case 'innerText': // Properties
984
case 'textContent':
985
if (enableCustomElementPropertySupport) {
992
warnForInvalidEventListener(key, value);
993
}
994
} else {
393
- if (isCustomElementTag) {
394
- if (enableCustomElementPropertySupport) {
395
- setValueForPropertyOnCustomComponent(domElement, key, value);
396
- } else {
397
- if (typeof value === 'boolean') {
398
- // Special case before the new flag is on
399
- value = '' + (value: any);
400
- }
401
- setValueForAttribute(domElement, key, value);
402
- }
995
+ if (enableCustomElementPropertySupport) {
996
+ setValueForPropertyOnCustomComponent(domElement, key, value);
997
} else {
404
- if (
405
- // shouldIgnoreAttribute
406
- // We have already filtered out reserved words.
407
- key.length > 2 &&
408
- (key[0] === 'o' || key[0] === 'O') &&
409
- (key[1] === 'n' || key[1] === 'N')
410
- ) {
411
- return;
412
- }
413
-
414
- const propertyInfo = getPropertyInfo(key);
415
- if (propertyInfo !== null) {
416
- setValueForProperty(domElement, propertyInfo, value);
417
- } else {
418
- setValueForAttribute(domElement, key, value);
998
+ if (typeof value === 'boolean') {
999
+ // Special case before the new flag is on
1000
+ value = '' + (value: any);
1001
}
1002
+ setValueForAttribute(domElement, key, value);
1003
}
1004
}
1005
}
1058
}
1059
// defaultChecked and defaultValue are ignored by setProp
1060
default: {
478
- setProp(domElement, tag, propKey, propValue, false, props);
1061
+ setProp(domElement, tag, propKey, propValue, props);
1062
}
1063
}
1064
}
1088
}
1089
// defaultValue are ignored by setProp
1090
default: {
508
- setProp(domElement, tag, propKey, propValue, false, props);
1091
+ setProp(domElement, tag, propKey, propValue, props);
1092
}
1093
}
1094
}
1128
}
1129
// defaultValue is ignored by setProp
1130
default: {
548
- setProp(domElement, tag, propKey, propValue, false, props);
1131
+ setProp(domElement, tag, propKey, propValue, props);
1132
}
1133
}
1134
}
1158
break;
1159
}
1160
default: {
578
- setProp(domElement, tag, propKey, propValue, false, props);
1161
+ setProp(domElement, tag, propKey, propValue, props);
1162
}
1163
}
1164
}
1240
}
1241
// defaultChecked and defaultValue are ignored by setProp
1242
default: {
660
- setProp(domElement, tag, propKey, propValue, false, props);
1243
+ setProp(domElement, tag, propKey, propValue, props);
1244
}
1245
}
1246
}
1248
}
1249
}
1250
668
- const isCustomElementTag = isCustomElement(tag, props);
669
- for (const propKey in props) {
670
- if (!props.hasOwnProperty(propKey)) {
671
- continue;
672
- }
673
- const propValue = props[propKey];
674
- if (propValue == null) {
675
- continue;
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);
1261
+ }
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);
1272
}
677
- setProp(domElement, tag, propKey, propValue, isCustomElementTag, props);
1273
}
1274
}
1275
1432
}
1433
// defaultChecked and defaultValue are ignored by setProp
1434
default: {
840
- setProp(domElement, tag, propKey, propValue, false, nextProps);
1435
+ setProp(domElement, tag, propKey, propValue, nextProps);
1436
}
1437
}
1438
}
1453
}
1454
// defaultValue are ignored by setProp
1455
default: {
861
- setProp(domElement, tag, propKey, propValue, false, nextProps);
1456
+ setProp(domElement, tag, propKey, propValue, nextProps);
1457
}
1458
}
1459
}
1486
}
1487
// defaultValue is ignored by setProp
1488
default: {
894
- setProp(domElement, tag, propKey, propValue, false, nextProps);
1489
+ setProp(domElement, tag, propKey, propValue, nextProps);
1490
}
1491
}
1492
}
1507
break;
1508
}
1509
default: {
915
- setProp(domElement, tag, propKey, propValue, false, nextProps);
1510
+ setProp(domElement, tag, propKey, propValue, nextProps);
1511
}
1512
}
1513
}
1546
}
1547
// defaultChecked and defaultValue are ignored by setProp
1548
default: {
954
- setProp(domElement, tag, propKey, propValue, false, nextProps);
1549
+ setProp(domElement, tag, propKey, propValue, nextProps);
1550
}
1551
}
1552
}
1554
}
1555
}
1556
962
- const isCustomElementTag = isCustomElement(tag, nextProps);
1557
// Apply the diff.
964
- for (let i = 0; i < updatePayload.length; i += 2) {
965
- const propKey = updatePayload[i];
966
- const propValue = updatePayload[i + 1];
967
- setProp(domElement, tag, propKey, propValue, isCustomElementTag, nextProps);
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
+ }
1570
}
1571
}
1572
1592
if (canDiffStyleForHydrationWarning) {
1593
const expectedStyle = createDangerousStringForStyles(value);
1594
const serverValue = domElement.getAttribute('style');
993
- if (expectedStyle !== serverValue) {
994
- warnForPropDifference('style', serverValue, expectedStyle);
1595
+ warnForPropDifference('style', serverValue, expectedStyle);
1596
+ }
1597
+}
1598
+
1599
+function hydrateAttribute(
1600
+ domElement: Element,
1601
+ propKey: string,
1602
+ attributeName: string,
1603
+ value: any,
1604
+ extraAttributes: Set<string>,
1605
+): void {
1606
+ extraAttributes.delete(attributeName);
1607
+ const serverValue = domElement.getAttribute(attributeName);
1608
+ if (serverValue === null) {
1609
+ switch (typeof value) {
1610
+ case 'undefined':
1611
+ case 'function':
1612
+ case 'symbol':
1613
+ case 'boolean':
1614
+ return;
1615
+ }
1616
+ } else {
1617
+ if (value == null) {
1618
+ // We had an attribute but shouldn't have had one, so read it
1619
+ // for the error message.
1620
+ } else {
1621
+ switch (typeof value) {
1622
+ case 'function':
1623
+ case 'symbol':
1624
+ case 'boolean':
1625
+ break;
1626
+ default: {
1627
+ if (__DEV__) {
1628
+ checkAttributeStringCoercion(value, propKey);
1629
+ }
1630
+ if (serverValue === '' + value) {
1631
+ return;
1632
+ }
1633
+ }
1634
+ }
1635
+ }
1636
+ }
1637
+ warnForPropDifference(propKey, serverValue, value);
1638
+}
1639
+
1640
+function hydrateBooleanAttribute(
1641
+ domElement: Element,
1642
+ propKey: string,
1643
+ attributeName: string,
1644
+ value: any,
1645
+ extraAttributes: Set<string>,
1646
+): void {
1647
+ extraAttributes.delete(attributeName);
1648
+ const serverValue = domElement.getAttribute(attributeName);
1649
+ if (serverValue === null) {
1650
+ switch (typeof value) {
1651
+ case 'function':
1652
+ case 'symbol':
1653
+ return;
1654
+ }
1655
+ if (!value) {
1656
+ return;
1657
+ }
1658
+ } else {
1659
+ switch (typeof value) {
1660
+ case 'function':
1661
+ case 'symbol':
1662
+ break;
1663
+ default: {
1664
+ if (value) {
1665
+ // If this was a boolean, it doesn't matter what the value is
1666
+ // the fact that we have it is the same as the expected.
1667
+ // As long as it's positive.
1668
+ return;
1669
+ }
1670
+ }
1671
+ }
1672
+ }
1673
+ warnForPropDifference(propKey, serverValue, value);
1674
+}
1675
+
1676
+function hydrateOverloadedBooleanAttribute(
1677
+ domElement: Element,
1678
+ propKey: string,
1679
+ attributeName: string,
1680
+ value: any,
1681
+ extraAttributes: Set<string>,
1682
+): void {
1683
+ extraAttributes.delete(attributeName);
1684
+ const serverValue = domElement.getAttribute(attributeName);
1685
+ if (serverValue === null) {
1686
+ switch (typeof value) {
1687
+ case 'undefined':
1688
+ case 'function':
1689
+ case 'symbol':
1690
+ return;
1691
+ default:
1692
+ if (value === false) {
1693
+ return;
1694
+ }
1695
+ }
1696
+ } else {
1697
+ if (value == null) {
1698
+ // We had an attribute but shouldn't have had one, so read it
1699
+ // for the error message.
1700
+ } else {
1701
+ switch (typeof value) {
1702
+ case 'function':
1703
+ case 'symbol':
1704
+ break;
1705
+ case 'boolean':
1706
+ if (value === true && serverValue === '') {
1707
+ return;
1708
+ }
1709
+ break;
1710
+ default: {
1711
+ if (__DEV__) {
1712
+ checkAttributeStringCoercion(value, propKey);
1713
+ }
1714
+ if (serverValue === '' + value) {
1715
+ return;
1716
+ }
1717
+ }
1718
+ }
1719
+ }
1720
+ }
1721
+ warnForPropDifference(propKey, serverValue, value);
1722
+}
1723
+
1724
+function hydrateBooleanishAttribute(
1725
+ domElement: Element,
1726
+ propKey: string,
1727
+ attributeName: string,
1728
+ value: any,
1729
+ extraAttributes: Set<string>,
1730
+): void {
1731
+ extraAttributes.delete(attributeName);
1732
+ const serverValue = domElement.getAttribute(attributeName);
1733
+ if (serverValue === null) {
1734
+ switch (typeof value) {
1735
+ case 'undefined':
1736
+ case 'function':
1737
+ case 'symbol':
1738
+ return;
1739
+ }
1740
+ } else {
1741
+ if (value == null) {
1742
+ // We had an attribute but shouldn't have had one, so read it
1743
+ // for the error message.
1744
+ } else {
1745
+ switch (typeof value) {
1746
+ case 'function':
1747
+ case 'symbol':
1748
+ break;
1749
+ default: {
1750
+ if (__DEV__) {
1751
+ checkAttributeStringCoercion(value, attributeName);
1752
+ }
1753
+ if (serverValue === '' + (value: any)) {
1754
+ return;
1755
+ }
1756
+ }
1757
+ }
1758
+ }
1759
+ }
1760
+ warnForPropDifference(propKey, serverValue, value);
1761
+}
1762
+
1763
+function hydrateNumericAttribute(
1764
+ domElement: Element,
1765
+ propKey: string,
1766
+ attributeName: string,
1767
+ value: any,
1768
+ extraAttributes: Set<string>,
1769
+): void {
1770
+ extraAttributes.delete(attributeName);
1771
+ const serverValue = domElement.getAttribute(attributeName);
1772
+ if (serverValue === null) {
1773
+ switch (typeof value) {
1774
+ case 'undefined':
1775
+ case 'function':
1776
+ case 'symbol':
1777
+ case 'boolean':
1778
+ return;
1779
+ default:
1780
+ if (isNaN(value)) {
1781
+ return;
1782
+ }
1783
+ }
1784
+ } else {
1785
+ if (value == null) {
1786
+ // We had an attribute but shouldn't have had one, so read it
1787
+ // for the error message.
1788
+ } else {
1789
+ switch (typeof value) {
1790
+ case 'function':
1791
+ case 'symbol':
1792
+ case 'boolean':
1793
+ break;
1794
+ default: {
1795
+ if (isNaN(value)) {
1796
+ // We had an attribute but shouldn't have had one, so read it
1797
+ // for the error message.
1798
+ break;
1799
+ }
1800
+ if (__DEV__) {
1801
+ checkAttributeStringCoercion(value, propKey);
1802
+ }
1803
+ if (serverValue === '' + value) {
1804
+ return;
1805
+ }
1806
+ }
1807
+ }
1808
+ }
1809
+ }
1810
+ warnForPropDifference(propKey, serverValue, value);
1811
+}
1812
+
1813
+function hydratePositiveNumericAttribute(
1814
+ domElement: Element,
1815
+ propKey: string,
1816
+ attributeName: string,
1817
+ value: any,
1818
+ extraAttributes: Set<string>,
1819
+): void {
1820
+ extraAttributes.delete(attributeName);
1821
+ const serverValue = domElement.getAttribute(attributeName);
1822
+ if (serverValue === null) {
1823
+ switch (typeof value) {
1824
+ case 'undefined':
1825
+ case 'function':
1826
+ case 'symbol':
1827
+ case 'boolean':
1828
+ return;
1829
+ default:
1830
+ if (isNaN(value) || value < 1) {
1831
+ return;
1832
+ }
1833
+ }
1834
+ } else {
1835
+ if (value == null) {
1836
+ // We had an attribute but shouldn't have had one, so read it
1837
+ // for the error message.
1838
+ } else {
1839
+ switch (typeof value) {
1840
+ case 'function':
1841
+ case 'symbol':
1842
+ case 'boolean':
1843
+ break;
1844
+ default: {
1845
+ if (isNaN(value) || value < 1) {
1846
+ // We had an attribute but shouldn't have had one, so read it
1847
+ // for the error message.
1848
+ break;
1849
+ }
1850
+ if (__DEV__) {
1851
+ checkAttributeStringCoercion(value, propKey);
1852
+ }
1853
+ if (serverValue === '' + value) {
1854
+ return;
1855
+ }
1856
+ }
1857
+ }
1858
+ }
1859
+ }
1860
+ warnForPropDifference(propKey, serverValue, value);
1861
+}
1862
+
1863
+function hydrateSanitizedAttribute(
1864
+ domElement: Element,
1865
+ propKey: string,
1866
+ attributeName: string,
1867
+ value: any,
1868
+ extraAttributes: Set<string>,
1869
+): void {
1870
+ extraAttributes.delete(attributeName);
1871
+ const serverValue = domElement.getAttribute(attributeName);
1872
+ if (serverValue === null) {
1873
+ switch (typeof value) {
1874
+ case 'undefined':
1875
+ case 'function':
1876
+ case 'symbol':
1877
+ case 'boolean':
1878
+ return;
1879
+ }
1880
+ } else {
1881
+ if (value == null) {
1882
+ // We had an attribute but shouldn't have had one, so read it
1883
+ // for the error message.
1884
+ } else {
1885
+ switch (typeof value) {
1886
+ case 'function':
1887
+ case 'symbol':
1888
+ case 'boolean':
1889
+ break;
1890
+ default: {
1891
+ if (__DEV__) {
1892
+ checkAttributeStringCoercion(value, propKey);
1893
+ }
1894
+ const sanitizedValue = sanitizeURL('' + value);
1895
+ if (serverValue === sanitizedValue) {
1896
+ return;
1897
+ }
1898
+ }
1899
+ }
1900
}
1901
}
1902
+ warnForPropDifference(propKey, serverValue, value);
1903
}
1904
1905
function diffHydratedCustomComponent(
1907
tag: string,
1908
props: Object,
1909
parentNamespaceDev: string,
1004
- extraAttributeNames: Set<string>,
1910
+ extraAttributes: Set<string>,
1911
) {
1912
for (const propKey in props) {
1913
if (!props.hasOwnProperty(propKey)) {
1914
continue;
1915
}
1010
- const nextProp = props[propKey];
1011
- if (nextProp == null) {
1916
+ const value = props[propKey];
1917
+ if (value == null) {
1918
continue;
1919
}
1920
if (registrationNameDependencies.hasOwnProperty(propKey)) {
1015
- if (typeof nextProp !== 'function') {
1016
- warnForInvalidEventListener(propKey, nextProp);
1921
+ if (typeof value !== 'function') {
1922
+ warnForInvalidEventListener(propKey, value);
1923
}
1924
continue;
1925
}
1939
continue;
1940
case 'dangerouslySetInnerHTML':
1941
const serverHTML = domElement.innerHTML;
1036
- const nextHtml = nextProp ? nextProp.__html : undefined;
1942
+ const nextHtml = value ? value.__html : undefined;
1943
if (nextHtml != null) {
1944
const expectedHTML = normalizeHTML(domElement, nextHtml);
1039
- if (expectedHTML !== serverHTML) {
1040
- warnForPropDifference(propKey, serverHTML, expectedHTML);
1041
- }
1945
+ warnForPropDifference(propKey, serverHTML, expectedHTML);
1946
}
1947
continue;
1948
case 'style':
1045
- extraAttributeNames.delete(propKey);
1046
- diffHydratedStyles(domElement, nextProp);
1949
+ extraAttributes.delete(propKey);
1950
+ diffHydratedStyles(domElement, value);
1951
continue;
1952
case 'offsetParent':
1953
case 'offsetTop':
1958
case 'outerText':
1959
case 'outerHTML':
1960
if (enableCustomElementPropertySupport) {
1057
- extraAttributeNames.delete(propKey.toLowerCase());
1961
+ extraAttributes.delete(propKey.toLowerCase());
1962
if (__DEV__) {
1963
console.error(
1964
'Assignment to read-only property will result in a no-op: `%s`',
1971
case 'className':
1972
if (enableCustomElementPropertySupport) {
1973
// className is a special cased property on the server to render as an attribute.
1070
- extraAttributeNames.delete('class');
1974
+ extraAttributes.delete('class');
1975
const serverValue = getValueForAttributeOnCustomComponent(
1976
domElement,
1977
'class',
1074
- nextProp,
1978
+ value,
1979
);
1076
- if (nextProp !== serverValue) {
1077
- warnForPropDifference('className', serverValue, nextProp);
1078
- }
1980
+ warnForPropDifference('className', serverValue, value);
1981
continue;
1982
}
1983
// eslint-disable-next-line no-fallthrough
1987
ownNamespaceDev = getIntrinsicNamespace(tag);
1988
}
1989
if (ownNamespaceDev === HTML_NAMESPACE) {
1088
- extraAttributeNames.delete(propKey.toLowerCase());
1990
+ extraAttributes.delete(propKey.toLowerCase());
1991
} else {
1090
- extraAttributeNames.delete(propKey);
1992
+ extraAttributes.delete(propKey);
1993
}
1994
const serverValue = getValueForAttributeOnCustomComponent(
1995
domElement,
1996
propKey,
1095
- nextProp,
1997
+ value,
1998
);
1097
- if (nextProp !== serverValue) {
1098
- warnForPropDifference(propKey, serverValue, nextProp);
1099
- }
1999
+ warnForPropDifference(propKey, serverValue, value);
2000
}
2001
}
2002
}
2007
tag: string,
2008
props: Object,
2009
parentNamespaceDev: string,
1110
- extraAttributeNames: Set<string>,
2010
+ extraAttributes: Set<string>,
2011
) {
2012
for (const propKey in props) {
2013
if (!props.hasOwnProperty(propKey)) {
2014
continue;
2015
}
1116
- const nextProp = props[propKey];
1117
- if (nextProp == null) {
2016
+ const value = props[propKey];
2017
+ if (value == null) {
2018
continue;
2019
}
2020
if (registrationNameDependencies.hasOwnProperty(propKey)) {
1121
- if (typeof nextProp !== 'function') {
1122
- warnForInvalidEventListener(propKey, nextProp);
2021
+ if (typeof value !== 'function') {
2022
+ warnForInvalidEventListener(propKey, value);
2023
}
2024
continue;
2025
}
2042
continue;
2043
case 'dangerouslySetInnerHTML':
2044
const serverHTML = domElement.innerHTML;
1145
- const nextHtml = nextProp ? nextProp.__html : undefined;
2045
+ const nextHtml = value ? value.__html : undefined;
2046
if (nextHtml != null) {
2047
const expectedHTML = normalizeHTML(domElement, nextHtml);
1148
- if (expectedHTML !== serverHTML) {
1149
- warnForPropDifference(propKey, serverHTML, expectedHTML);
1150
- }
2048
+ warnForPropDifference(propKey, serverHTML, expectedHTML);
2049
}
2050
continue;
2051
case 'style':
1154
- extraAttributeNames.delete(propKey);
1155
- diffHydratedStyles(domElement, nextProp);
2052
+ extraAttributes.delete(propKey);
2053
+ diffHydratedStyles(domElement, value);
2054
continue;
2055
case 'multiple': {
1158
- extraAttributeNames.delete(propKey);
2056
+ extraAttributes.delete(propKey);
2057
const serverValue = (domElement: any).multiple;
1160
- if (nextProp !== serverValue) {
1161
- warnForPropDifference('multiple', serverValue, nextProp);
1162
- }
2058
+ warnForPropDifference(propKey, serverValue, value);
2059
continue;
2060
}
2061
case 'muted': {
1166
- extraAttributeNames.delete(propKey);
2062
+ extraAttributes.delete(propKey);
2063
const serverValue = (domElement: any).muted;
1168
- if (nextProp !== serverValue) {
1169
- warnForPropDifference('muted', serverValue, nextProp);
1170
- }
2064
+ warnForPropDifference(propKey, serverValue, value);
2065
continue;
2066
}
1173
- default:
1174
- if (
1175
- // shouldIgnoreAttribute
1176
- // We have already filtered out null/undefined and reserved words.
1177
- propKey.length > 2 &&
1178
- (propKey[0] === 'o' || propKey[0] === 'O') &&
1179
- (propKey[1] === 'n' || propKey[1] === 'N')
1180
- ) {
1181
- continue;
1182
- }
1183
- const propertyInfo = getPropertyInfo(propKey);
1184
- let isMismatchDueToBadCasing = false;
1185
- let serverValue;
1186
- if (propertyInfo !== null) {
1187
- extraAttributeNames.delete(propertyInfo.attributeName);
1188
- serverValue = getValueForProperty(
1189
- domElement,
1190
- propKey,
1191
- nextProp,
1192
- propertyInfo,
1193
- );
1194
- } else {
1195
- let ownNamespaceDev = parentNamespaceDev;
1196
- if (ownNamespaceDev === HTML_NAMESPACE) {
1197
- ownNamespaceDev = getIntrinsicNamespace(tag);
1198
- }
1199
- if (ownNamespaceDev === HTML_NAMESPACE) {
1200
- extraAttributeNames.delete(propKey.toLowerCase());
1201
- } else {
1202
- const standardName = getPossibleStandardName(propKey);
1203
- if (standardName !== null && standardName !== propKey) {
1204
- // If an SVG prop is supplied with bad casing, it will
1205
- // be successfully parsed from HTML, but will produce a mismatch
1206
- // (and would be incorrectly rendered on the client).
1207
- // However, we already warn about bad casing elsewhere.
1208
- // So we'll skip the misleading extra mismatch warning in this case.
1209
- isMismatchDueToBadCasing = true;
1210
- extraAttributeNames.delete(standardName);
2067
+ case 'autoFocus': {
2068
+ extraAttributes.delete('autofocus');
2069
+ const serverValue = (domElement: any).autofocus;
2070
+ warnForPropDifference(propKey, serverValue, value);
2071
+ continue;
2072
+ }
2073
+ case 'src':
2074
+ case 'href':
2075
+ case 'action':
2076
+ if (enableFilterEmptyStringAttributesDOM) {
2077
+ if (value === '') {
2078
+ if (__DEV__) {
2079
+ if (propKey === 'src') {
2080
+ console.error(
2081
+ 'An empty string ("") was passed to the %s attribute. ' +
2082
+ 'This may cause the browser to download the whole page again over the network. ' +
2083
+ 'To fix this, either do not render the element at all ' +
2084
+ 'or pass null to %s instead of an empty string.',
2085
+ propKey,
2086
+ propKey,
2087
+ );
2088
+ } else {
2089
+ console.error(
2090
+ 'An empty string ("") was passed to the %s attribute. ' +
2091
+ 'To fix this, either do not render the element at all ' +
2092
+ 'or pass null to %s instead of an empty string.',
2093
+ propKey,
2094
+ propKey,
2095
+ );
2096
+ }
2097
}
1212
- extraAttributeNames.delete(propKey);
2098
+ hydrateSanitizedAttribute(
2099
+ domElement,
2100
+ propKey,
2101
+ propKey,
2102
+ null,
2103
+ extraAttributes,
2104
+ );
2105
+ continue;
2106
}
1214
- serverValue = getValueForAttribute(domElement, propKey, nextProp);
1215
- }
1216
-
1217
- if (nextProp !== serverValue && !isMismatchDueToBadCasing) {
1218
- warnForPropDifference(propKey, serverValue, nextProp);
2107
}
1220
- }
1221
- }
1222
-}
1223
-
1224
-export function diffHydratedProperties(
1225
- domElement: Element,
1226
- tag: string,
1227
- props: Object,
1228
- isConcurrentMode: boolean,
1229
- shouldWarnDev: boolean,
1230
- parentNamespaceDev: string,
1231
-): null | Array<mixed> {
1232
- if (__DEV__) {
1233
- validatePropertiesInDevelopment(tag, props);
1234
- }
1235
-
1236
- // TODO: Make sure that we check isMounted before firing any of these events.
1237
- switch (tag) {
1238
- case 'dialog':
1239
- listenToNonDelegatedEvent('cancel', domElement);
1240
- listenToNonDelegatedEvent('close', domElement);
1241
- break;
1242
- case 'iframe':
1243
- case 'object':
1244
- case 'embed':
1245
- // We listen to this event in case to ensure emulated bubble
1246
- // listeners still fire for the load event.
1247
- listenToNonDelegatedEvent('load', domElement);
1248
- break;
1249
- case 'video':
1250
- case 'audio':
1251
- // We listen to these events in case to ensure emulated bubble
1252
- // listeners still fire for all the media events.
1253
- for (let i = 0; i < mediaEventTypes.length; i++) {
1254
- listenToNonDelegatedEvent(mediaEventTypes[i], domElement);
2108
+ hydrateSanitizedAttribute(
2109
+ domElement,
2110
+ propKey,
2111
+ propKey,
2112
+ value,
2113
+ extraAttributes,
2114
+ );
2115
+ continue;
2116
+ case 'formAction':
2117
+ hydrateSanitizedAttribute(
2118
+ domElement,
2119
+ propKey,
2120
+ 'formaction',
2121
+ value,
2122
+ extraAttributes,
2123
+ );
2124
+ continue;
2125
+ case 'xlinkHref':
2126
+ hydrateSanitizedAttribute(
2127
+ domElement,
2128
+ propKey,
2129
+ 'xlink:href',
2130
+ value,
2131
+ extraAttributes,
2132
+ );
2133
+ continue;
2134
+ case 'contentEditable': {
2135
+ // Lower-case Booleanish String
2136
+ hydrateBooleanishAttribute(
2137
+ domElement,
2138
+ propKey,
2139
+ 'contenteditable',
2140
+ value,
2141
+ extraAttributes,
2142
+ );
2143
+ continue;
2144
}
1256
- break;
1257
- case 'source':
1258
- // We listen to this event in case to ensure emulated bubble
1259
- // listeners still fire for the error event.
1260
- listenToNonDelegatedEvent('error', domElement);
1261
- break;
1262
- case 'img':
1263
- case 'image':
1264
- case 'link':
1265
- // We listen to these events in case to ensure emulated bubble
1266
- // listeners still fire for error and load events.
1267
- listenToNonDelegatedEvent('error', domElement);
1268
- listenToNonDelegatedEvent('load', domElement);
1269
- break;
1270
- case 'details':
1271
- // We listen to this event in case to ensure emulated bubble
1272
- // listeners still fire for the toggle event.
1273
- listenToNonDelegatedEvent('toggle', domElement);
1274
- break;
1275
- case 'input':
1276
- ReactDOMInputInitWrapperState(domElement, props);
1277
- // We listen to this event in case to ensure emulated bubble
1278
- // listeners still fire for the invalid event.
1279
- listenToNonDelegatedEvent('invalid', domElement);
1280
- // TODO: Make sure we check if this is still unmounted or do any clean
1281
- // up necessary since we never stop tracking anymore.
1282
- track((domElement: any));
1283
- // For input and textarea we current always set the value property at
1284
- // post mount to force it to diverge from attributes. However, for
1285
- // option and select we don't quite do the same thing and select
1286
- // is not resilient to the DOM state changing so we don't do that here.
1287
- // TODO: Consider not doing this for input and textarea.
1288
- ReactDOMInputPostMountWrapper(domElement, props, true);
1289
- break;
1290
- case 'option':
1291
- ReactDOMOptionValidateProps(domElement, props);
1292
- break;
1293
- case 'select':
1294
- ReactDOMSelectInitWrapperState(domElement, props);
1295
- // We listen to this event in case to ensure emulated bubble
2145
+ case 'spellCheck': {
2146
+ // Lower-case Booleanish String
2147
+ hydrateBooleanishAttribute(
2148
+ domElement,
2149
+ propKey,
2150
+ 'spellcheck',
2151
+ value,
2152
+ extraAttributes,
2153
+ );
2154
+ continue;
2155
+ }
2156
+ case 'draggable':
2157
+ case 'autoReverse':
2158
+ case 'externalResourcesRequired':
2159
+ case 'focusable':
2160
+ case 'preserveAlpha': {
2161
+ // Case-sensitive Booleanish String
2162
+ hydrateBooleanishAttribute(
2163
+ domElement,
2164
+ propKey,
2165
+ propKey,
2166
+ value,
2167
+ extraAttributes,
2168
+ );
2169
+ continue;
2170
+ }
2171
+ case 'allowFullScreen':
2172
+ case 'async':
2173
+ case 'autoPlay':
2174
+ case 'controls':
2175
+ case 'default':
2176
+ case 'defer':
2177
+ case 'disabled':
2178
+ case 'disablePictureInPicture':
2179
+ case 'disableRemotePlayback':
2180
+ case 'formNoValidate':
2181
+ case 'hidden':
2182
+ case 'loop':
2183
+ case 'noModule':
2184
+ case 'noValidate':
2185
+ case 'open':
2186
+ case 'playsInline':
2187
+ case 'readOnly':
2188
+ case 'required':
2189
+ case 'reversed':
2190
+ case 'scoped':
2191
+ case 'seamless':
2192
+ case 'itemScope': {
2193
+ // Some of these need to be lower case to remove them from the extraAttributes list.
2194
+ hydrateBooleanAttribute(
2195
+ domElement,
2196
+ propKey,
2197
+ propKey.toLowerCase(),
2198
+ value,
2199
+ extraAttributes,
2200
+ );
2201
+ continue;
2202
+ }
2203
+ case 'capture':
2204
+ case 'download': {
2205
+ hydrateOverloadedBooleanAttribute(
2206
+ domElement,
2207
+ propKey,
2208
+ propKey,
2209
+ value,
2210
+ extraAttributes,
2211
+ );
2212
+ continue;
2213
+ }
2214
+ case 'cols':
2215
+ case 'rows':
2216
+ case 'size':
2217
+ case 'span': {
2218
+ hydratePositiveNumericAttribute(
2219
+ domElement,
2220
+ propKey,
2221
+ propKey,
2222
+ value,
2223
+ extraAttributes,
2224
+ );
2225
+ continue;
2226
+ }
2227
+ case 'rowSpan': {
2228
+ hydrateNumericAttribute(
2229
+ domElement,
2230
+ propKey,
2231
+ 'rowspan',
2232
+ value,
2233
+ extraAttributes,
2234
+ );
2235
+ continue;
2236
+ }
2237
+ case 'start': {
2238
+ hydrateNumericAttribute(
2239
+ domElement,
2240
+ propKey,
2241
+ propKey,
2242
+ value,
2243
+ extraAttributes,
2244
+ );
2245
+ continue;
2246
+ }
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':
2472
+ hydrateAttribute(
2473
+ domElement,
2474
+ propKey,
2475
+ 'font-stretch',
2476
+ value,
2477
+ extraAttributes,
2478
+ );
2479
+ continue;
2480
+ case 'fontStyle':
2481
+ hydrateAttribute(
2482
+ domElement,
2483
+ propKey,
2484
+ 'font-style',
2485
+ value,
2486
+ extraAttributes,
2487
+ );
2488
+ continue;
2489
+ case 'fontVariant':
2490
+ hydrateAttribute(
2491
+ domElement,
2492
+ propKey,
2493
+ 'font-variant',
2494
+ value,
2495
+ extraAttributes,
2496
+ );
2497
+ continue;
2498
+ case 'fontWeight':
2499
+ hydrateAttribute(
2500
+ domElement,
2501
+ propKey,
2502
+ 'font-weight',
2503
+ value,
2504
+ extraAttributes,
2505
+ );
2506
+ continue;
2507
+ case 'glyphName':
2508
+ hydrateAttribute(
2509
+ domElement,
2510
+ propKey,
2511
+ 'glyph-name',
2512
+ value,
2513
+ extraAttributes,
2514
+ );
2515
+ continue;
2516
+ case 'glyphOrientationHorizontal':
2517
+ hydrateAttribute(
2518
+ domElement,
2519
+ propKey,
2520
+ 'glyph-orientation-horizontal',
2521
+ value,
2522
+ extraAttributes,
2523
+ );
2524
+ continue;
2525
+ case 'glyphOrientationVertical':
2526
+ hydrateAttribute(
2527
+ domElement,
2528
+ propKey,
2529
+ 'glyph-orientation-vertical',
2530
+ value,
2531
+ extraAttributes,
2532
+ );
2533
+ continue;
2534
+ case 'horizAdvX':
2535
+ hydrateAttribute(
2536
+ domElement,
2537
+ propKey,
2538
+ 'horiz-adv-x',
2539
+ value,
2540
+ extraAttributes,
2541
+ );
2542
+ continue;
2543
+ case 'horizOriginX':
2544
+ hydrateAttribute(
2545
+ domElement,
2546
+ propKey,
2547
+ 'horiz-origin-x',
2548
+ value,
2549
+ extraAttributes,
2550
+ );
2551
+ continue;
2552
+ case 'imageRendering':
2553
+ hydrateAttribute(
2554
+ domElement,
2555
+ propKey,
2556
+ 'image-rendering',
2557
+ value,
2558
+ extraAttributes,
2559
+ );
2560
+ continue;
2561
+ case 'letterSpacing':
2562
+ hydrateAttribute(
2563
+ domElement,
2564
+ propKey,
2565
+ 'letter-spacing',
2566
+ value,
2567
+ extraAttributes,
2568
+ );
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);
3069
+ if (!isMismatchDueToBadCasing) {
3070
+ warnForPropDifference(propKey, serverValue, value);
3071
+ }
3072
+ }
3073
+ }
3074
+ }
3075
+}
3076
+
3077
+export function diffHydratedProperties(
3078
+ domElement: Element,
3079
+ tag: string,
3080
+ props: Object,
3081
+ isConcurrentMode: boolean,
3082
+ shouldWarnDev: boolean,
3083
+ parentNamespaceDev: string,
3084
+): null | Array<mixed> {
3085
+ if (__DEV__) {
3086
+ validatePropertiesInDevelopment(tag, props);
3087
+ }
3088
+
3089
+ // TODO: Make sure that we check isMounted before firing any of these events.
3090
+ switch (tag) {
3091
+ case 'dialog':
3092
+ listenToNonDelegatedEvent('cancel', domElement);
3093
+ listenToNonDelegatedEvent('close', domElement);
3094
+ break;
3095
+ case 'iframe':
3096
+ case 'object':
3097
+ case 'embed':
3098
+ // We listen to this event in case to ensure emulated bubble
3099
+ // listeners still fire for the load event.
3100
+ listenToNonDelegatedEvent('load', domElement);
3101
+ break;
3102
+ case 'video':
3103
+ case 'audio':
3104
+ // We listen to these events in case to ensure emulated bubble
3105
+ // listeners still fire for all the media events.
3106
+ for (let i = 0; i < mediaEventTypes.length; i++) {
3107
+ listenToNonDelegatedEvent(mediaEventTypes[i], domElement);
3108
+ }
3109
+ break;
3110
+ case 'source':
3111
+ // We listen to this event in case to ensure emulated bubble
3112
+ // listeners still fire for the error event.
3113
+ listenToNonDelegatedEvent('error', domElement);
3114
+ break;
3115
+ case 'img':
3116
+ case 'image':
3117
+ case 'link':
3118
+ // We listen to these events in case to ensure emulated bubble
3119
+ // listeners still fire for error and load events.
3120
+ listenToNonDelegatedEvent('error', domElement);
3121
+ listenToNonDelegatedEvent('load', domElement);
3122
+ break;
3123
+ case 'details':
3124
+ // We listen to this event in case to ensure emulated bubble
3125
+ // listeners still fire for the toggle event.
3126
+ listenToNonDelegatedEvent('toggle', domElement);
3127
+ break;
3128
+ case 'input':
3129
+ ReactDOMInputInitWrapperState(domElement, props);
3130
+ // We listen to this event in case to ensure emulated bubble
3131
+ // listeners still fire for the invalid event.
3132
+ listenToNonDelegatedEvent('invalid', domElement);
3133
+ // TODO: Make sure we check if this is still unmounted or do any clean
3134
+ // up necessary since we never stop tracking anymore.
3135
+ track((domElement: any));
3136
+ // For input and textarea we current always set the value property at
3137
+ // post mount to force it to diverge from attributes. However, for
3138
+ // option and select we don't quite do the same thing and select
3139
+ // is not resilient to the DOM state changing so we don't do that here.
3140
+ // TODO: Consider not doing this for input and textarea.
3141
+ ReactDOMInputPostMountWrapper(domElement, props, true);
3142
+ break;
3143
+ case 'option':
3144
+ ReactDOMOptionValidateProps(domElement, props);
3145
+ break;
3146
+ case 'select':
3147
+ ReactDOMSelectInitWrapperState(domElement, props);
3148
+ // We listen to this event in case to ensure emulated bubble
3149
// listeners still fire for the invalid event.
3150
listenToNonDelegatedEvent('invalid', domElement);
3151
break;
3199
}
3200
3201
if (__DEV__ && shouldWarnDev) {
1349
- const extraAttributeNames: Set<string> = new Set();
3202
+ const extraAttributes: Set<string> = new Set();
3203
const attributes = domElement.attributes;
3204
for (let i = 0; i < attributes.length; i++) {
3205
const name = attributes[i].name.toLowerCase();
3215
default:
3216
// Intentionally use the original name.
3217
// See discussion in https://github.com/facebook/react/pull/10676.
1365
- extraAttributeNames.add(attributes[i].name);
3218
+ extraAttributes.add(attributes[i].name);
3219
}
3220
}
3221
if (isCustomElement(tag, props)) {
3224
tag,
3225
props,
3226
parentNamespaceDev,
1374
- extraAttributeNames,
3227
+ extraAttributes,
3228
);
3229
} else {
3230
diffHydratedGenericElement(
3232
tag,
3233
props,
3234
parentNamespaceDev,
1382
- extraAttributeNames,
3235
+ extraAttributes,
3236
);
3237
}
1385
- if (
1386
- extraAttributeNames.size > 0 &&
1387
- props.suppressHydrationWarning !== true
1388
- ) {
1389
- warnForExtraAttributes(extraAttributeNames);
3238
+ if (extraAttributes.size > 0 && props.suppressHydrationWarning !== true) {
3239
+ warnForExtraAttributes(extraAttributes);
3240
}
3241
}
3242