<!DOCTYPE html>
<html>
<head>
<script src="../../resources/js-test.js"></script>
<script src="resources/svgtags.js"></script>
</head>
<body>
<div id="console"></div>
<div id="container" style="position: fixed;"></div>
<script>
var svgNS = "http://www.w3.org/2000/svg";
function createNode(tag, container) {
var node = document.createElementNS(svgNS, tag);
container.appendChild(node);
return node;
}
function createValidNode(tag, container, createAncestors) {
var realContainer = container;
if (createAncestors && SvgTags[tag].needParent)
realContainer = createValidNode(SvgTags[tag].needParent, container, true);
var node = createNode(tag, realContainer);
for (var attr in SvgTags[tag].needAttr)
node.setAttribute(attr, SvgTags[tag].needAttr[attr]);
var requiredChildren = SvgTags[tag].needChild || [];
for (var childTag of requiredChildren)
createValidNode(childTag, node, false);
return node;
}
function expandClasses(tags) {
var expandedTags = [];
for (var tag of tags)
Array.prototype.push.apply(expandedTags, SvgTagClasses[tag] || [tag]);
return expandedTags;
}
function createsRenderer(parentTag, childTag) {
var divContainer = document.getElementById('container');
var svgContainer = createNode('svg', divContainer);
var parentNode = createValidNode(parentTag, svgContainer, true);
createValidNode(childTag, parentNode, false);
var drt = internals.elementLayoutTreeAsText(divContainer);
divContainer.removeChild(svgContainer);
// Skip to the line after the LayoutSVGRoot.
var skip = drt.indexOf('LayoutSVGRoot');
skip = drt.indexOf('\n', skip) + 1;
// Skip the line with the parent tag.
skip = drt.indexOf('\n', skip) + 1;
return drt.substr(skip).search(new RegExp('\\{' + childTag + '\\}|\\[' + childTag + '[\\] ]')) > 0;
}
function validateTag(tag, validChildren) {
var expandedValidChildren = expandClasses(validChildren);
var unexpectedValid = [];
var unexpectedInvalid = [];
for (var childTag in SvgTags) {
// Only testing elements with renderers.
if (SvgTags[childTag].noRenderer)
continue;
if (createsRenderer(tag, childTag)) {
if (expandedValidChildren.indexOf(childTag) < 0)
unexpectedValid.push(childTag);
} else {
if (expandedValidChildren.indexOf(childTag) >= 0)
unexpectedInvalid.push(childTag);
}
}
var msg = '<' + tag + '>: ';
var passed = (unexpectedValid.length == 0 && unexpectedInvalid.length == 0);
if (passed) {
msg += '[' + validChildren + ']';
testPassed(msg);
} else {
if (unexpectedValid.length > 0)
msg += 'unexpected valid [' + unexpectedValid + '] ';
if (unexpectedInvalid.length > 0)
msg += 'unexpected invalid [' + unexpectedInvalid + ']';
testFailed(msg);
}
}
if (!window.testRunner || !window.internals)
testFailed('this test requires DumpRenderTree.');
description('This test validates renderer instantiation against the SVG content model.');
validateTag('filter', [
// http://dev.w3.org/fxtf/filters/#FilterElement
'CLASS_DESCRIPTIVE', 'CLASS_FILTER_PRIMITIVE', 'animate', 'set'
]);
validateTag('linearGradient', [
// https://svgwg.org/svg2-draft/pservers.html#LinearGradientElement
'CLASS_DESCRIPTIVE', 'CLASS_PAINT_SERVER', 'animate', 'animateTransform', 'script', 'set',
'stop'
]);
validateTag('g', [
// https://svgwg.org/svg2-draft/struct.html#GElement
'CLASS_ANIMATION', 'CLASS_DESCRIPTIVE', 'CLASS_PAINT_SERVER', 'CLASS_SHAPE',
'CLASS_STRUCTURAL', 'a', 'clipPath', 'cursor', 'filter', 'foreignObject', 'image', 'marker',
'mask', 'script', 'style', 'switch', 'text', 'view'
]);
validateTag('radialGradient', [
// https://svgwg.org/svg2-draft/pservers.html#RadialGradientElement
'CLASS_DESCRIPTIVE', 'CLASS_PAINT_SERVER', 'animate', 'animateTransform', 'script', 'set',
'stop'
]);
validateTag('svg', [
// https://svgwg.org/svg2-draft/struct.html#SVGElement
'CLASS_ANIMATION', 'CLASS_DESCRIPTIVE', 'CLASS_PAINT_SERVER', 'CLASS_SHAPE',
'CLASS_STRUCTURAL', 'a', 'clipPath', 'cursor', 'filter', 'foreignObject', 'image', 'marker',
'mask', 'script', 'style', 'switch', 'text', 'view'
]);
</script>
</body>
</html>