use of org.w3c.css.css.StyleSheetParser in project validator by validator.
the class Assertions method endElement.
/**
* @see nu.validator.checker.Checker#endElement(java.lang.String,
* java.lang.String, java.lang.String)
*/
@Override
public void endElement(String uri, String localName, String name) throws SAXException {
if ("http://www.w3.org/1999/xhtml" == uri && "template".equals(localName)) {
numberOfTemplatesDeep--;
if (numberOfTemplatesDeep != 0) {
return;
}
} else if (numberOfTemplatesDeep > 0) {
return;
}
StackNode node = pop();
String systemId = node.locator().getSystemId();
String publicId = node.locator().getPublicId();
Locator locator = null;
openSingleSelects.remove(node);
openLabels.remove(node);
openMediaElements.remove(node);
if ("http://www.w3.org/1999/xhtml" == uri) {
if ("figure" == localName) {
if ((node.needsFigcaption() && !node.hasFigcaptionContent()) || node.hasTextNode() || node.hasEmbeddedContent()) {
for (Locator imgLocator : node.getImagesLackingAlt()) {
err("An \u201Cimg\u201D element must have an" + " \u201Calt\u201D attribute, except under" + " certain conditions. For details, consult" + " guidance on providing text alternatives" + " for images.", imgLocator);
}
}
} else if ("picture" == localName) {
siblingSources.clear();
} else if ("select" == localName && node.isOptionNeeded()) {
if (!node.hasOption()) {
err("A \u201Cselect\u201D element with a" + " \u201Crequired\u201D attribute, and without a" + " \u201Cmultiple\u201D attribute, and without a" + " \u201Csize\u201D attribute whose value is" + " greater than" + " \u201C1\u201D, must have a child" + " \u201Coption\u201D element.");
}
if (node.nonEmptyOptionLocator() != null) {
err("The first child \u201Coption\u201D element of a" + " \u201Cselect\u201D element with a" + " \u201Crequired\u201D attribute, and without a" + " \u201Cmultiple\u201D attribute, and without a" + " \u201Csize\u201D attribute whose value is" + " greater than" + " \u201C1\u201D, must have either an empty" + " \u201Cvalue\u201D attribute, or must have no" + " text content." + " Consider either adding a placeholder option" + " label, or adding a" + " \u201Csize\u201D attribute with a value equal" + " to the number of" + " \u201Coption\u201D elements.", node.nonEmptyOptionLocator());
}
} else if ("section" == localName && !node.hasHeading()) {
warn("Section lacks heading. Consider using" + " \u201ch2\u201d-\u201ch6\u201d elements to add" + " identifying headings to all sections.", node.locator());
} else if ("article" == localName && !node.hasHeading()) {
warn("Article lacks heading. Consider using" + " \u201ch2\u201d-\u201ch6\u201d elements to add" + " identifying headings to all articles.", node.locator());
} else if (("h1" == localName || "h2" == localName || "h3" == localName || "h4" == localName || "h5" == localName || "h6" == localName) && !node.hasTextNode() && !node.hasImg()) {
warn("Empty heading.", node.locator());
} else if ("option" == localName && !stack[currentPtr].hasOption()) {
stack[currentPtr].setOptionFound();
} else if ("style" == localName) {
String styleContents = node.getTextContent().toString();
int lineOffset = 0;
if (styleContents.startsWith("\n")) {
lineOffset = 1;
}
ApplContext ac = new ApplContext("en");
ac.setCssVersionAndProfile("css3svg");
ac.setMedium("all");
ac.setTreatVendorExtensionsAsWarnings(true);
ac.setTreatCssHacksAsWarnings(true);
ac.setWarningLevel(-1);
ac.setFakeURL("file://localhost/StyleElement");
StyleSheetParser styleSheetParser = new StyleSheetParser();
styleSheetParser.parseStyleSheet(ac, new StringReader(styleContents.substring(lineOffset)), null);
styleSheetParser.getStyleSheet().findConflicts(ac);
Errors errors = styleSheetParser.getStyleSheet().getErrors();
if (errors.getErrorCount() > 0) {
incrementUseCounter("style-element-errors-found");
}
for (int i = 0; i < errors.getErrorCount(); i++) {
String message = "";
String cssProperty = "";
String cssMessage = "";
CssError error = errors.getErrorAt(i);
int beginLine = error.getBeginLine() + lineOffset;
int beginColumn = error.getBeginColumn();
int endLine = error.getEndLine() + lineOffset;
int endColumn = error.getEndColumn();
if (beginLine == 0) {
continue;
}
Throwable ex = error.getException();
if (ex instanceof CssParseException) {
CssParseException cpe = (CssParseException) ex;
if (//
"generator.unrecognize".equals(cpe.getErrorType())) {
cssMessage = "Parse Error";
}
if (cpe.getProperty() != null) {
cssProperty = String.format("\u201c%s\u201D: ", cpe.getProperty());
}
if (cpe.getMessage() != null) {
cssMessage = cpe.getMessage();
}
if (!"".equals(cssMessage)) {
message = cssProperty + cssMessage + ".";
}
} else {
message = ex.getMessage();
}
if (!"".equals(message)) {
int lastLine = //
node.locator.getLineNumber() + endLine - 1;
int lastColumn = endColumn;
int columnOffset = node.locator.getColumnNumber();
if (error.getBeginLine() == 1) {
if (lineOffset != 0) {
columnOffset = 0;
}
} else {
columnOffset = 0;
}
String prefix = sourceIsCss ? "" : "CSS: ";
SAXParseException spe = new //
SAXParseException(//
prefix + message, //
publicId, //
systemId, lastLine, lastColumn);
int[] start = { node.locator.getLineNumber() + beginLine - 1, beginColumn, columnOffset };
if ((getErrorHandler() instanceof MessageEmitterAdapter) && !(getErrorHandler() instanceof TestRunner)) {
//
((MessageEmitterAdapter) getErrorHandler()).errorWithStart(spe, start);
} else {
getErrorHandler().error(spe);
}
}
}
}
if ("article" == localName || "aside" == localName || "nav" == localName || "section" == localName) {
currentSectioningElementPtr = currentPtr - 1;
currentSectioningDepth--;
}
}
if ((locator = openActiveDescendants.remove(node)) != null) {
warn("Attribute \u201Caria-activedescendant\u201D value should " + "either refer to a descendant element, or should " + "be accompanied by attribute \u201Caria-owns\u201D.", locator);
}
}
use of org.w3c.css.css.StyleSheetParser in project validator by validator.
the class Assertions method startElement.
/**
* @see nu.validator.checker.Checker#startElement(java.lang.String,
* java.lang.String, java.lang.String, org.xml.sax.Attributes)
*/
@Override
public void startElement(String uri, String localName, String name, Attributes atts) throws SAXException {
if ("http://www.w3.org/1999/xhtml" == uri && "template".equals(localName)) {
numberOfTemplatesDeep++;
if (numberOfTemplatesDeep != 1) {
return;
}
} else if (numberOfTemplatesDeep > 0) {
return;
}
Set<String> ids = new HashSet<>();
String role = null;
String inputTypeVal = null;
String activeDescendant = null;
String owns = null;
String forAttr = null;
boolean href = false;
boolean activeDescendantWithAriaOwns = false;
// see nu.validator.datatype.ImageCandidateStrings
System.setProperty("nu.validator.checker.imageCandidateString.hasWidth", "0");
StackNode parent = peek();
int ancestorMask = 0;
String parentRole = null;
String parentName = null;
if (parent != null) {
ancestorMask = parent.getAncestorMask();
parentName = parent.getName();
parentRole = parent.getRole();
}
if ("http://www.w3.org/1999/xhtml" == uri) {
boolean controls = false;
boolean hidden = false;
boolean toolbar = false;
boolean usemap = false;
boolean ismap = false;
boolean selected = false;
boolean itemid = false;
boolean itemref = false;
boolean itemscope = false;
boolean itemtype = false;
boolean tabindex = false;
boolean languageJavaScript = false;
boolean typeNotTextJavaScript = false;
String xmlLang = null;
String lang = null;
String id = null;
String list = null;
int len = atts.getLength();
for (int i = 0; i < len; i++) {
String attUri = atts.getURI(i);
if (attUri.length() == 0) {
String attLocal = atts.getLocalName(i);
if ("embed".equals(localName)) {
for (int j = 0; j < attLocal.length(); j++) {
char c = attLocal.charAt(j);
if (c >= 'A' && c <= 'Z') {
err("Bad attribute name \u201c" + attLocal + "\u201d. Attribute names for the" + " \u201cembed\u201d element must not" + " contain uppercase ASCII letters.");
}
}
if (!NCName.isNCName(attLocal)) {
err("Bad attribute name \u201c" + attLocal + "\u201d. Attribute names for the" + " \u201cembed\u201d element must be" + " XML-compatible.");
}
}
if ("style" == attLocal) {
String styleContents = atts.getValue(i);
ApplContext ac = new ApplContext("en");
ac.setCssVersionAndProfile("css3svg");
ac.setMedium("all");
ac.setTreatVendorExtensionsAsWarnings(true);
ac.setTreatCssHacksAsWarnings(true);
ac.setWarningLevel(-1);
ac.setFakeURL("file://localhost/StyleAttribute");
//
StyleSheetParser styleSheetParser = new StyleSheetParser();
styleSheetParser.parseStyleAttribute(ac, new ByteArrayInputStream(styleContents.getBytes()), "", ac.getFakeURL(), getDocumentLocator().getLineNumber());
styleSheetParser.getStyleSheet().findConflicts(ac);
//
Errors errors = styleSheetParser.getStyleSheet().getErrors();
if (errors.getErrorCount() > 0) {
incrementUseCounter("style-attribute-errors-found");
}
for (int j = 0; j < errors.getErrorCount(); j++) {
String message = "";
String cssProperty = "";
String cssMessage = "";
CssError error = errors.getErrorAt(j);
Throwable ex = error.getException();
if (ex instanceof CssParseException) {
CssParseException cpe = (CssParseException) ex;
if (//
"generator.unrecognize".equals(cpe.getErrorType())) {
cssMessage = "Parse Error";
}
if (cpe.getProperty() != null) {
cssProperty = String.format("\u201c%s\u201D: ", cpe.getProperty());
}
if (cpe.getMessage() != null) {
cssMessage = cpe.getMessage();
}
if (!"".equals(cssMessage)) {
message = cssProperty + cssMessage + ".";
}
} else {
message = ex.getMessage();
}
if (!"".equals(message)) {
err("CSS: " + message);
}
}
} else if ("tabindex" == attLocal) {
tabindex = true;
} else if ("href" == attLocal) {
href = true;
} else if ("controls" == attLocal) {
controls = true;
} else if ("type" == attLocal && "param" != localName && "ol" != localName && "ul" != localName && "li" != localName) {
if ("input" == localName) {
inputTypeVal = atts.getValue(i).toLowerCase();
}
String attValue = atts.getValue(i);
if (AttributeUtil.lowerCaseLiteralEqualsIgnoreAsciiCaseString("hidden", attValue)) {
hidden = true;
} else if (AttributeUtil.lowerCaseLiteralEqualsIgnoreAsciiCaseString("toolbar", attValue)) {
toolbar = true;
}
if (!AttributeUtil.lowerCaseLiteralEqualsIgnoreAsciiCaseString("text/javascript", attValue)) {
typeNotTextJavaScript = true;
}
} else 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);
} else if ("list" == attLocal) {
list = atts.getValue(i);
} else if ("lang" == attLocal) {
lang = atts.getValue(i);
} else if ("id" == attLocal) {
id = atts.getValue(i);
} else if ("for" == attLocal && "label" == localName) {
forAttr = atts.getValue(i);
ancestorMask |= LABEL_FOR_MASK;
} else if ("ismap" == attLocal) {
ismap = true;
} else if ("selected" == attLocal) {
selected = true;
} else if ("usemap" == attLocal && "input" != localName) {
usemap = true;
} else if ("itemid" == attLocal) {
itemid = true;
} else if ("itemref" == attLocal) {
itemref = true;
} else if ("itemscope" == attLocal) {
itemscope = true;
} else if ("itemtype" == attLocal) {
itemtype = true;
} else if ("language" == attLocal && AttributeUtil.lowerCaseLiteralEqualsIgnoreAsciiCaseString("javascript", atts.getValue(i))) {
languageJavaScript = true;
} else if ("rev" == attLocal && !("1".equals(System.getProperty("nu.validator.schema.rdfa-full")))) {
errObsoleteAttribute("rev", localName, " Use the \u201Crel\u201D attribute instead," + " with a term having the opposite meaning.");
} else if (OBSOLETE_ATTRIBUTES.containsKey(attLocal) && "ol" != localName && "ul" != localName && "li" != localName) {
String[] elementNames = OBSOLETE_ATTRIBUTES.get(attLocal);
Arrays.sort(elementNames);
if (Arrays.binarySearch(elementNames, localName) >= 0) {
String suggestion = OBSOLETE_ATTRIBUTES_MSG.containsKey(attLocal) ? " " + OBSOLETE_ATTRIBUTES_MSG.get(attLocal) : "";
errObsoleteAttribute(attLocal, localName, suggestion);
}
} else if (OBSOLETE_STYLE_ATTRS.containsKey(attLocal)) {
String[] elementNames = OBSOLETE_STYLE_ATTRS.get(attLocal);
Arrays.sort(elementNames);
if (Arrays.binarySearch(elementNames, localName) >= 0) {
errObsoleteAttribute(attLocal, localName, " Use CSS instead.");
}
} else if (INPUT_ATTRIBUTES.containsKey(attLocal) && "input" == localName) {
String[] allowedTypes = INPUT_ATTRIBUTES.get(attLocal);
Arrays.sort(allowedTypes);
inputTypeVal = inputTypeVal == null ? "text" : inputTypeVal;
if (Arrays.binarySearch(allowedTypes, inputTypeVal) < 0) {
err("Attribute \u201c" + attLocal + "\u201d is only allowed when the input" + " type is " + renderTypeList(allowedTypes) + ".");
}
} else if ("autofocus" == attLocal) {
if (hasAutofocus) {
err("A document must not include more than one" + " \u201Cautofocus\u201D attribute.");
}
hasAutofocus = true;
} else if (ATTRIBUTES_WITH_IMPLICIT_STATE_OR_PROPERTY.contains(attLocal)) {
String stateOrProperty = "aria-" + attLocal;
if (atts.getIndex("", stateOrProperty) > -1 && "true".equals(atts.getValue("", stateOrProperty))) {
warn("Attribute \u201C" + stateOrProperty + "\u201D is unnecessary for elements that" + " have attribute \u201C" + attLocal + "\u201D.");
}
}
} else if ("http://www.w3.org/XML/1998/namespace" == attUri) {
if ("lang" == atts.getLocalName(i)) {
xmlLang = atts.getValue(i);
}
}
if (atts.getType(i) == "ID" || "id" == atts.getLocalName(i)) {
String attVal = atts.getValue(i);
if (attVal.length() != 0) {
ids.add(attVal);
}
}
}
if (followW3Cspec) {
if (("input".equals(localName) || "textarea".equals(localName)) && atts.getIndex("", "inputmode") > -1) {
err("The \u201cinputmode\u201d attribute is not allowed by" + " the W3C HTML specification.");
}
}
if ("input".equals(localName)) {
if (atts.getIndex("", "name") > -1 && "isindex".equals(atts.getValue("", "name"))) {
err("The value \u201cisindex\u201d for the \u201cname\u201d" + " attribute of the \u201cinput\u201d element is" + " not allowed.");
}
inputTypeVal = inputTypeVal == null ? "text" : inputTypeVal;
if (atts.getIndex("", "autocomplete") > -1) {
Class<?> datatypeClass = null;
String autocompleteVal = atts.getValue("", "autocomplete");
try {
if (!"on".equals(autocompleteVal) && !"off".equals(autocompleteVal)) {
if ("hidden".equals(inputTypeVal)) {
AutocompleteDetailsAny.THE_INSTANCE.checkValid(autocompleteVal);
datatypeClass = AutocompleteDetailsAny.class;
} else if ("text".equals(inputTypeVal) || "search".equals(autocompleteVal)) {
AutocompleteDetailsText.THE_INSTANCE.checkValid(autocompleteVal);
datatypeClass = AutocompleteDetailsText.class;
} else if ("password".equals(inputTypeVal)) {
AutocompleteDetailsPassword.THE_INSTANCE.checkValid(autocompleteVal);
datatypeClass = AutocompleteDetailsPassword.class;
} else if ("url".equals(inputTypeVal)) {
AutocompleteDetailsUrl.THE_INSTANCE.checkValid(autocompleteVal);
datatypeClass = AutocompleteDetailsUrl.class;
} else if ("email".equals(inputTypeVal)) {
AutocompleteDetailsEmail.THE_INSTANCE.checkValid(autocompleteVal);
datatypeClass = AutocompleteDetailsEmail.class;
} else if ("tel".equals(inputTypeVal)) {
AutocompleteDetailsTel.THE_INSTANCE.checkValid(autocompleteVal);
datatypeClass = AutocompleteDetailsTel.class;
} else if ("number".equals(inputTypeVal)) {
AutocompleteDetailsNumeric.THE_INSTANCE.checkValid(autocompleteVal);
datatypeClass = AutocompleteDetailsNumeric.class;
} else if ("month".equals(inputTypeVal)) {
AutocompleteDetailsMonth.THE_INSTANCE.checkValid(autocompleteVal);
datatypeClass = AutocompleteDetailsMonth.class;
} else if ("date".equals(inputTypeVal)) {
AutocompleteDetailsDate.THE_INSTANCE.checkValid(autocompleteVal);
datatypeClass = AutocompleteDetailsDate.class;
}
}
} catch (DatatypeException e) {
try {
if (getErrorHandler() != null) {
String msg = e.getMessage();
msg = msg.substring(msg.indexOf(": ") + 2);
VnuBadAttrValueException ex = new VnuBadAttrValueException(localName, uri, "autocomplete", autocompleteVal, msg, getDocumentLocator(), datatypeClass, false);
getErrorHandler().error(ex);
}
} catch (ClassNotFoundException ce) {
}
}
}
}
if ("img".equals(localName) || "source".equals(localName)) {
if (atts.getIndex("", "srcset") > -1) {
String srcsetVal = atts.getValue("", "srcset");
try {
if (atts.getIndex("", "sizes") > -1) {
ImageCandidateStringsWidthRequired.THE_INSTANCE.checkValid(srcsetVal);
} else {
ImageCandidateStrings.THE_INSTANCE.checkValid(srcsetVal);
}
// see nu.validator.datatype.ImageCandidateStrings
if ("1".equals(System.getProperty("nu.validator.checker.imageCandidateString.hasWidth"))) {
if (atts.getIndex("", "sizes") < 0) {
err("When the \u201csrcset\u201d attribute has" + " any image candidate string with a" + " width descriptor, the" + " \u201csizes\u201d attribute" + " must also be present.");
}
}
} catch (DatatypeException e) {
Class<?> datatypeClass = ImageCandidateStrings.class;
if (atts.getIndex("", "sizes") > -1) {
datatypeClass = ImageCandidateStringsWidthRequired.class;
}
try {
if (getErrorHandler() != null) {
String msg = e.getMessage();
if (e instanceof Html5DatatypeException) {
Html5DatatypeException ex5 = (Html5DatatypeException) e;
if (!ex5.getDatatypeClass().equals(ImageCandidateURL.class)) {
msg = msg.substring(msg.indexOf(": ") + 2);
}
}
VnuBadAttrValueException ex = new VnuBadAttrValueException(localName, uri, "srcset", srcsetVal, msg, getDocumentLocator(), datatypeClass, false);
getErrorHandler().error(ex);
}
} catch (ClassNotFoundException ce) {
}
}
if ("picture".equals(parentName) && !siblingSources.isEmpty()) {
for (Map.Entry<Locator, Map<String, String>> entry : siblingSources.entrySet()) {
Locator locator = entry.getKey();
Map<String, String> sourceAtts = entry.getValue();
String media = sourceAtts.get("media");
if (media == null && sourceAtts.get("type") == null) {
err("A \u201csource\u201d element that has a" + " following sibling" + " \u201csource\u201d element or" + " \u201cimg\u201d element with a" + " \u201csrcset\u201d attribute" + " must have a" + " \u201cmedia\u201d attribute and/or" + " \u201ctype\u201d attribute.", locator);
siblingSources.remove(locator);
} else if (media != null && AttributeUtil.lowerCaseLiteralEqualsIgnoreAsciiCaseString("all", trimSpaces(media))) {
err("Value of \u201cmedia\u201d attribute here" + " must not be \u201call\u201d.", locator);
}
}
}
} else if (atts.getIndex("", "sizes") > -1) {
err("The \u201csizes\u201d attribute may be specified" + " only if the \u201csrcset\u201d attribute is" + " also present.");
}
}
if ("picture".equals(parentName) && "source".equals(localName)) {
Map<String, String> sourceAtts = new HashMap<>();
for (int i = 0; i < atts.getLength(); i++) {
sourceAtts.put(atts.getLocalName(i), atts.getValue(i));
}
siblingSources.put((new LocatorImpl(getDocumentLocator())), sourceAtts);
}
if ("figure" == localName) {
currentFigurePtr = currentPtr + 1;
}
if ((ancestorMask & FIGURE_MASK) != 0) {
if ("img" == localName) {
if (stack[currentFigurePtr].hasImg()) {
stack[currentFigurePtr].setEmbeddedContentFound();
} else {
stack[currentFigurePtr].setImgFound();
}
} else if ("audio" == localName || "canvas" == localName || "embed" == localName || "iframe" == localName || "math" == localName || "object" == localName || "svg" == localName || "video" == localName) {
stack[currentFigurePtr].setEmbeddedContentFound();
}
}
if ("article" == localName || "aside" == localName || "nav" == localName || "section" == localName) {
currentSectioningElementPtr = currentPtr + 1;
currentSectioningDepth++;
}
if ("h1" == localName || "h2" == localName || "h3" == localName || "h4" == localName || "h5" == localName || "h6" == localName) {
currentHeadingPtr = currentPtr + 1;
if (currentSectioningElementPtr > -1) {
stack[currentSectioningElementPtr].setHeadingFound();
}
}
if (((ancestorMask & H1_MASK) != 0 || (ancestorMask & H2_MASK) != 0 || (ancestorMask & H3_MASK) != 0 || (ancestorMask & H4_MASK) != 0 || (ancestorMask & H5_MASK) != 0 || (ancestorMask & H6_MASK) != 0) && "img" == localName && atts.getIndex("", "alt") > -1 && !"".equals(atts.getValue("", "alt"))) {
stack[currentHeadingPtr].setImgFound();
}
if ("option" == localName && !parent.hasOption()) {
if (atts.getIndex("", "value") < 0) {
parent.setNoValueOptionFound();
} else if (atts.getIndex("", "value") > -1 && "".equals(atts.getValue("", "value"))) {
parent.setEmptyValueOptionFound();
} else {
parent.setNonEmptyOption((new LocatorImpl(getDocumentLocator())));
}
}
// Obsolete elements
if (OBSOLETE_ELEMENTS.get(localName) != null) {
err("The \u201C" + localName + "\u201D element is obsolete. " + OBSOLETE_ELEMENTS.get(localName));
}
// Exclusions
Integer maskAsObject;
int mask = 0;
String descendantUiString = "The element \u201C" + localName + "\u201D";
if ((maskAsObject = ANCESTOR_MASK_BY_DESCENDANT.get(localName)) != null) {
mask = maskAsObject.intValue();
} else if ("video" == localName && controls) {
mask = A_BUTTON_MASK;
descendantUiString = "The element \u201Cvideo\u201D with the" + " attribute \u201Ccontrols\u201D";
checkForInteractiveAncestorRole(descendantUiString);
} else if ("audio" == localName && controls) {
mask = A_BUTTON_MASK;
descendantUiString = "The element \u201Caudio\u201D with the" + " attribute \u201Ccontrols\u201D";
checkForInteractiveAncestorRole(descendantUiString);
} else if ("menu" == localName && toolbar) {
mask = A_BUTTON_MASK;
descendantUiString = "The element \u201Cmenu\u201D with the" + " attribute \u201Ctype=toolbar\u201D";
checkForInteractiveAncestorRole(descendantUiString);
} else if ("img" == localName && usemap) {
mask = A_BUTTON_MASK;
descendantUiString = "The element \u201Cimg\u201D with the" + " attribute \u201Cusemap\u201D";
checkForInteractiveAncestorRole(descendantUiString);
} else if ("object" == localName && usemap) {
mask = A_BUTTON_MASK;
descendantUiString = "The element \u201Cobject\u201D with the" + " attribute \u201Cusemap\u201D";
checkForInteractiveAncestorRole(descendantUiString);
} else if ("input" == localName && !hidden) {
mask = A_BUTTON_MASK;
checkForInteractiveAncestorRole(descendantUiString);
} else if (tabindex) {
mask = A_BUTTON_MASK;
descendantUiString = "An element with the attribute" + " \u201Ctabindex\u201D";
checkForInteractiveAncestorRole(descendantUiString);
} else if (role != null && role != "" && Arrays.binarySearch(INTERACTIVE_ROLES, role) >= 0) {
mask = A_BUTTON_MASK;
descendantUiString = "An element with the attribute \u201C" + "role=" + role + "\u201D";
checkForInteractiveAncestorRole(descendantUiString);
}
if (mask != 0) {
int maskHit = ancestorMask & mask;
if (maskHit != 0) {
for (String ancestor : SPECIAL_ANCESTORS) {
if ((maskHit & 1) != 0) {
err(descendantUiString + " must not appear as a" + " descendant of the \u201C" + ancestor + "\u201D element.");
}
maskHit >>= 1;
}
}
}
if (Arrays.binarySearch(INTERACTIVE_ELEMENTS, localName) >= 0) {
checkForInteractiveAncestorRole("The element \u201C" + localName + "\u201D");
}
// Ancestor requirements/restrictions
if ("area" == localName && ((ancestorMask & MAP_MASK) == 0)) {
err("The \u201Carea\u201D element must have a \u201Cmap\u201D ancestor.");
} else if ("img" == localName) {
String titleVal = atts.getValue("", "title");
if (ismap && ((ancestorMask & HREF_MASK) == 0)) {
err("The \u201Cimg\u201D element with the " + "\u201Cismap\u201D attribute set must have an " + "\u201Ca\u201D ancestor with the " + "\u201Chref\u201D attribute.");
}
if (atts.getIndex("", "alt") < 0) {
if (followW3Cspec || (titleVal == null || "".equals(titleVal))) {
if ((ancestorMask & FIGURE_MASK) == 0) {
err("An \u201Cimg\u201D element must have an" + " \u201Calt\u201D attribute, except under" + " certain conditions. For details, consult" + " guidance on providing text alternatives" + " for images.");
} else {
stack[currentFigurePtr].setFigcaptionNeeded();
stack[currentFigurePtr].addImageLackingAlt(new LocatorImpl(getDocumentLocator()));
}
}
} else {
if ("".equals(atts.getValue("", "alt")) && role != null) {
List<String> roles = Arrays.asList(//
role.trim().toLowerCase().split("\\s+"));
if (!roles.contains("none") && !roles.contains("presentation")) {
err("An \u201Cimg\u201D element which has an" + " \u201Calt\u201D attribute whose value" + " is the empty string must not have a" + " \u201Crole\u201D attribute with any" + " value other than \u201Cnone\u201D or" + " \u201Cpresentation\u201D");
}
}
}
} else if ("table" == localName) {
if (atts.getIndex("", "summary") >= 0) {
errObsoleteAttribute("summary", "table", " Consider describing the structure of the" + " \u201Ctable\u201D in a \u201Ccaption\u201D " + " element or in a \u201Cfigure\u201D element " + " containing the \u201Ctable\u201D; or," + " simplify the structure of the" + " \u201Ctable\u201D so that no description" + " is needed.");
}
if (atts.getIndex("", "border") > -1) {
if (followW3Cspec) {
if (atts.getIndex("", "border") > -1 && (!("".equals(atts.getValue("", "border")) || "1".equals(atts.getValue("", "border"))))) {
errObsoleteAttribute("border", "table", " Use CSS instead.");
} else {
warnPresentationalAttribute("border", "table", " For example: \u201Ctable, td, th { border: 1px solid gray }\u201D");
}
} else {
errObsoleteAttribute("border", "table", " Use CSS instead.");
}
}
} 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) {
for (int i = 0; i < currentPtr; i++) {
String ancestorName = stack[currentPtr - i].getName();
if (Arrays.binarySearch(PROHIBITED_MAIN_ANCESTORS, ancestorName) >= 0) {
err("The \u201Cmain\u201D element must not appear as a" + " descendant of the \u201C" + ancestorName + "\u201D element.");
}
}
if (atts.getIndex("", "hidden") < 0) {
if (hasVisibleMain) {
err("A document must not include more than one visible" + " \u201Cmain\u201D element.");
}
hasVisibleMain = true;
}
} else if ("h1" == localName) {
if (currentSectioningDepth > 1) {
warn(h1WarningMessage);
} else if (currentSectioningDepth == 1) {
secondLevelH1s.add(new LocatorImpl(getDocumentLocator()));
} else {
hasTopLevelH1 = true;
}
} else // progress
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.");
}
}
}
} else // meter
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.");
}
} else // map required attrs
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.");
}
} else if ("object" == localName) {
if (atts.getIndex("", "typemustmatch") >= 0) {
if ((atts.getIndex("", "data") < 0) || (atts.getIndex("", "type") < 0)) {
{
err("Element \u201Cobject\u201D must not have" + " attribute \u201Ctypemustmatch\u201D unless" + " both attribute \u201Cdata\u201D" + " and attribute \u201Ctype\u201D are also specified.");
}
}
}
} else // script
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.");
}
if (atts.getIndex("", "charset") >= 0) {
warnObsoleteAttribute("charset", "script", "");
if (!"utf-8".equals(atts.getValue("", "charset").toLowerCase())) {
err("The only allowed value for the \u201Ccharset\u201D" + " attribute for the \u201Cscript\u201D" + " element is \u201Cutf-8\u201D. (But the" + " attribute is not needed and should be" + " omitted altogether.)");
}
}
// 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) {
if (!(//
atts.getIndex("", "type") > -1 && "module".equals(//
atts.getValue("", "type").toLowerCase()))) {
err("Element \u201Cscript\u201D must not have" + " attribute \u201Casync\u201D unless" + " attribute \u201Csrc\u201D is also" + " specified or unless attribute" + " \u201Ctype\u201D is specified with" + " value \u201Cmodule\u201D.");
}
}
if (atts.getIndex("", "integrity") >= 0) {
err("Element \u201Cscript\u201D must not have attribute" + " \u201Cintegrity\u201D unless attribute" + " \u201Csrc\u201D is also specified.");
}
}
if (atts.getIndex("", "type") > -1) {
String scriptType = atts.getValue("", "type").toLowerCase();
if (JAVASCRIPT_MIME_TYPES.contains(scriptType) || "".equals(scriptType)) {
warn("The \u201Ctype\u201D attribute is unnecessary for" + " JavaScript resources.");
}
if ("module".equals(scriptType)) {
if (atts.getIndex("", "integrity") > -1) {
err("A \u201Cscript\u201D element with an" + " \u201Cintegrity\u201D attribute must not have a" + " \u201Ctype\u201D attribute with the value" + " \u201Cmodule\u201D.");
}
if (atts.getIndex("", "defer") > -1) {
err("A \u201Cscript\u201D element with a" + " \u201Cdefer\u201D attribute must not have a" + " \u201Ctype\u201D attribute with the value" + " \u201Cmodule\u201D.");
}
if (atts.getIndex("", "nomodule") > -1) {
err("A \u201Cscript\u201D element with a" + " \u201Cnomodule\u201D attribute must not have a" + " \u201Ctype\u201D attribute with the value" + " \u201Cmodule\u201D.");
}
}
}
} else if ("style" == localName) {
if (atts.getIndex("", "type") > -1) {
String styleType = atts.getValue("", "type").toLowerCase();
if ("text/css".equals(styleType)) {
warn(" The \u201Ctype\u201D attribute for the" + " \u201Cstyle\u201D element is not needed and" + " should be omitted.");
} else {
err(" The only allowed value for the \u201Ctype\u201D" + " attribute for the \u201Cstyle\u201D element" + " is \u201Ctext/css\u201D (with no" + " parameters). (But the attribute is not" + " needed and should be omitted altogether.)");
}
}
} else // bdo required attrs
if ("bdo" == localName && atts.getIndex("", "dir") < 0) {
err("Element \u201Cbdo\u201D must have attribute \u201Cdir\u201D.");
}
// labelable elements
if ("button" == localName || ("input" == localName && !AttributeUtil.lowerCaseLiteralEqualsIgnoreAsciiCaseString("hidden", atts.getValue("", "type"))) || "meter" == localName || "output" == localName || "progress" == localName || "select" == localName || "textarea" == localName) {
for (Map.Entry<StackNode, Locator> entry : openLabels.entrySet()) {
StackNode node = entry.getKey();
Locator locator = entry.getValue();
if (node.isLabeledDescendants()) {
err("The \u201Clabel\u201D element may contain at most" + " one \u201Cbutton\u201D, \u201Cinput\u201D," + " \u201Cmeter\u201D, \u201Coutput\u201D," + " \u201Cprogress\u201D, \u201Cselect\u201D," + " or \u201Ctextarea\u201D descendant.");
warn("\u201Clabel\u201D element with multiple labelable" + " descendants.", locator);
} else {
node.setLabeledDescendants();
}
}
if ((ancestorMask & LABEL_FOR_MASK) != 0) {
boolean hasMatchingFor = false;
for (int i = 0; (stack[currentPtr - i].getAncestorMask() & LABEL_FOR_MASK) != 0; i++) {
String forVal = stack[currentPtr - i].getForAttr();
if (forVal != null && forVal.equals(id)) {
hasMatchingFor = true;
break;
}
}
if (id == null || !hasMatchingFor) {
err("Any \u201C" + localName + "\u201D descendant of a \u201Clabel\u201D" + " element with a \u201Cfor\u201D attribute" + " must have an ID value that matches that" + " \u201Cfor\u201D attribute.");
}
}
}
// 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.");
}
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 (String ownedId : ownedIds) {
Set<String> ownedIdsForThisRole = ariaOwnsIdsByRole.get(role);
if (ownedIdsForThisRole == null) {
ownedIdsForThisRole = new HashSet<>();
}
ownedIdsForThisRole.add(ownedId);
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 ((//
"button" == localName || //
"input" == localName && !hidden) || //
"meter" == localName || //
"output" == localName || //
"progress" == localName || //
"select" == localName || "textarea" == localName) {
formControlIds.addAll(ids);
}
if ("button" == localName || "fieldset" == localName || ("input" == localName && !hidden) || "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 && AttributeUtil.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 (AttributeUtil.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.");
} else if (AttributeUtil.lowerCaseLiteralEqualsIgnoreAsciiCaseString("x-ua-compatible", atts.getValue("", "http-equiv")) && !AttributeUtil.lowerCaseLiteralEqualsIgnoreAsciiCaseString("ie=edge", atts.getValue("", "content"))) {
err("A \u201Cmeta\u201D element with an" + " \u201Chttp-equiv\u201D attribute whose value is" + " \u201CX-UA-Compatible\u201D" + " must have a" + " \u201Ccontent\u201D attribute with the value" + " \u201CIE=edge\u201D.");
}
if (atts.getIndex("", "charset") > -1) {
if (!"utf-8".equals(atts.getValue("", "charset").toLowerCase())) {
err("The only allowed value for the \u201Ccharset\u201D" + " attribute for the \u201Cmeta\u201D" + " element is \u201Cutf-8\u201D.");
}
if (hasMetaCharset) {
err("A document must not include more than one" + " \u201Cmeta\u201D element with a" + " \u201Ccharset\u201D attribute.");
}
if (hasContentTypePragma) {
err("A document must not include both a" + " \u201Cmeta\u201D element with an" + " \u201Chttp-equiv\u201D attribute" + " whose value is \u201Ccontent-type\u201D," + " and a \u201Cmeta\u201D element with a" + " \u201Ccharset\u201D attribute.");
}
hasMetaCharset = true;
}
if (atts.getIndex("", "name") > -1) {
if ("description".equals(atts.getValue("", "name"))) {
if (hasMetaDescription) {
err("A document must not include more than one" + " \u201Cmeta\u201D element with its" + " \u201Cname\u201D attribute set to the" + " value \u201Cdescription\u201D.");
}
hasMetaDescription = true;
}
if ("viewport".equals(atts.getValue("", "name")) && atts.getIndex("", "content") > -1) {
String contentVal = atts.getValue("", "content").toLowerCase();
if (contentVal.contains("user-scalable=no") || contentVal.contains("maximum-scale=1.0")) {
warn("Consider avoiding viewport values that" + " prevent users from resizing documents.");
}
}
if ("theme-color".equals(atts.getValue("", "name")) && atts.getIndex("", "content") > -1) {
String contentVal = atts.getValue("", "content").toLowerCase();
try {
Color.THE_INSTANCE.checkValid(contentVal);
} catch (DatatypeException e) {
try {
if (getErrorHandler() != null) {
String msg = e.getMessage();
if (e instanceof Html5DatatypeException) {
msg = msg.substring(msg.indexOf(": ") + 2);
}
//
VnuBadAttrValueException ex = new VnuBadAttrValueException(localName, uri, "content", contentVal, msg, getDocumentLocator(), Color.class, false);
getErrorHandler().error(ex);
}
} catch (ClassNotFoundException ce) {
}
}
}
}
if (atts.getIndex("", "http-equiv") > -1 && AttributeUtil.lowerCaseLiteralEqualsIgnoreAsciiCaseString("content-type", atts.getValue("", "http-equiv"))) {
if (hasMetaCharset) {
err("A document must not include both a" + " \u201Cmeta\u201D element with an" + " \u201Chttp-equiv\u201D attribute" + " whose value is \u201Ccontent-type\u201D," + " and a \u201Cmeta\u201D element with a" + " \u201Ccharset\u201D attribute.");
}
if (hasContentTypePragma) {
err("A document must not include more than one" + " \u201Cmeta\u201D element with a" + " \u201Chttp-equiv\u201D attribute" + " whose value is \u201Ccontent-type\u201D.");
}
hasContentTypePragma = true;
}
}
if ("link" == localName) {
boolean hasRel = false;
List<String> relList = new ArrayList<>();
if (atts.getIndex("", "rel") > -1) {
hasRel = true;
//
Collections.addAll(//
relList, //
atts.getValue("", "rel").toLowerCase().split("\\s+"));
}
if (atts.getIndex("", "as") > -1 && ((relList != null && !relList.contains("preload") || !hasRel))) {
err("A \u201Clink\u201D element with an" + " \u201Cas\u201D attribute must have a" + " \u201Crel\u201D attribute that contains the" + " value \u201Cpreload\u201D.");
}
if (atts.getIndex("", "integrity") > -1 && ((relList != null && !relList.contains("stylesheet") || !hasRel))) {
err("A \u201Clink\u201D element with an" + " \u201Cintegrity\u201D attribute must have a" + " \u201Crel\u201D attribute that contains the" + " value \u201Cstylesheet\u201D.");
}
if (atts.getIndex("", "sizes") > -1 && ((relList != null && !relList.contains("icon") && !relList.contains("apple-touch-icon")) && !relList.contains("apple-touch-icon-precomposed") || !hasRel)) {
err("A \u201Clink\u201D element with a" + " \u201Csizes\u201D attribute must have a" + " \u201Crel\u201D attribute that contains the" + " value \u201Cicon\u201D or the value" + " \u201Capple-touch-icon\u201D or the value" + " \u201Capple-touch-icon-precomposed\u201D.");
}
if (//
atts.getIndex("", "color") > -1 && (!hasRel || (relList != null && !relList.contains("mask-icon")))) {
err("A \u201Clink\u201D element with a" + " \u201Ccolor\u201D attribute must have a" + " \u201Crel\u201D attribute that contains" + " the value \u201Cmask-icon\u201D.");
}
if (//
atts.getIndex("", "scope") > -1 && ((relList != null && !relList.contains("serviceworker")) || !hasRel)) {
err("A \u201Clink\u201D element with a" + " \u201Cscope\u201D attribute must have a" + " \u201Crel\u201D attribute that contains the" + " value \u201Cserviceworker\u201D.");
}
if (//
atts.getIndex("", "updateviacache") > -1 && ((relList != null && !relList.contains("serviceworker")) || !hasRel)) {
err("A \u201Clink\u201D element with an" + " \u201Cupdateviacache\u201D attribute must have a" + " \u201Crel\u201D attribute that contains the" + " value \u201Cserviceworker\u201D.");
}
if (//
atts.getIndex("", "workertype") > -1 && ((relList != null && !relList.contains("serviceworker")) || !hasRel)) {
err("A \u201Clink\u201D element with a" + " \u201Cworkertype\u201D attribute must have a" + " \u201Crel\u201D attribute that contains the" + " value \u201Cserviceworker\u201D.");
}
if ((ancestorMask & BODY_MASK) != 0 && (relList != null && !(relList.contains("dns-prefetch") || relList.contains("pingback") || relList.contains("preconnect") || relList.contains("prefetch") || relList.contains("preload") || relList.contains("prerender") || relList.contains("stylesheet"))) && atts.getIndex("", "itemprop") < 0 && atts.getIndex("", "property") < 0) {
err("A \u201Clink\u201D element must not appear" + " as a descendant of a \u201Cbody\u201D element" + " unless the \u201Clink\u201D element has an" + " \u201Citemprop\u201D attribute or has a" + " \u201Crel\u201D attribute whose value contains" + " \u201Cdns-prefetch\u201D," + " \u201Cpingback\u201D," + " \u201Cpreconnect\u201D," + " \u201Cprefetch\u201D," + " \u201Cpreload\u201D," + " \u201Cprerender\u201D, or" + " \u201Cstylesheet\u201D.");
}
}
// 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.");
}
// having implicit ARIA semantics.
if (ELEMENTS_WITH_IMPLICIT_ROLE.containsKey(localName) && ELEMENTS_WITH_IMPLICIT_ROLE.get(localName).equals(role)) {
warn("The \u201C" + role + "\u201D role is unnecessary for" + " element" + " \u201C" + localName + "\u201D.");
} else if (ELEMENTS_THAT_NEVER_NEED_ROLE.containsKey(localName) && ELEMENTS_THAT_NEVER_NEED_ROLE.get(localName).equals(role)) {
warn("Element \u201C" + localName + "\u201D does not need a" + " \u201Crole\u201D attribute.");
} else if ("input" == localName) {
inputTypeVal = inputTypeVal == null ? "text" : inputTypeVal;
if (INPUT_TYPES_WITH_IMPLICIT_ROLE.containsKey(inputTypeVal) && INPUT_TYPES_WITH_IMPLICIT_ROLE.get(inputTypeVal).equals(role)) {
warnExplicitRoleUnnecessaryForType("input", role, inputTypeVal);
} else if ("email".equals(inputTypeVal) || "search".equals(inputTypeVal) || "tel".equals(inputTypeVal) || "text".equals(inputTypeVal) || "url".equals(inputTypeVal)) {
if (atts.getIndex("", "list") < 0) {
if ("textbox".equals(role)) {
warn("The \u201Ctextbox\u201D role is unnecessary" + " for an \u201Cinput\u201D element that" + " has no \u201Clist\u201D attribute and" + " whose type is" + " \u201C" + inputTypeVal + "\u201D.");
}
} else {
if ("combobox".equals(role)) {
warn("The \u201Ccombobox\u201D role is unnecessary" + " for an \u201Cinput\u201D element that" + " has a \u201Clist\u201D attribute and" + " whose type is" + " \u201C" + inputTypeVal + "\u201D.");
}
}
}
} else if (atts.getIndex("", "href") > -1 && "link".equals(role) && ("a".equals(localName) || "area".equals(localName) || "link".equals(localName))) {
warn("The \u201Clink\u201D role is unnecessary for element" + " \u201C" + localName + "\u201D with attribute" + " \u201Chref\u201D.");
} else if (("tbody".equals(localName) || "tfoot".equals(localName) || "thead".equals(localName)) && "rowgroup".equals(role)) {
warn("The \u201Crowgroup\u201D role is unnecessary for element" + " \u201C" + localName + "\u201D.");
} else if ("th" == localName && ("columnheader".equals(role) || "columnheader".equals(role))) {
warn("The \u201C" + role + "\u201D role is unnecessary for" + " element \u201Cth\u201D.");
} else if ("li" == localName && "listitem".equals(role) && !"menu".equals(parentName)) {
warn("The \u201Clistitem\u201D role is unnecessary for an" + " \u201Cli\u201D element whose parent is" + " an \u201Col\u201D element or a" + " \u201Cul\u201D element.");
} else if ("button" == localName && "button".equals(role) && "menu".equals(atts.getValue("", "type"))) {
warnExplicitRoleUnnecessaryForType("button", "button", "menu");
} else if ("menu" == localName && "toolbar".equals(role) && "toolbar".equals(atts.getValue("", "type"))) {
warnExplicitRoleUnnecessaryForType("menu", "toolbar", "toolbar");
} else if ("li" == localName && "listitem".equals(role) && !"menu".equals(parentName)) {
warn("The \u201Clistitem\u201D role is unnecessary for an" + " \u201Cli\u201D element whose parent is" + " an \u201Col\u201D element or a" + " \u201Cul\u201D element.");
}
} 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 (String token : tokens) {
ariaReferences.add(new IdrefLocator(getDocumentLocator(), token, att));
}
}
}
allIds.addAll(ids);
// aria-activedescendant accompanied by aria-owns
if (activeDescendant != null && !"".equals(activeDescendant)) {
// "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 ("style" == localName) {
child.setIsCollectingCharacters(true);
}
if ("script" == localName) {
child.setIsCollectingCharacters(true);
}
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 ("article" == localName || "aside" == localName || "nav" == localName || "section" == localName) {
if (atts.getIndex("", "aria-label") > -1 && !"".equals(atts.getValue("", "aria-label"))) {
child.setHeadingFound();
}
}
if ("select" == localName && atts.getIndex("", "required") > -1 && atts.getIndex("", "multiple") < 0) {
if (atts.getIndex("", "size") > -1) {
String size = trimSpaces(atts.getValue("", "size"));
if (!"".equals(size)) {
try {
if ((size.length() > 1 && size.charAt(0) == '+' && Integer.parseInt(size.substring(1)) == 1) || Integer.parseInt(size) == 1) {
child.setOptionNeeded();
} else {
// do nothing
}
} catch (NumberFormatException e) {
}
}
} else {
// default size is 1
child.setOptionNeeded();
}
}
if (localName.contains("-")) {
if (atts.getIndex("", "is") > -1) {
err("Autonomous custom elements must not specify the" + " \u201cis\u201d attribute.");
}
try {
CustomElementName.THE_INSTANCE.checkValid(localName);
} catch (DatatypeException e) {
try {
if (getErrorHandler() != null) {
String msg = e.getMessage();
if (e instanceof Html5DatatypeException) {
msg = msg.substring(msg.indexOf(": ") + 2);
}
VnuBadElementNameException ex = new VnuBadElementNameException(localName, uri, msg, getDocumentLocator(), CustomElementName.class, false);
getErrorHandler().error(ex);
}
} catch (ClassNotFoundException ce) {
}
}
}
} else if ("http://n.validator.nu/custom-elements/" == uri) {
/*
* For elements with names containing "-" (custom elements), the
* customelements/NamespaceChanging* code exposes them to jing as
* elements in the http://n.validator.nu/custom-elements/ namespace.
* Therefore our RelaxNG schema allows those elements. However,
* schematronequiv.Assertions still sees those elements as being in
* the HTML namespace, so here we need to emit an error for the case
* where, in source transmitted with an XML content type, somebody
* (for whatever reason) has elements in their markup which they
* have explicitly placed in that namespace (otherwise, due to
* allowing those elements in our RelaxNG schema, Jing on its own
* won't emit any error for them).
*/
err("Element \u201c" + localName + "\u201d from namespace" + " \u201chttp://n.validator.nu/custom-elements/\u201d" + " not allowed.");
} else {
StackNode child = new StackNode(ancestorMask, null, role, activeDescendant, forAttr);
if (activeDescendant != null) {
openActiveDescendants.put(child, new LocatorImpl(getDocumentLocator()));
}
push(child);
}
stack[currentPtr].setLocator(new LocatorImpl(getDocumentLocator()));
}
Aggregations