/** * 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