@@ -16,6 +16,7 @@ import type {
16
16
ObserveVisibleRectsCallback ,
17
17
} from 'react-reconciler/src/ReactTestSelectors' ;
18
18
import type { ReactScopeInstance } from 'shared/ReactTypes' ;
19
+ import type { AncestorInfoDev } from './validateDOMNesting' ;
19
20
20
21
import {
21
22
precacheFiberNode ,
@@ -47,13 +48,13 @@ import {
47
48
} from './ReactDOMComponent' ;
48
49
import { getSelectionInformation , restoreSelection } from './ReactInputSelection' ;
49
50
import setTextContent from './setTextContent' ;
50
- import { validateDOMNesting , updatedAncestorInfo } from './validateDOMNesting' ;
51
+ import { validateDOMNesting , updatedAncestorInfoDev } from './validateDOMNesting' ;
51
52
import {
52
53
isEnabled as ReactBrowserEventEmitterIsEnabled ,
53
54
setEnabled as ReactBrowserEventEmitterSetEnabled ,
54
55
getEventPriority ,
55
56
} from '../events/ReactDOMEventListener' ;
56
- import { getChildNamespace } from '../shared/DOMNamespaces' ;
57
+ import { getChildNamespace , SVG_NAMESPACE } from '../shared/DOMNamespaces' ;
57
58
import {
58
59
ELEMENT_NODE ,
59
60
TEXT_NODE ,
@@ -89,8 +90,8 @@ import {
89
90
prepareToRenderResources ,
90
91
cleanupAfterRenderResources ,
91
92
clearRootResources ,
92
- isHostResourceType ,
93
93
} from './ReactDOMFloatClient' ;
94
+ import { validateLinkPropsForStyleResource } from '../shared/ReactDOMResourceValidation' ;
94
95
95
96
export type Type = string ;
96
97
export type Props = {
@@ -107,6 +108,9 @@ export type Props = {
107
108
top ?: null | number ,
108
109
...
109
110
} ;
111
+ type RawProps = {
112
+ [ string ] : mixed ,
113
+ } ;
110
114
export type EventTargetChildElement = {
111
115
type : string ,
112
116
props : null | {
@@ -136,8 +140,7 @@ export type HydratableInstance = Instance | TextInstance | SuspenseInstance;
136
140
export type PublicInstance = Element | Text ;
137
141
type HostContextDev = {
138
142
namespace : string ,
139
- ancestorInfo : mixed ,
140
- ...
143
+ ancestorInfo : AncestorInfoDev ,
141
144
} ;
142
145
type HostContextProd = string ;
143
146
export type HostContext = HostContextDev | HostContextProd ;
@@ -193,7 +196,7 @@ export function getRootHostContext(
193
196
}
194
197
if ( __DEV__ ) {
195
198
const validatedTag = type . toLowerCase ( ) ;
196
- const ancestorInfo = updatedAncestorInfo ( null , validatedTag ) ;
199
+ const ancestorInfo = updatedAncestorInfoDev ( null , validatedTag ) ;
197
200
return { namespace, ancestorInfo} ;
198
201
}
199
202
return namespace ;
@@ -206,7 +209,7 @@ export function getChildHostContext(
206
209
if ( __DEV__ ) {
207
210
const parentHostContextDev = ( ( parentHostContext : any ) : HostContextDev ) ;
208
211
const namespace = getChildNamespace ( parentHostContextDev . namespace , type ) ;
209
- const ancestorInfo = updatedAncestorInfo (
212
+ const ancestorInfo = updatedAncestorInfoDev (
210
213
parentHostContextDev . ancestorInfo ,
211
214
type ,
212
215
) ;
@@ -220,16 +223,6 @@ export function getPublicInstance(instance: Instance): Instance {
220
223
return instance ;
221
224
}
222
225
223
- export function getNamespace ( hostContext : HostContext ) : string {
224
- if ( __DEV__ ) {
225
- const hostContextDev : HostContextDev = ( hostContext : any ) ;
226
- return hostContextDev . namespace ;
227
- } else {
228
- const hostContextProd : HostContextProd = ( hostContext : any ) ;
229
- return hostContextProd ;
230
- }
231
- }
232
-
233
226
export function prepareForCommit ( containerInfo : Container ) : Object | null {
234
227
eventsEnabled = ReactBrowserEventEmitterIsEnabled ( ) ;
235
228
selectionInformation = getSelectionInformation ( ) ;
@@ -287,7 +280,7 @@ export function createInstance(
287
280
typeof props . children === 'number'
288
281
) {
289
282
const string = '' + props . children ;
290
- const ownAncestorInfo = updatedAncestorInfo (
283
+ const ownAncestorInfo = updatedAncestorInfoDev (
291
284
hostContextDev . ancestorInfo ,
292
285
type ,
293
286
) ;
@@ -350,7 +343,7 @@ export function prepareUpdate(
350
343
typeof newProps . children === 'number' )
351
344
) {
352
345
const string = '' + newProps . children ;
353
- const ownAncestorInfo = updatedAncestorInfo (
346
+ const ownAncestorInfo = updatedAncestorInfoDev (
354
347
hostContextDev . ancestorInfo ,
355
348
type ,
356
349
) ;
@@ -1573,7 +1566,131 @@ export function requestPostPaintCallback(callback: (time: number) => void) {
1573
1566
1574
1567
export const supportsResources = true ;
1575
1568
1576
- export { isHostResourceType} ;
1569
+ export function isHostResourceType (
1570
+ type : string ,
1571
+ props : RawProps ,
1572
+ hostContext : HostContext ,
1573
+ ) : boolean {
1574
+ let outsideHostContainerContext : boolean ;
1575
+ let namespace : string ;
1576
+ if ( __DEV__ ) {
1577
+ const hostContextDev : HostContextDev = ( hostContext : any ) ;
1578
+ // We can only render resources when we are not within the host container context
1579
+ outsideHostContainerContext = ! hostContextDev . ancestorInfo
1580
+ . containerTagInScope ;
1581
+ namespace = hostContextDev . namespace ;
1582
+ } else {
1583
+ const hostContextProd : HostContextProd = ( hostContext : any ) ;
1584
+ namespace = hostContextProd ;
1585
+ }
1586
+ switch ( type ) {
1587
+ case 'base' :
1588
+ case 'meta' : {
1589
+ return true ;
1590
+ }
1591
+ case 'title' : {
1592
+ return namespace !== SVG_NAMESPACE ;
1593
+ }
1594
+ case 'link' : {
1595
+ const { onLoad, onError} = props ;
1596
+ if ( onLoad || onError ) {
1597
+ if ( __DEV__ ) {
1598
+ if ( outsideHostContainerContext ) {
1599
+ console . error (
1600
+ 'Cannot render a <link> with onLoad or onError listeners outside the main document.' +
1601
+ ' Try removing onLoad={...} and onError={...} or moving it into the root <head> tag or' +
1602
+ ' somewhere in the <body>.' ,
1603
+ ) ;
1604
+ } else if ( namespace === SVG_NAMESPACE ) {
1605
+ console . error (
1606
+ 'Cannot render a <link> with onLoad or onError listeners as a descendent of <svg>.' +
1607
+ ' Try removing onLoad={...} and onError={...} or moving it above the <svg> ancestor.' ,
1608
+ ) ;
1609
+ }
1610
+ }
1611
+ return false ;
1612
+ }
1613
+ switch ( props . rel ) {
1614
+ case 'stylesheet' : {
1615
+ const { href, precedence, disabled} = props ;
1616
+ if ( __DEV__ ) {
1617
+ validateLinkPropsForStyleResource ( props ) ;
1618
+ if ( typeof precedence !== 'string' ) {
1619
+ if ( outsideHostContainerContext ) {
1620
+ console . error (
1621
+ 'Cannot render a <link rel="stylesheet" /> outside the main document without knowing its precedence.' +
1622
+ ' Consider adding precedence="default" or moving it into the root <head> tag.' ,
1623
+ ) ;
1624
+ } else if ( namespace === SVG_NAMESPACE ) {
1625
+ console . error (
1626
+ 'Cannot render a <link rel="stylesheet" /> as a descendent of an <svg> element without knowing its precedence.' +
1627
+ ' Consider adding precedence="default" or moving it above the <svg> ancestor.' ,
1628
+ ) ;
1629
+ }
1630
+ }
1631
+ }
1632
+ return (
1633
+ typeof href === 'string' &&
1634
+ typeof precedence === 'string' &&
1635
+ disabled == null
1636
+ ) ;
1637
+ }
1638
+ default : {
1639
+ const { rel, href} = props ;
1640
+ return typeof href === 'string' && typeof rel === 'string' ;
1641
+ }
1642
+ }
1643
+ }
1644
+ case 'script' : {
1645
+ // We don't validate because it is valid to use async with onLoad/onError unlike combining
1646
+ // precedence with these for style resources
1647
+ const { src, async, onLoad, onError} = props ;
1648
+ if ( __DEV__ ) {
1649
+ if ( async !== true ) {
1650
+ if ( outsideHostContainerContext ) {
1651
+ console . error (
1652
+ 'Cannot render a sync or defer <script> outside the main document without knowing its order.' +
1653
+ ' Try adding async="" or moving it into the root <head> tag.' ,
1654
+ ) ;
1655
+ } else if ( namespace === SVG_NAMESPACE ) {
1656
+ console . error (
1657
+ 'Cannot render a sync or defer <script> as a descendent of an <svg> element.' +
1658
+ ' Try adding async="" or moving it above the ancestor <svg> element.' ,
1659
+ ) ;
1660
+ }
1661
+ } else if ( onLoad || onError ) {
1662
+ if ( outsideHostContainerContext ) {
1663
+ console . error (
1664
+ 'Cannot render a <script> with onLoad or onError listeners outside the main document.' +
1665
+ ' Try removing onLoad={...} and onError={...} or moving it into the root <head> tag or' +
1666
+ ' somewhere in the <body>.' ,
1667
+ ) ;
1668
+ } else if ( namespace === SVG_NAMESPACE ) {
1669
+ console . error (
1670
+ 'Cannot render a <script> with onLoad or onError listeners as a descendent of an <svg> element.' +
1671
+ ' Try removing onLoad={...} and onError={...} or moving it above the ancestor <svg> element.' ,
1672
+ ) ;
1673
+ }
1674
+ }
1675
+ }
1676
+ return ( async : any ) && typeof src === 'string' && ! onLoad && ! onError ;
1677
+ }
1678
+ case 'noscript' :
1679
+ case 'template' :
1680
+ case 'style' : {
1681
+ if ( __DEV__ ) {
1682
+ if ( outsideHostContainerContext ) {
1683
+ console . error (
1684
+ 'Cannot render <%s> outside the main document. Try moving it into the root <head> tag.' ,
1685
+ type ,
1686
+ ) ;
1687
+ }
1688
+ }
1689
+ return false ;
1690
+ }
1691
+ }
1692
+ return false ;
1693
+ }
1577
1694
1578
1695
export function prepareRendererToRender ( rootContainer : Container ) {
1579
1696
if ( enableFloat ) {
0 commit comments