@@ -71,6 +71,7 @@ export type AncestorInfoDev = {
71
71
72
72
// <head> or <body>
73
73
containerTagInScope : ?Info ,
74
+ implicitBodyScope : boolean ,
74
75
} ;
75
76
76
77
// This validation code was written based on the HTML5 parsing spec:
@@ -219,16 +220,19 @@ const emptyAncestorInfoDev: AncestorInfoDev = {
219
220
dlItemTagAutoclosing : null ,
220
221
221
222
containerTagInScope : null ,
223
+ implicitBodyScope : false ,
222
224
} ;
223
225
224
226
function updatedAncestorInfoDev (
225
- oldInfo : ? AncestorInfoDev ,
227
+ oldInfo : null | AncestorInfoDev ,
226
228
tag : string ,
227
229
) : AncestorInfoDev {
228
230
if ( __DEV__ ) {
229
231
const ancestorInfo = { ...( oldInfo || emptyAncestorInfoDev ) } ;
230
232
const info = { tag} ;
231
233
234
+ ancestorInfo . implicitBodyScope = false ;
235
+
232
236
if ( inScopeTags . indexOf ( tag ) !== - 1 ) {
233
237
ancestorInfo . aTagInScope = null ;
234
238
ancestorInfo . buttonTagInScope = null ;
@@ -238,14 +242,14 @@ function updatedAncestorInfoDev(
238
242
ancestorInfo . pTagInButtonScope = null ;
239
243
}
240
244
241
- // See rules for 'li', 'dd', 'dt' start tags in
242
- // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
243
245
if (
244
246
specialTags . indexOf ( tag ) !== - 1 &&
245
247
tag !== 'address' &&
246
248
tag !== 'div' &&
247
249
tag !== 'p'
248
250
) {
251
+ // See rules for 'li', 'dd', 'dt' start tags in
252
+ // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
249
253
ancestorInfo . listItemTagAutoclosing = null ;
250
254
ancestorInfo . dlItemTagAutoclosing = null ;
251
255
}
@@ -274,6 +278,10 @@ function updatedAncestorInfoDev(
274
278
ancestorInfo . dlItemTagAutoclosing = info ;
275
279
}
276
280
if ( tag === '#document' || tag === 'html' ) {
281
+ if ( oldInfo === null ) {
282
+ // We're at the root with implicit body scope
283
+ ancestorInfo . implicitBodyScope = true ;
284
+ }
277
285
ancestorInfo . containerTagInScope = null ;
278
286
} else if ( ! ancestorInfo . containerTagInScope ) {
279
287
ancestorInfo . containerTagInScope = info ;
@@ -363,11 +371,16 @@ function isTagValidWithParent(tag: string, parentTag: ?string): boolean {
363
371
) ;
364
372
// https://html.spec.whatwg.org/multipage/semantics.html#the-html-element
365
373
case 'html' :
366
- return tag === 'head' || tag === 'body' || tag === 'frameset' ;
374
+ return (
375
+ tag === 'head' ||
376
+ tag === 'body' ||
377
+ tag === 'frameset' ||
378
+ parentTag === null
379
+ ) ;
367
380
case 'frameset' :
368
381
return tag === 'frame' ;
369
382
case '#document' :
370
- return tag === 'html' ;
383
+ return tag === 'html' || parentTag === null ;
371
384
}
372
385
373
386
// Probably in the "in body" parsing mode, so we outlaw only tag combos
@@ -511,7 +524,13 @@ function validateDOMNesting(
511
524
if ( __DEV__ ) {
512
525
ancestorInfo = ancestorInfo || emptyAncestorInfoDev ;
513
526
const parentInfo = ancestorInfo . current ;
514
- const parentTag = parentInfo && parentInfo . tag ;
527
+ const parentTag =
528
+ parentInfo &&
529
+ // If we are in implicit body scope we validate as if we have no parent.
530
+ // This is because we expect the host to insert the tag into the necessary context
531
+ ancestorInfo . implicitBodyScope === false
532
+ ? parentInfo . tag
533
+ : null ;
515
534
516
535
const invalidParent = isTagValidWithParent ( childTag , parentTag )
517
536
? null
0 commit comments