@@ -16,6 +16,7 @@ import type {
1616 ObserveVisibleRectsCallback ,
1717} from 'react-reconciler/src/ReactTestSelectors' ;
1818import type { ReactScopeInstance } from 'shared/ReactTypes' ;
19+ import type { AncestorInfoDev } from './validateDOMNesting' ;
1920
2021import {
2122 precacheFiberNode ,
@@ -47,13 +48,13 @@ import {
4748} from './ReactDOMComponent' ;
4849import { getSelectionInformation , restoreSelection } from './ReactInputSelection' ;
4950import setTextContent from './setTextContent' ;
50- import { validateDOMNesting , updatedAncestorInfo } from './validateDOMNesting' ;
51+ import { validateDOMNesting , updatedAncestorInfoDev } from './validateDOMNesting' ;
5152import {
5253 isEnabled as ReactBrowserEventEmitterIsEnabled ,
5354 setEnabled as ReactBrowserEventEmitterSetEnabled ,
5455 getEventPriority ,
5556} from '../events/ReactDOMEventListener' ;
56- import { getChildNamespace } from '../shared/DOMNamespaces' ;
57+ import { getChildNamespace , SVG_NAMESPACE } from '../shared/DOMNamespaces' ;
5758import {
5859 ELEMENT_NODE ,
5960 TEXT_NODE ,
@@ -89,8 +90,8 @@ import {
8990 prepareToRenderResources ,
9091 cleanupAfterRenderResources ,
9192 clearRootResources ,
92- isHostResourceType ,
9393} from './ReactDOMFloatClient' ;
94+ import { validateLinkPropsForStyleResource } from '../shared/ReactDOMResourceValidation' ;
9495
9596export type Type = string ;
9697export type Props = {
@@ -107,6 +108,9 @@ export type Props = {
107108 top ?: null | number ,
108109 ...
109110} ;
111+ type RawProps = {
112+ [ string ] : mixed ,
113+ } ;
110114export type EventTargetChildElement = {
111115 type : string ,
112116 props : null | {
@@ -136,8 +140,7 @@ export type HydratableInstance = Instance | TextInstance | SuspenseInstance;
136140export type PublicInstance = Element | Text ;
137141type HostContextDev = {
138142 namespace : string ,
139- ancestorInfo : mixed ,
140- ...
143+ ancestorInfo : AncestorInfoDev ,
141144} ;
142145type HostContextProd = string ;
143146export type HostContext = HostContextDev | HostContextProd ;
@@ -193,7 +196,7 @@ export function getRootHostContext(
193196 }
194197 if ( __DEV__ ) {
195198 const validatedTag = type . toLowerCase ( ) ;
196- const ancestorInfo = updatedAncestorInfo ( null , validatedTag ) ;
199+ const ancestorInfo = updatedAncestorInfoDev ( null , validatedTag ) ;
197200 return { namespace, ancestorInfo} ;
198201 }
199202 return namespace ;
@@ -206,7 +209,7 @@ export function getChildHostContext(
206209 if ( __DEV__ ) {
207210 const parentHostContextDev = ( ( parentHostContext : any ) : HostContextDev ) ;
208211 const namespace = getChildNamespace ( parentHostContextDev . namespace , type ) ;
209- const ancestorInfo = updatedAncestorInfo (
212+ const ancestorInfo = updatedAncestorInfoDev (
210213 parentHostContextDev . ancestorInfo ,
211214 type ,
212215 ) ;
@@ -220,16 +223,6 @@ export function getPublicInstance(instance: Instance): Instance {
220223 return instance ;
221224}
222225
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-
233226export function prepareForCommit ( containerInfo : Container ) : Object | null {
234227 eventsEnabled = ReactBrowserEventEmitterIsEnabled ( ) ;
235228 selectionInformation = getSelectionInformation ( ) ;
@@ -287,7 +280,7 @@ export function createInstance(
287280 typeof props . children === 'number'
288281 ) {
289282 const string = '' + props . children ;
290- const ownAncestorInfo = updatedAncestorInfo (
283+ const ownAncestorInfo = updatedAncestorInfoDev (
291284 hostContextDev . ancestorInfo ,
292285 type ,
293286 ) ;
@@ -350,7 +343,7 @@ export function prepareUpdate(
350343 typeof newProps . children === 'number' )
351344 ) {
352345 const string = '' + newProps . children ;
353- const ownAncestorInfo = updatedAncestorInfo (
346+ const ownAncestorInfo = updatedAncestorInfoDev (
354347 hostContextDev . ancestorInfo ,
355348 type ,
356349 ) ;
@@ -1573,7 +1566,131 @@ export function requestPostPaintCallback(callback: (time: number) => void) {
15731566
15741567export const supportsResources = true ;
15751568
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+ }
15771694
15781695export function prepareRendererToRender ( rootContainer : Container ) {
15791696 if ( enableFloat ) {
0 commit comments