}
}
} else if ("track" == localName && atts.getIndex("", "default") >= 0) {
for (Map.Entry<StackNode, TaintableLocatorImpl> entry : openMediaElements.entrySet()) {
StackNode node = entry.getKey();
TaintableLocatorImpl locator = entry.getValue();
if (node.isTrackDescendant()) {
err("The \u201Cdefault\u201D attribute must not occur"
+ " on more than one \u201Ctrack\u201D element"
+ " within the same \u201Caudio\u201D or"
+ " \u201Cvideo\u201D element.");
if (!locator.isTainted()) {
warn("\u201Caudio\u201D or \u201Cvideo\u201D element"
+ " has more than one \u201Ctrack\u201D child"
+ " element with a \u201Cdefault\u201D attribute.",
locator);
locator.markTainted();
}
} else {
node.setTrackDescendants();
}
}
} else if ("main" == localName) {
if (hasMain) {
err("A document must not include more than one"
+ " \u201Cmain\u201D element.");
}
hasMain = true;
} else if ("h1" == localName) {
if (currentSectioningDepth > 1) {
warn(h1WarningMessage);
} else if (currentSectioningDepth == 1) {
secondLevelH1s.add(new LocatorImpl(getDocumentLocator()));
} else {
hasTopLevelH1 = true;
}
}
// progress
else if ("progress" == localName) {
double value = getDoubleAttribute(atts, "value");
if (!Double.isNaN(value)) {
double max = getDoubleAttribute(atts, "max");
if (Double.isNaN(max)) {
if (!(value <= 1.0)) {
err("The value of the \u201Cvalue\u201D attribute must be less than or equal to one when the \u201Cmax\u201D attribute is absent.");
}
} else {
if (!(value <= max)) {
err("The value of the \u201Cvalue\u201D attribute must be less than or equal to the value of the \u201Cmax\u201D attribute.");
}
}
}
}
// meter
else if ("meter" == localName) {
double value = getDoubleAttribute(atts, "value");
double min = getDoubleAttribute(atts, "min");
double max = getDoubleAttribute(atts, "max");
double optimum = getDoubleAttribute(atts, "optimum");
double low = getDoubleAttribute(atts, "low");
double high = getDoubleAttribute(atts, "high");
if (!Double.isNaN(min) && !Double.isNaN(value)
&& !(min <= value)) {
err("The value of the \u201Cmin\u201D attribute must be less than or equal to the value of the \u201Cvalue\u201D attribute.");
}
if (Double.isNaN(min) && !Double.isNaN(value) && !(0 <= value)) {
err("The value of the \u201Cvalue\u201D attribute must be greater than or equal to zero when the \u201Cmin\u201D attribute is absent.");
}
if (!Double.isNaN(value) && !Double.isNaN(max)
&& !(value <= max)) {
err("The value of the \u201Cvalue\u201D attribute must be less than or equal to the value of the \u201Cmax\u201D attribute.");
}
if (!Double.isNaN(value) && Double.isNaN(max) && !(value <= 1)) {
err("The value of the \u201Cvalue\u201D attribute must be less than or equal to one when the \u201Cmax\u201D attribute is absent.");
}
if (!Double.isNaN(min) && !Double.isNaN(max) && !(min <= max)) {
err("The value of the \u201Cmin\u201D attribute must be less than or equal to the value of the \u201Cmax\u201D attribute.");
}
if (Double.isNaN(min) && !Double.isNaN(max) && !(0 <= max)) {
err("The value of the \u201Cmax\u201D attribute must be greater than or equal to zero when the \u201Cmin\u201D attribute is absent.");
}
if (!Double.isNaN(min) && Double.isNaN(max) && !(min <= 1)) {
err("The value of the \u201Cmin\u201D attribute must be less than or equal to one when the \u201Cmax\u201D attribute is absent.");
}
if (!Double.isNaN(min) && !Double.isNaN(low) && !(min <= low)) {
err("The value of the \u201Cmin\u201D attribute must be less than or equal to the value of the \u201Clow\u201D attribute.");
}
if (Double.isNaN(min) && !Double.isNaN(low) && !(0 <= low)) {
err("The value of the \u201Clow\u201D attribute must be greater than or equal to zero when the \u201Cmin\u201D attribute is absent.");
}
if (!Double.isNaN(min) && !Double.isNaN(high) && !(min <= high)) {
err("The value of the \u201Cmin\u201D attribute must be less than or equal to the value of the \u201Chigh\u201D attribute.");
}
if (Double.isNaN(min) && !Double.isNaN(high) && !(0 <= high)) {
err("The value of the \u201Chigh\u201D attribute must be greater than or equal to zero when the \u201Cmin\u201D attribute is absent.");
}
if (!Double.isNaN(low) && !Double.isNaN(high) && !(low <= high)) {
err("The value of the \u201Clow\u201D attribute must be less than or equal to the value of the \u201Chigh\u201D attribute.");
}
if (!Double.isNaN(high) && !Double.isNaN(max) && !(high <= max)) {
err("The value of the \u201Chigh\u201D attribute must be less than or equal to the value of the \u201Cmax\u201D attribute.");
}
if (!Double.isNaN(high) && Double.isNaN(max) && !(high <= 1)) {
err("The value of the \u201Chigh\u201D attribute must be less than or equal to one when the \u201Cmax\u201D attribute is absent.");
}
if (!Double.isNaN(low) && !Double.isNaN(max) && !(low <= max)) {
err("The value of the \u201Clow\u201D attribute must be less than or equal to the value of the \u201Cmax\u201D attribute.");
}
if (!Double.isNaN(low) && Double.isNaN(max) && !(low <= 1)) {
err("The value of the \u201Clow\u201D attribute must be less than or equal to one when the \u201Cmax\u201D attribute is absent.");
}
if (!Double.isNaN(min) && !Double.isNaN(optimum)
&& !(min <= optimum)) {
err("The value of the \u201Cmin\u201D attribute must be less than or equal to the value of the \u201Coptimum\u201D attribute.");
}
if (Double.isNaN(min) && !Double.isNaN(optimum)
&& !(0 <= optimum)) {
err("The value of the \u201Coptimum\u201D attribute must be greater than or equal to zero when the \u201Cmin\u201D attribute is absent.");
}
if (!Double.isNaN(optimum) && !Double.isNaN(max)
&& !(optimum <= max)) {
err("The value of the \u201Coptimum\u201D attribute must be less than or equal to the value of the \u201Cmax\u201D attribute.");
}
if (!Double.isNaN(optimum) && Double.isNaN(max)
&& !(optimum <= 1)) {
err("The value of the \u201Coptimum\u201D attribute must be less than or equal to one when the \u201Cmax\u201D attribute is absent.");
}
}
// map required attrs
else if ("map" == localName && id != null) {
String nameVal = atts.getValue("", "name");
if (nameVal != null && !nameVal.equals(id)) {
err("The \u201Cid\u201D attribute on a \u201Cmap\u201D element must have an the same value as the \u201Cname\u201D attribute.");
}
}
// script
else if ("script" == localName) {
// script language
if (languageJavaScript && typeNotTextJavaScript) {
err("A \u201Cscript\u201D element with the \u201Clanguage=\"JavaScript\"\u201D attribute set must not have a \u201Ctype\u201D attribute whose value is not \u201Ctext/javascript\u201D.");
}
// src-less script
if (atts.getIndex("", "src") < 0) {
if (atts.getIndex("", "charset") >= 0) {
err("Element \u201Cscript\u201D must not have attribute \u201Ccharset\u201D unless attribute \u201Csrc\u201D is also specified.");
}
if (atts.getIndex("", "defer") >= 0) {
err("Element \u201Cscript\u201D must not have attribute \u201Cdefer\u201D unless attribute \u201Csrc\u201D is also specified.");
}
if (atts.getIndex("", "async") >= 0) {
err("Element \u201Cscript\u201D must not have attribute \u201Casync\u201D unless attribute \u201Csrc\u201D is also specified.");
}
}
}
// bdo required attrs
else if ("bdo" == localName && atts.getIndex("", "dir") < 0) {
err("Element \u201Cbdo\u201D must have attribute \u201Cdir\u201D.");
}
// lang and xml:lang for XHTML5
if (lang != null && xmlLang != null
&& !equalsIgnoreAsciiCase(lang, xmlLang)) {
err("When the attribute \u201Clang\u201D in no namespace and the attribute \u201Clang\u201D in the XML namespace are both present, they must have the same value.");
}
// contextmenu
if (contextmenu != null) {
contextmenuReferences.add(new IdrefLocator(new LocatorImpl(
getDocumentLocator()), contextmenu));
}
if ("menu" == localName) {
menuIds.addAll(ids);
}
if (role != null && owns != null) {
for (Set<String> value : REQUIRED_ROLE_ANCESTOR_BY_DESCENDANT.values()) {
if (value.contains(role)) {
String[] ownedIds = AttributeUtil.split(owns);
for (int i = 0; i < ownedIds.length; i++) {
Set<String> ownedIdsForThisRole = ariaOwnsIdsByRole.get(role);
if (ownedIdsForThisRole == null) {
ownedIdsForThisRole = new HashSet<String>();
}
ownedIdsForThisRole.add(ownedIds[i]);
ariaOwnsIdsByRole.put(role, ownedIdsForThisRole);
}
break;
}
}
}
if ("datalist" == localName) {
listIds.addAll(ids);
}
// label for
if ("label" == localName) {
String forVal = atts.getValue("", "for");
if (forVal != null) {
formControlReferences.add(new IdrefLocator(new LocatorImpl(
getDocumentLocator()), forVal));
}
}
if ("form" == localName) {
formElementIds.addAll(ids);
}
if (("input" == localName && !hidden) || "textarea" == localName
|| "select" == localName || "button" == localName
|| "keygen" == localName || "output" == localName) {
formControlIds.addAll(ids);
}
if ("button" == localName || "fieldset" == localName
|| ("input" == localName && !hidden)
|| "keygen" == localName || "label" == localName
|| "object" == localName || "output" == localName
|| "select" == localName || "textarea" == localName) {
String formVal = atts.getValue("", "form");
if (formVal != null) {
formElementReferences.add(new IdrefLocator(
new LocatorImpl(getDocumentLocator()), formVal));
}
}
// input list
if ("input" == localName && list != null) {
listReferences.add(new IdrefLocator(new LocatorImpl(
getDocumentLocator()), list));
}
// input@type=button
if ("input" == localName
&& lowerCaseLiteralEqualsIgnoreAsciiCaseString("button",
atts.getValue("", "type"))) {
if (atts.getValue("", "value") == null
|| "".equals(atts.getValue("", "value"))) {
err("Element \u201Cinput\u201D with attribute \u201Ctype\u201D whose value is \u201Cbutton\u201D must have non-empty attribute \u201Cvalue\u201D.");
}
}
// track
if ("track" == localName) {
if ("".equals(atts.getValue("", "label"))) {
err("Attribute \u201Clabel\u201D for element \u201Ctrack\u201D must have non-empty value.");
}
}
// multiple selected options
if ("option" == localName && selected) {
for (Map.Entry<StackNode, Locator> entry : openSingleSelects.entrySet()) {
StackNode node = entry.getKey();
if (node.isSelectedOptions()) {
err("The \u201Cselect\u201D element cannot have more than one selected \u201Coption\u201D descendant unless the \u201Cmultiple\u201D attribute is specified.");
} else {
node.setSelectedOptions();
}
}
}
if ("meta" == localName) {
if (lowerCaseLiteralEqualsIgnoreAsciiCaseString(
"content-language", atts.getValue("", "http-equiv"))) {
err("Using the \u201Cmeta\u201D element to specify the"
+ " document-wide default language is obsolete."
+ " Consider specifying the language on the root"
+ " element instead.");
}
}
// microdata
if (itemid && !(itemscope && itemtype)) {
err("The \u201Citemid\u201D attribute must not be specified on elements that do not have both an \u201Citemscope\u201D attribute and an \u201Citemtype\u201D attribute specified.");
}
if (itemref && !itemscope) {
err("The \u201Citemref\u201D attribute must not be specified on elements that do not have an \u201Citemscope\u201D attribute specified.");
}
if (itemtype && !itemscope) {
err("The \u201Citemtype\u201D attribute must not be specified on elements that do not have an \u201Citemscope\u201D attribute specified.");
}
} else {
int len = atts.getLength();
for (int i = 0; i < len; i++) {
if (atts.getType(i) == "ID") {
String attVal = atts.getValue(i);
if (attVal.length() != 0) {
ids.add(attVal);
}
}
String attLocal = atts.getLocalName(i);
if (atts.getURI(i).length() == 0) {
if ("role" == attLocal) {
role = atts.getValue(i);
} else if ("aria-activedescendant" == attLocal) {
activeDescendant = atts.getValue(i);
} else if ("aria-owns" == attLocal) {
owns = atts.getValue(i);
}
}
}
allIds.addAll(ids);
}
// ARIA required owner/ancestors
Set<String> requiredAncestorRoles = REQUIRED_ROLE_ANCESTOR_BY_DESCENDANT.get(role);
if (requiredAncestorRoles != null && !"presentation".equals(parentRole)
&& !"tbody".equals(localName) && !"tfoot".equals(localName)
&& !"thead".equals(localName)) {
if (!currentElementHasRequiredAncestorRole(requiredAncestorRoles)) {
if (atts.getIndex("", "id") > -1
&& !"".equals(atts.getValue("", "id"))) {
needsAriaOwner.add(new IdrefLocator(new LocatorImpl(
getDocumentLocator()), atts.getValue("", "id"),
role));
} else {
errContainedInOrOwnedBy(role, getDocumentLocator());
}
}
}
// ARIA IDREFS
for (String att : MUST_NOT_DANGLE_IDREFS) {
String attVal = atts.getValue("", att);
if (attVal != null) {
String[] tokens = AttributeUtil.split(attVal);
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i];
ariaReferences.add(new IdrefLocator(getDocumentLocator(),
token, att));
}
}
}
allIds.addAll(ids);
// aria-activedescendant accompanied by aria-owns
if (activeDescendant != null && !"".equals(activeDescendant)) {
// String activeDescendantVal = atts.getValue("",
// "aria-activedescendant");
if (owns != null && !"".equals(owns)) {
activeDescendantWithAriaOwns = true;
// String[] tokens = AttributeUtil.split(owns);
// for (int i = 0; i < tokens.length; i++) {
// String token = tokens[i];
// if (token.equals(activeDescendantVal)) {
// activeDescendantWithAriaOwns = true;
// break;
// }
// }
}
}
// activedescendant
for (Iterator<Map.Entry<StackNode, Locator>> iterator = openActiveDescendants.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<StackNode, Locator> entry = iterator.next();
if (ids.contains(entry.getKey().getActiveDescendant())) {
iterator.remove();
}
}
if ("http://www.w3.org/1999/xhtml" == uri) {
int number = specialAncestorNumber(localName);
if (number > -1) {
ancestorMask |= (1 << number);
}
if ("a" == localName && href) {
ancestorMask |= HREF_MASK;
}
StackNode child = new StackNode(ancestorMask, localName, role,
activeDescendant, forAttr);
if (activeDescendant != null && !activeDescendantWithAriaOwns) {
openActiveDescendants.put(child, new LocatorImpl(
getDocumentLocator()));
}
if ("select" == localName && atts.getIndex("", "multiple") == -1) {
openSingleSelects.put(child, getDocumentLocator());
} else if ("label" == localName) {
openLabels.put(child, new LocatorImpl(getDocumentLocator()));
} else if ("video" == localName || "audio" == localName ) {
openMediaElements.put(child, new TaintableLocatorImpl(getDocumentLocator()));
}
push(child);
if ("select" == localName && atts.getIndex("", "required") > -1
&& atts.getIndex("", "multiple") < 0) {
if (atts.getIndex("", "size") > -1) {