/**
* Copyright 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule validateDOMNesting
*/
'use strict';
var emptyFunction = require('emptyFunction');
var warning = require('warning');
var validateDOMNesting = emptyFunction;
if (__DEV__) {
// This validation code was written based on the HTML5 parsing spec:
// https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
//
// Note: this does not catch all invalid nesting, nor does it try to (as it's
// not clear what practical benefit doing so provides); instead, we warn only
// for cases where the parser will give a parse tree differing from what React
// intended. For example, is invalid but we don't warn
// because it still parses correctly; we do warn for other cases like nested
//
tags where the beginning of the second element implicitly closes the
// first, causing a confusing mess.
// https://html.spec.whatwg.org/multipage/syntax.html#special
var specialTags = [
'address', 'applet', 'area', 'article', 'aside', 'base', 'basefont',
'bgsound', 'blockquote', 'body', 'br', 'button', 'caption', 'center', 'col',
'colgroup', 'dd', 'details', 'dir', 'div', 'dl', 'dt', 'embed', 'fieldset',
'figcaption', 'figure', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2',
'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'iframe',
'img', 'input', 'isindex', 'li', 'link', 'listing', 'main', 'marquee',
'menu', 'menuitem', 'meta', 'nav', 'noembed', 'noframes', 'noscript',
'object', 'ol', 'p', 'param', 'plaintext', 'pre', 'script', 'section',
'select', 'source', 'style', 'summary', 'table', 'tbody', 'td', 'template',
'textarea', 'tfoot', 'th', 'thead', 'title', 'tr', 'track', 'ul', 'wbr',
'xmp'
];
/**
* Return whether `stack` contains `tag` and the last occurrence of `tag` is
* deeper than any element in the `scope` array.
*
* https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-the-specific-scope
*
* Examples:
* stackHasTagInSpecificScope(['p', 'quote'], 'p', ['button']) is true
* stackHasTagInSpecificScope(['p', 'button'], 'p', ['button']) is false
*
* @param {Array} stack
* @param {string} tag
* @param {Array} scope
*/
var stackHasTagInSpecificScope = function(stack, tag, scope) {
for (var i = stack.length - 1; i >= 0; i--) {
if (stack[i] === tag) {
return true;
}
if (scope.indexOf(stack[i]) !== -1) {
return false;
}
}
return false;
};
// https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
var inScopeTags = [
'applet', 'caption', 'html', 'table', 'td', 'th', 'marquee', 'object',
'template',
// https://html.spec.whatwg.org/multipage/syntax.html#html-integration-point
// TODO: Distinguish by namespace here
'foreignObject', 'desc', 'title'
];
var stackHasTagInScope = function(stack, tag) {
return stackHasTagInSpecificScope(stack, tag, inScopeTags);
};
// https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-button-scope
var buttonScopeTags = inScopeTags.concat(['button']);
var stackHasTagInButtonScope = function(stack, tag) {
return stackHasTagInSpecificScope(stack, tag, buttonScopeTags);
};
// See rules for 'li', 'dd', 'dt' start tags in
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
var listItemTagAllowed = function(tags, stack) {
// tags is ['li'] or ['dd, 'dt']
for (var i = stack.length - 1; i >= 0; i--) {
if (tags.indexOf(stack[i]) !== -1) {
return false;
} else if (
specialTags.indexOf(stack[i]) !== -1 &&
stack[i] !== 'address' && stack[i] !== 'div' && stack[i] !== 'p'
) {
return true;
}
}
return true;
};
// https://html.spec.whatwg.org/multipage/syntax.html#generate-implied-end-tags
var impliedEndTags =
['dd', 'dt', 'li', 'option', 'optgroup', 'p', 'rp', 'rt'];
/**
* Returns whether we allow putting `tag` in the document if the current stack
* of open tags is `openTagStack`.
*
* Examples:
* isTagValidInContext('tr', [..., 'table', 'tbody']) is true
* isTagValidInContext('tr', [..., 'table']) is false
*
* @param {string} tag Lowercase HTML tag name or node name like '#text'
* @param {Array} openTagStack
*/
var isTagValidInContext = function(tag, openTagStack) {
var currentTag = openTagStack[openTagStack.length - 1];
// First, let's check if we're in an unusual parsing mode...
switch (currentTag) {
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inselect
case 'select':
return tag === 'option' || tag === 'optgroup' || tag === '#text';
case 'optgroup':
return tag === 'option' || tag === '#text';
// Strictly speaking, seeing an