From 0d0cd86cb2822abf0a2cb488e207228f8007a10e Mon Sep 17 00:00:00 2001 From: Shunguo Yan Date: Mon, 20 Mar 2023 12:28:13 -0500 Subject: [PATCH 1/7] initial rule changes #1330 --- .../src/v4/rules/aria_attribute_conflict.ts | 1 + .../src/v4/util/CommonUtil.ts | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 accessibility-checker-engine/src/v4/util/CommonUtil.ts diff --git a/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts b/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts index 6f68cb24f..43ca43c85 100644 --- a/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts +++ b/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts @@ -46,6 +46,7 @@ export let aria_attribute_conflict: Rule = { const ruleContext = context["dom"].node as Element; // dependency check: if the ARIA attribute is completely invalid, skip this check + if (getCache(ruleContext, "aria_attribute_allowed", "") === "Fail") return null; let domAttributes = ruleContext.attributes; diff --git a/accessibility-checker-engine/src/v4/util/CommonUtil.ts b/accessibility-checker-engine/src/v4/util/CommonUtil.ts new file mode 100644 index 000000000..955377b2e --- /dev/null +++ b/accessibility-checker-engine/src/v4/util/CommonUtil.ts @@ -0,0 +1,87 @@ +/****************************************************************************** + Copyright:: 2022- IBM, Inc + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*****************************************************************************/ + +import { RPTUtil } from "../../v2/checker/accessibility/util/legacy"; +import { ARIADefinitions } from "../../v2/aria/ARIADefinitions"; + +/* + * check if any explicit role specified for the element is a valid ARIA role + * return: null if no explicit role is defined, + * true if the role(s) are defined in ARIA + * false if any role is not defined in ARIA +*/ +export function isRoleDefined(ruleContext: Element) { + let roles = RPTUtil.getRoles(ruleContext, false); + if (!roles) return null; + + let designPatterns = ARIADefinitions.designPatterns; + for (const role of roles) + if (!(role.toLowerCase() in designPatterns)) + return false; + + return true; +} + +/* + * this method assume the given explicit roles are valid, if no explicit role, it will check the implicit role + * return: null if any explicit role is invalid, + * a list of invalid attributes + * empty list if all attributes are valid, or no aria attributes are specified + */ +export function isAttributeAllowed(ruleContext: Element): string[] { + let roles = RPTUtil.getRoles(ruleContext, false); + + // the invalid role case: handled by Rpt_Aria_ValidRole. Ignore to avoid duplicated report + // for mutiple roles, skip if any role is invalid + let designPatterns = ARIADefinitions.designPatterns; + for (const role of roles) + if (!(role.toLowerCase() in designPatterns)) + return null; + + let attr = []; + let type = ""; + if (!roles || roles.length == 0) + roles = RPTUtil.getImplicitRole(ruleContext); + + + if (!roles || roles.length == 0) { + + } + + let tagName = ruleContext.tagName.toLowerCase(); + + // Failing attributes + let failAttributeTokens = []; + // Passing attributes + let passAttributeTokens = []; + + let tagProperty = RPTUtil.getElementAriaProperty(ruleContext); + // Attributes allowed on this node + let allowedAttributes = RPTUtil.getAllowedAriaAttributes(ruleContext, roles, tagProperty); + + let domAttributes = ruleContext.attributes; + if (domAttributes) { + for (let i = 0; i < domAttributes.length; i++) { + let attrName = domAttributes[i].name.trim().toLowerCase(); + let isAria = attrName.substring(0, 5) === 'aria-'; + if (isAria) { + if (!allowedAttributes.includes(attrName)) { + //valid attributes can be none also which is covered here + !failAttributeTokens.includes(attrName) ? failAttributeTokens.push(attrName) : false; + } else { + !passAttributeTokens.includes(attrName) ? passAttributeTokens.push(attrName) : false; + } + } + } + } +} From 61615f932f6be9f318d6bc4d5009b5d3356eb2b6 Mon Sep 17 00:00:00 2001 From: Shunguo Yan Date: Mon, 20 Mar 2023 14:16:56 -0500 Subject: [PATCH 2/7] add common utils #1330 --- .../src/v4/rules/aria_semantics.ts | 75 ++++++------------- .../src/v4/util/CommonUtil.ts | 48 ++++-------- 2 files changed, 37 insertions(+), 86 deletions(-) diff --git a/accessibility-checker-engine/src/v4/rules/aria_semantics.ts b/accessibility-checker-engine/src/v4/rules/aria_semantics.ts index 23dcbef1b..edc57f5b0 100644 --- a/accessibility-checker-engine/src/v4/rules/aria_semantics.ts +++ b/accessibility-checker-engine/src/v4/rules/aria_semantics.ts @@ -16,6 +16,7 @@ import { eRulePolicy, eToolkitLevel } from "../api/IRule"; import { RPTUtil } from "../../v2/checker/accessibility/util/legacy"; import { ARIADefinitions } from "../../v2/aria/ARIADefinitions"; import { getCache, setCache } from "../util/CacheUtil"; +import { getInvalidAttributes, isRoleDefined } from "../util/CommonUtil"; export let aria_semantics_role: Rule = { id: "aria_semantics_role", @@ -64,10 +65,10 @@ export let aria_semantics_role: Rule = { } // the invalid role case: handled by Rpt_Aria_ValidRole. Ignore to avoid duplicated report - let designPatterns = ARIADefinitions.designPatterns; - for (const role of domRoles) - if (!(role.toLowerCase() in designPatterns)) - return null; + let role_defined = isRoleDefined(domRoles); + if (domRoles && domRoles.length > 0 && !role_defined) + return null; + // Roles allowed on this node let allowedRoles = []; @@ -154,59 +155,27 @@ export let aria_attribute_allowed: Rule = { if (ruleContext.nodeType !== Node.ELEMENT_NODE) return null; - // get roles from RPTUtil because multiple explicit roles are allowed - let roles = RPTUtil.getRoles(ruleContext, false); - - // the invalid role (not defined in the spec) case: handled by Rpt_Aria_ValidRole. Ignore to avoid duplicated report - // for mutiple roles, skip if any role is invalid - let designPatterns = ARIADefinitions.designPatterns; - for (const role of roles) - if (!(role.toLowerCase() in designPatterns)) - return null; - - let type = ""; - if (roles && roles.length > 0) - type = "role_attr"; - else { - roles = RPTUtil.getImplicitRole(ruleContext); - if (roles && roles.length > 0) - type = "implicit_role_attr"; + let roles: string[] = RPTUtil.getUserDefinedRoles(ruleContext); - } + // the invalid role case: handled by Rpt_Aria_ValidRole. Ignore to avoid duplicated report + let role_defined = isRoleDefined(roles); + if (roles && roles.length > 0 && !role_defined) + return null; + let tagName = ruleContext.tagName.toLowerCase(); + let failedAttributes = getInvalidAttributes(ruleContext); + if (failedAttributes.length === 0) + return RulePass("Pass"); - // Failing attributes - let failAttributeTokens = []; - // Passing attributes - let passAttributeTokens = []; - - let tagProperty = RPTUtil.getElementAriaProperty(ruleContext); - // Attributes allowed on this node - let allowedAttributes = RPTUtil.getAllowedAriaAttributes(ruleContext, roles, tagProperty); - let domAriaAttributes = RPTUtil.getUserDefinedAriaAttributes(ruleContext); - for (let i = 0; i < domAriaAttributes.length; i++) { - if (!allowedAttributes.includes(domAriaAttributes[i])) { - //valid attributes can be none also which is covered here - !failAttributeTokens.includes(domAriaAttributes[i]) ? failAttributeTokens.push(domAriaAttributes[i]) : false; - } else { - !passAttributeTokens.includes(domAriaAttributes[i]) ? passAttributeTokens.push(domAriaAttributes[i]) : false; - } + if (role_defined === null) { + //no explicit role defined + roles = RPTUtil.getImplicitRole(ruleContext); + if (!roles && roles.length === 0) + return RuleFail("Fail_invalid_role_attr", [failedAttributes.join(", "), tagName, "none"]); + else + return RuleFail("Fail_invalid_implicit_role_attr", [failedAttributes.join(", "), tagName, roles.join(", ")]); } - if (failAttributeTokens.length > 0) { - - setCache(ruleContext, "aria_attribute_allowed", "Fail"); - if (type === "implicit_role_attr") - return RuleFail("Fail_invalid_implicit_role_attr", [failAttributeTokens.join(", "), tagName, roles.join(", ")]); - else { - if (!roles || roles.length === 0) roles = ["none"]; - return RuleFail("Fail_invalid_role_attr", [failAttributeTokens.join(", "), tagName, roles.join(", ")]); - } - - } else if (passAttributeTokens.length > 0) { - return RulePass("Pass", [passAttributeTokens.join(", "), tagName, roles.join(", ")]); - } else { - return null; - } + return RuleFail("Fail_invalid_role_attr", [failedAttributes.join(", "), tagName, roles.join(", ")]); } } \ No newline at end of file diff --git a/accessibility-checker-engine/src/v4/util/CommonUtil.ts b/accessibility-checker-engine/src/v4/util/CommonUtil.ts index 955377b2e..cb84fffc4 100644 --- a/accessibility-checker-engine/src/v4/util/CommonUtil.ts +++ b/accessibility-checker-engine/src/v4/util/CommonUtil.ts @@ -20,8 +20,7 @@ import { ARIADefinitions } from "../../v2/aria/ARIADefinitions"; * true if the role(s) are defined in ARIA * false if any role is not defined in ARIA */ -export function isRoleDefined(ruleContext: Element) { - let roles = RPTUtil.getRoles(ruleContext, false); +export function isRoleDefined(roles: string[]) { if (!roles) return null; let designPatterns = ARIADefinitions.designPatterns; @@ -33,55 +32,38 @@ export function isRoleDefined(ruleContext: Element) { } /* - * this method assume the given explicit roles are valid, if no explicit role, it will check the implicit role + * this method first checks explicit roles, if no explicit role, it will check the implicit role * return: null if any explicit role is invalid, * a list of invalid attributes * empty list if all attributes are valid, or no aria attributes are specified */ -export function isAttributeAllowed(ruleContext: Element): string[] { - let roles = RPTUtil.getRoles(ruleContext, false); +export function getInvalidAttributes(ruleContext: Element): string[] { + let roles = RPTUtil.getUserDefinedRoles(ruleContext); // the invalid role case: handled by Rpt_Aria_ValidRole. Ignore to avoid duplicated report // for mutiple roles, skip if any role is invalid - let designPatterns = ARIADefinitions.designPatterns; - for (const role of roles) - if (!(role.toLowerCase() in designPatterns)) - return null; + if (isRoleDefined(roles)) + return null; - let attr = []; - let type = ""; + let attrs = []; if (!roles || roles.length == 0) roles = RPTUtil.getImplicitRole(ruleContext); - + let aria_attrs = RPTUtil.getUserDefinedAriaAttributes(ruleContext); if (!roles || roles.length == 0) { - + return aria_attrs; } - let tagName = ruleContext.tagName.toLowerCase(); - - // Failing attributes - let failAttributeTokens = []; - // Passing attributes - let passAttributeTokens = []; - let tagProperty = RPTUtil.getElementAriaProperty(ruleContext); // Attributes allowed on this node let allowedAttributes = RPTUtil.getAllowedAriaAttributes(ruleContext, roles, tagProperty); - let domAttributes = ruleContext.attributes; - if (domAttributes) { - for (let i = 0; i < domAttributes.length; i++) { - let attrName = domAttributes[i].name.trim().toLowerCase(); - let isAria = attrName.substring(0, 5) === 'aria-'; - if (isAria) { - if (!allowedAttributes.includes(attrName)) { - //valid attributes can be none also which is covered here - !failAttributeTokens.includes(attrName) ? failAttributeTokens.push(attrName) : false; - } else { - !passAttributeTokens.includes(attrName) ? passAttributeTokens.push(attrName) : false; - } - } + if (aria_attrs) { + for (let i = 0; i < aria_attrs.length; i++) { + let attrName = aria_attrs[i].name.trim().toLowerCase(); + if (!allowedAttributes.includes(attrName) && !attrs.includes(attrName)) + attrs.push(attrName); } } + return attrs; } From e733bc56cd01c2b0eb658f21be608c1172417203 Mon Sep 17 00:00:00 2001 From: Shunguo Yan Date: Tue, 21 Mar 2023 11:38:31 -0500 Subject: [PATCH 3/7] update dependencies #1330 --- .../src/v4/rules/aria_attribute_conflict.ts | 8 ++-- .../src/v4/rules/aria_semantics.ts | 43 +++++++++++-------- .../src/v4/util/CommonUtil.ts | 22 +++++----- .../Fail.html | 5 --- 4 files changed, 40 insertions(+), 38 deletions(-) diff --git a/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts b/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts index 43ca43c85..24692b456 100644 --- a/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts +++ b/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts @@ -15,6 +15,7 @@ import { Rule, RuleResult, RuleFail, RuleContext, RulePass, RuleContextHierarchy import { eRulePolicy, eToolkitLevel } from "../api/IRule"; import { RPTUtil } from "../../v2/checker/accessibility/util/legacy"; import { getCache, setCache } from "../util/CacheUtil"; +import { getInvalidAriaAttributes } from "../util/CommonUtil"; export let aria_attribute_conflict: Rule = { id: "aria_attribute_conflict", @@ -44,11 +45,12 @@ export let aria_attribute_conflict: Rule = { act: [], run: (context: RuleContext, options?: {}, contextHierarchies?: RuleContextHierarchy): RuleResult | RuleResult[] => { const ruleContext = context["dom"].node as Element; + // dependency check: if the ARIA attribute is completely invalid, skip this check - + let invalidAttributes = getInvalidAriaAttributes(ruleContext); + if (invalidAttributes && invalidAttributes.length > 0) + return null; - if (getCache(ruleContext, "aria_attribute_allowed", "") === "Fail") return null; - let domAttributes = ruleContext.attributes; let ariaAttrs = []; let htmlAttrs = []; diff --git a/accessibility-checker-engine/src/v4/rules/aria_semantics.ts b/accessibility-checker-engine/src/v4/rules/aria_semantics.ts index edc57f5b0..0a02abcd9 100644 --- a/accessibility-checker-engine/src/v4/rules/aria_semantics.ts +++ b/accessibility-checker-engine/src/v4/rules/aria_semantics.ts @@ -11,12 +11,11 @@ limitations under the License. *****************************************************************************/ -import { Rule, RuleResult, RuleFail, RuleContext, RulePotential, RuleManual, RulePass, RuleContextHierarchy } from "../api/IRule"; +import { Rule, RuleResult, RuleFail, RuleContext, RulePass, RuleContextHierarchy } from "../api/IRule"; import { eRulePolicy, eToolkitLevel } from "../api/IRule"; import { RPTUtil } from "../../v2/checker/accessibility/util/legacy"; -import { ARIADefinitions } from "../../v2/aria/ARIADefinitions"; import { getCache, setCache } from "../util/CacheUtil"; -import { getInvalidAttributes, isRoleDefined } from "../util/CommonUtil"; +import { getInvalidAriaAttributes, areRolesDefined } from "../util/CommonUtil"; export let aria_semantics_role: Rule = { id: "aria_semantics_role", @@ -65,7 +64,7 @@ export let aria_semantics_role: Rule = { } // the invalid role case: handled by Rpt_Aria_ValidRole. Ignore to avoid duplicated report - let role_defined = isRoleDefined(domRoles); + let role_defined = areRolesDefined(domRoles); if (domRoles && domRoles.length > 0 && !role_defined) return null; @@ -155,27 +154,35 @@ export let aria_attribute_allowed: Rule = { if (ruleContext.nodeType !== Node.ELEMENT_NODE) return null; - let roles: string[] = RPTUtil.getUserDefinedRoles(ruleContext); - - // the invalid role case: handled by Rpt_Aria_ValidRole. Ignore to avoid duplicated report - let role_defined = isRoleDefined(roles); - if (roles && roles.length > 0 && !role_defined) + // ignore if no aria attribute + let ariaAttributes:string[] = RPTUtil.getUserDefinedAriaAttributes(ruleContext); + if (ariaAttributes === null || ariaAttributes.length === 0) return null; + + let roles: string[] = RPTUtil.getUserDefinedRoles(ruleContext); + let explicit = true; + if (roles && roles.length > 0) { + // the invalid role case: handled by Rpt_Aria_ValidRole. Ignore to avoid duplicated report + if (!areRolesDefined(roles)) + return null; + } else { + //no explicit role defined + roles = RPTUtil.getImplicitRole(ruleContext); + explicit = false; + } let tagName = ruleContext.tagName.toLowerCase(); - let failedAttributes = getInvalidAttributes(ruleContext); - if (failedAttributes.length === 0) - return RulePass("Pass"); + let failedAttributes = getInvalidAriaAttributes(ruleContext); + if (!failedAttributes || failedAttributes.length === 0) + return RulePass("Pass", [ariaAttributes.join(", "), tagName, roles.join(", ")]); - if (role_defined === null) { - //no explicit role defined - roles = RPTUtil.getImplicitRole(ruleContext); - if (!roles && roles.length === 0) - return RuleFail("Fail_invalid_role_attr", [failedAttributes.join(", "), tagName, "none"]); + if (roles.length > 0) { + if (explicit) + return RuleFail("Fail_invalid_role_attr", [failedAttributes.join(", "), tagName, roles.join(", ")]); else return RuleFail("Fail_invalid_implicit_role_attr", [failedAttributes.join(", "), tagName, roles.join(", ")]); } - return RuleFail("Fail_invalid_role_attr", [failedAttributes.join(", "), tagName, roles.join(", ")]); + return RuleFail("Fail_invalid_role_attr", [failedAttributes.join(", "), tagName, "none"]); } } \ No newline at end of file diff --git a/accessibility-checker-engine/src/v4/util/CommonUtil.ts b/accessibility-checker-engine/src/v4/util/CommonUtil.ts index cb84fffc4..177a994f7 100644 --- a/accessibility-checker-engine/src/v4/util/CommonUtil.ts +++ b/accessibility-checker-engine/src/v4/util/CommonUtil.ts @@ -20,8 +20,8 @@ import { ARIADefinitions } from "../../v2/aria/ARIADefinitions"; * true if the role(s) are defined in ARIA * false if any role is not defined in ARIA */ -export function isRoleDefined(roles: string[]) { - if (!roles) return null; +export function areRolesDefined(roles: string[]) { + if (!roles || roles.length ===0) return null; let designPatterns = ARIADefinitions.designPatterns; for (const role of roles) @@ -37,30 +37,28 @@ export function isRoleDefined(roles: string[]) { * a list of invalid attributes * empty list if all attributes are valid, or no aria attributes are specified */ -export function getInvalidAttributes(ruleContext: Element): string[] { +export function getInvalidAriaAttributes(ruleContext: Element): string[] { let roles = RPTUtil.getUserDefinedRoles(ruleContext); - + // the invalid role case: handled by Rpt_Aria_ValidRole. Ignore to avoid duplicated report // for mutiple roles, skip if any role is invalid - if (isRoleDefined(roles)) + let defined = areRolesDefined(roles); + if (defined !==null && !defined) return null; - + let attrs = []; if (!roles || roles.length == 0) roles = RPTUtil.getImplicitRole(ruleContext); - let aria_attrs = RPTUtil.getUserDefinedAriaAttributes(ruleContext); - if (!roles || roles.length == 0) { - return aria_attrs; - } - + let aria_attrs: string[] = RPTUtil.getUserDefinedAriaAttributes(ruleContext); + let tagProperty = RPTUtil.getElementAriaProperty(ruleContext); // Attributes allowed on this node let allowedAttributes = RPTUtil.getAllowedAriaAttributes(ruleContext, roles, tagProperty); if (aria_attrs) { for (let i = 0; i < aria_attrs.length; i++) { - let attrName = aria_attrs[i].name.trim().toLowerCase(); + let attrName = aria_attrs[i].trim().toLowerCase(); if (!allowedAttributes.includes(attrName) && !attrs.includes(attrName)) attrs.push(attrName); } diff --git a/accessibility-checker-engine/test/v2/checker/accessibility/rules/aria_attribute_conflict_ruleunit/Fail.html b/accessibility-checker-engine/test/v2/checker/accessibility/rules/aria_attribute_conflict_ruleunit/Fail.html index adfdb7057..202a5614f 100755 --- a/accessibility-checker-engine/test/v2/checker/accessibility/rules/aria_attribute_conflict_ruleunit/Fail.html +++ b/accessibility-checker-engine/test/v2/checker/accessibility/rules/aria_attribute_conflict_ruleunit/Fail.html @@ -69,12 +69,7 @@ passedXpaths: [ ], failedXpaths: [ - "/html[1]/body[1]/div[1]", - "/html[1]/body[1]/div[2]", - "/html[1]/body[1]/div[3]", - "/html[1]/body[1]/div[4]", "/html[1]/body[1]/form[1]/input[1]", - "/html[1]/body[1]/form[2]", "/html[1]/body[1]/form[3]/input[1]", "/html[1]/body[1]/form[4]/input[2]" From 612ecf9b711c008198055f2072a5cb7288edf0fd Mon Sep 17 00:00:00 2001 From: Shunguo Yan Date: Tue, 21 Mar 2023 13:16:17 -0500 Subject: [PATCH 4/7] update aria and html attributes #1330 --- .../v2/checker/accessibility/util/legacy.ts | 63 ++++++++++++++++++- .../src/v4/rules/aria_attribute_conflict.ts | 26 +++++++- .../src/v4/rules/aria_attribute_redundant.ts | 14 ++++- .../src/v4/util/CommonUtil.ts | 25 ++++++++ .../Fail.html | 38 ----------- 5 files changed, 121 insertions(+), 45 deletions(-) diff --git a/accessibility-checker-engine/src/v2/checker/accessibility/util/legacy.ts b/accessibility-checker-engine/src/v2/checker/accessibility/util/legacy.ts index cfca19aeb..63958f4a8 100644 --- a/accessibility-checker-engine/src/v2/checker/accessibility/util/legacy.ts +++ b/accessibility-checker-engine/src/v2/checker/accessibility/util/legacy.ts @@ -223,7 +223,7 @@ export class RPTUtil { } /** - * this method returns user-defined aria attribute from dom + * this method returns user-defined aria attribute name from dom * @param ele element * @returns user defined aria attributes */ @@ -241,6 +241,67 @@ export class RPTUtil { return ariaAttributes; } + /** + * this method returns user-defined html attribute name from dom + * @param ele element + * @returns user defined html attributes + */ + public static getUserDefinedHtmlAttributes(elem) { + let htmlAttributes = []; + let domAttributes = elem.attributes; + if (domAttributes) { + for (let i = 0; i < domAttributes.length; i++) { + let attrName = domAttributes[i].name.trim().toLowerCase(); + let isAria = attrName.substring(0, 5) === 'aria-'; + if (!isAria) + htmlAttributes.push(attrName); + } + } + return htmlAttributes; + } + + /** + * this method returns user-defined aria attribute name-value pair from dom + * @param ele element + * @returns user defined aria attributes + */ + public static getUserDefinedAriaAttributeNameValuePairs(elem) { + let ariaAttributes = []; + let domAttributes = elem.attributes; + if (domAttributes) { + for (let i = 0; i < domAttributes.length; i++) { + let attrName = domAttributes[i].name.trim().toLowerCase(); + let attrValue = elem.getAttribute(attrName); + if (attrValue === '') attrValue = null; + let isAria = attrName.substring(0, 5) === 'aria-'; + if (isAria) + ariaAttributes.push({name: attrName, value: attrValue}); + } + } + return ariaAttributes; + } + + /** + * this method returns user-defined html attribute name-value pair from dom + * @param ele element + * @returns user defined html attributes + */ + public static getUserDefinedHtmlAttributeNameValuePairs(elem) { + let htmlAttributes = []; + let domAttributes = elem.attributes; + if (domAttributes) { + for (let i = 0; i < domAttributes.length; i++) { + let attrName = domAttributes[i].name.trim().toLowerCase(); + let attrValue = elem.getAttribute(attrName); + if (attrValue === '') attrValue = null; + let isAria = attrName.substring(0, 5) === 'aria-'; + if (!isAria) + htmlAttributes.push({name: attrName, value: attrValue}); + } + } + return htmlAttributes; + } + /** * This method handles implicit aria definitions, for example, an input with checked is equivalent to aria-checked="true" */ diff --git a/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts b/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts index 24692b456..79f116dcc 100644 --- a/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts +++ b/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts @@ -14,8 +14,7 @@ import { Rule, RuleResult, RuleFail, RuleContext, RulePass, RuleContextHierarchy } from "../api/IRule"; import { eRulePolicy, eToolkitLevel } from "../api/IRule"; import { RPTUtil } from "../../v2/checker/accessibility/util/legacy"; -import { getCache, setCache } from "../util/CacheUtil"; -import { getInvalidAriaAttributes } from "../util/CommonUtil"; +import { getInvalidAriaAttributes, getConflictAriaAndHtmlAttributes } from "../util/CommonUtil"; export let aria_attribute_conflict: Rule = { id: "aria_attribute_conflict", @@ -51,6 +50,26 @@ export let aria_attribute_conflict: Rule = { if (invalidAttributes && invalidAttributes.length > 0) return null; + let ret = []; + let ariaAttributes = RPTUtil.getUserDefinedAriaAttributes(ruleContext); + if (!ariaAttributes || ariaAttributes.length ===0) + return null; + + let conflictAttributes = getConflictAriaAndHtmlAttributes(ruleContext); + for (let i = 0; i < conflictAttributes.length; i++) { + ret.push(RuleFail("fail_conflict", [conflictAttributes[i]['ariaAttr'], conflictAttributes[i]['htmlAttr']])); + if (ariaAttributes.includes(conflictAttributes[i]['ariaAttr'])) + RPTUtil.reduceArrayItemList([conflictAttributes[i]['ariaAttr']], ariaAttributes); + } + + for (let i = 0; i < ariaAttributes.length; i++) + ret.push(RulePass("pass")); + + if (ret.length > 0) + return ret; + + return null; + /** let domAttributes = ruleContext.attributes; let ariaAttrs = []; let htmlAttrs = []; @@ -80,6 +99,7 @@ export let aria_attribute_conflict: Rule = { } if (ret.length > 0) return ret; - return null; + return null; + */ } } \ No newline at end of file diff --git a/accessibility-checker-engine/src/v4/rules/aria_attribute_redundant.ts b/accessibility-checker-engine/src/v4/rules/aria_attribute_redundant.ts index 49bcfd49e..582195f01 100644 --- a/accessibility-checker-engine/src/v4/rules/aria_attribute_redundant.ts +++ b/accessibility-checker-engine/src/v4/rules/aria_attribute_redundant.ts @@ -15,6 +15,7 @@ import { Rule, RuleResult, RuleFail, RuleContext, RulePass, RuleContextHierarchy import { eRulePolicy, eToolkitLevel } from "../api/IRule"; import { RPTUtil } from "../../v2/checker/accessibility/util/legacy"; import { getCache } from "../util/CacheUtil"; +import { getInvalidAriaAttributes, getConflictAriaAndHtmlAttributes } from "../util/CommonUtil"; export let aria_attribute_redundant: Rule = { id: "aria_attribute_redundant", @@ -46,11 +47,18 @@ export let aria_attribute_redundant: Rule = { const ruleContext = context["dom"].node as Element; // dependency check: if the ARIA attribute is completely invalid, skip this check - if (getCache(ruleContext, "aria_attribute_allowed", "") === "Fail") return null; + //if (getCache(ruleContext, "aria_attribute_allowed", "") === "Fail") return null; + // dependency check: if the ARIA attribute is completely invalid, skip this check + let invalidAttributes = getInvalidAriaAttributes(ruleContext); + if (invalidAttributes && invalidAttributes.length > 0) + return null; // if conflict already reported, ignore reporting overlap - if (getCache(ruleContext, "aria_attribute_conflict", "") === "fail_conflict") return null; - + //if (getCache(ruleContext, "aria_attribute_conflict", "") === "fail_conflict") return null; + let conflictAttributes = getConflictAriaAndHtmlAttributes(ruleContext); + if (conflictAttributes && conflictAttributes.length > 0) + return null; + let domAttributes = ruleContext.attributes; let ariaAttrs = []; let htmlAttrs = []; diff --git a/accessibility-checker-engine/src/v4/util/CommonUtil.ts b/accessibility-checker-engine/src/v4/util/CommonUtil.ts index 177a994f7..8397dab5e 100644 --- a/accessibility-checker-engine/src/v4/util/CommonUtil.ts +++ b/accessibility-checker-engine/src/v4/util/CommonUtil.ts @@ -65,3 +65,28 @@ export function getInvalidAriaAttributes(ruleContext: Element): string[] { } return attrs; } + +/* + * check if any explicit role specified for the element is a valid ARIA role + * return: null if no explicit role is defined, + * true if the role(s) are defined in ARIA + * false if any role is not defined in ARIA +*/ +export function getConflictAriaAndHtmlAttributes(elem: Element) { + + let ariaAttrs = RPTUtil.getUserDefinedAriaAttributeNameValuePairs(elem); + let htmlAttrs = RPTUtil.getUserDefinedHtmlAttributeNameValuePairs(elem); + + let ret = []; + if (ariaAttrs && ariaAttrs.length > 0 && htmlAttrs && htmlAttrs.length > 0) { + for (let i = 0; i < ariaAttrs.length; i++) { + const examinedHtmlAtrNames = RPTUtil.getConflictOrOverlappingHtmlAttribute(ariaAttrs[i], htmlAttrs, 'conflict'); + if (examinedHtmlAtrNames === null) continue; + examinedHtmlAtrNames.forEach(item => { + if (item['result'] === 'Failed') //failed + ret.push({'ariaAttr': ariaAttrs[i]['name'], 'htmlAttr': item['attr']}); + }); + } + } + return ret; +} diff --git a/accessibility-checker-engine/test/v2/checker/accessibility/rules/aria_attribute_redundant_ruleunit/Fail.html b/accessibility-checker-engine/test/v2/checker/accessibility/rules/aria_attribute_redundant_ruleunit/Fail.html index 53ce47ddb..e2726667a 100755 --- a/accessibility-checker-engine/test/v2/checker/accessibility/rules/aria_attribute_redundant_ruleunit/Fail.html +++ b/accessibility-checker-engine/test/v2/checker/accessibility/rules/aria_attribute_redundant_ruleunit/Fail.html @@ -120,44 +120,6 @@ ], "apiArgs": [], "category": "Accessibility" - }, - { - "ruleId": "aria_attribute_redundant", - "value": [ - "INFORMATION", - "FAIL" - ], - "path": { - "dom": "/html[1]/body[1]/div[3]", - "aria": "/document[1]" - }, - "reasonId": "fail_redundant", - "message": "The ARIA attribute \"aria-colspan\" is redundant with the HTML attribute \"colspan\"", - "messageArgs": [ - "aria-colspan", - "colspan" - ], - "apiArgs": [], - "category": "Accessibility" - }, - { - "ruleId": "aria_attribute_redundant", - "value": [ - "INFORMATION", - "FAIL" - ], - "path": { - "dom": "/html[1]/body[1]/div[4]", - "aria": "/document[1]" - }, - "reasonId": "fail_redundant", - "message": "The ARIA attribute \"aria-rowspan\" is redundant with the HTML attribute \"rowspan\"", - "messageArgs": [ - "aria-rowspan", - "rowspan" - ], - "apiArgs": [], - "category": "Accessibility" } ] } From b7b9dbca31a6f7b6df8aaa7f1cfd4a232a633230 Mon Sep 17 00:00:00 2001 From: Shunguo Yan Date: Tue, 21 Mar 2023 15:44:17 -0500 Subject: [PATCH 5/7] update invalid aria role dependencies #1330 --- .../src/v4/rules/aria_role_redundant.ts | 11 +++- .../src/v4/rules/aria_semantics.ts | 59 +++++++++++-------- .../src/v4/rules/table_aria_descendants.ts | 11 ++-- .../src/v4/util/CommonUtil.ts | 55 +++++++++++++++-- 4 files changed, 99 insertions(+), 37 deletions(-) diff --git a/accessibility-checker-engine/src/v4/rules/aria_role_redundant.ts b/accessibility-checker-engine/src/v4/rules/aria_role_redundant.ts index ec4827a3c..3ccd7d3b1 100644 --- a/accessibility-checker-engine/src/v4/rules/aria_role_redundant.ts +++ b/accessibility-checker-engine/src/v4/rules/aria_role_redundant.ts @@ -15,6 +15,7 @@ import { Rule, RuleResult, RuleFail, RuleContext, RulePass, RuleContextHierarchy import { eRulePolicy, eToolkitLevel } from "../api/IRule"; import { RPTUtil } from "../../v2/checker/accessibility/util/legacy"; import { getCache } from "../util/CacheUtil"; +import { isTableDescendant } from "../util/CommonUtil"; export let aria_role_redundant: Rule = { id: "aria_role_redundant", @@ -46,10 +47,14 @@ export let aria_role_redundant: Rule = { // dependency check: if the ARIA attribute is completely invalid, skip this check if (getCache(ruleContext, "aria_semantics_role", "") === "Fail_1") return null; + // dependency check: if it's already failed in the parent relation, then skip this check - if (["td", "th", "tr"].includes(elemName) && getCache(ruleContext, "table_aria_descendants", "") === "explicit_role") - return null; - + if (["td", "th", "tr"].includes(elemName)) { + let parentRole = isTableDescendant(contextHierarchies); + if (parentRole !== null && parentRole.length > 0) + return null; + } + let ariaRoles = RPTUtil.getRoles(ruleContext, false); if (!ariaRoles || ariaRoles.length === 0) return; diff --git a/accessibility-checker-engine/src/v4/rules/aria_semantics.ts b/accessibility-checker-engine/src/v4/rules/aria_semantics.ts index 0a02abcd9..e91c2bd39 100644 --- a/accessibility-checker-engine/src/v4/rules/aria_semantics.ts +++ b/accessibility-checker-engine/src/v4/rules/aria_semantics.ts @@ -15,7 +15,7 @@ import { Rule, RuleResult, RuleFail, RuleContext, RulePass, RuleContextHierarchy import { eRulePolicy, eToolkitLevel } from "../api/IRule"; import { RPTUtil } from "../../v2/checker/accessibility/util/legacy"; import { getCache, setCache } from "../util/CacheUtil"; -import { getInvalidAriaAttributes, areRolesDefined } from "../util/CommonUtil"; +import { getInvalidAriaAttributes, areRolesDefined, isTableDescendant } from "../util/CommonUtil"; export let aria_semantics_role: Rule = { id: "aria_semantics_role", @@ -52,9 +52,12 @@ export let aria_semantics_role: Rule = { return null; // dependency check: if it's already failed, then skip - if (["td", "th", "tr"].includes(tagName) && getCache(ruleContext, "table_aria_descendants", "") === "explicit_role") - return null; - + if (["td", "th", "tr"].includes(tagName)) { + let parentRole = isTableDescendant(contextHierarchies); + if (parentRole !== null && parentRole.length > 0) + return null; + } + let domRoles: string[] = RPTUtil.getUserDefinedRoles(ruleContext); // check the 'generic' role first @@ -78,29 +81,33 @@ export let aria_semantics_role: Rule = { let tagProperty = RPTUtil.getElementAriaProperty(ruleContext); allowedRoles = RPTUtil.getAllowedAriaRoles(ruleContext, tagProperty); - // Testing restrictions for each role and adding the corresponding attributes to the allowed attribute list - for (let i = 0; i < domRoles.length; i++) { - if (allowedRoles.length === 0) { - if (!failRoleTokens.includes(domRoles[i])) { - failRoleTokens.push(domRoles[i]); - } - } else if (!allowedRoles.includes("any")) { // any role is valid so no checking here. the validity of the aria role is checked by Rpt_Aria_ValidRole - if (!allowedRoles.includes(domRoles[i])) { - if (!failRoleTokens.includes(domRoles[i])) { - failRoleTokens.push(domRoles[i]); - } - } else if (!passRoleTokens.includes(domRoles[i])) { - passRoleTokens.push(domRoles[i]) - } - } else if (allowedRoles.includes("any")) { - if (domRoles[i] === 'generic' && failRoleTokens.indexOf(domRoles[i]) === -1) { - failRoleTokens.push(domRoles[i]); - } else { - if (passRoleTokens.indexOf(domRoles[i]) === -1) - passRoleTokens.push(domRoles[i]); - } + + if (!allowedRoles || allowedRoles.length === 0) { + RPTUtil.concatUniqueArrayItemList(domRoles, failRoleTokens); + } else { + if (allowedRoles && allowedRoles.includes('any')) { + RPTUtil.concatUniqueArrayItemList(domRoles, passRoleTokens); + } else { + // Testing restrictions for each role and adding the corresponding attributes to the allowed attribute list + for (let i = 0; i < domRoles.length; i++) { + if (!allowedRoles.includes(domRoles[i])) { + if (!failRoleTokens.includes(domRoles[i])) { + failRoleTokens.push(domRoles[i]); + } + } else if (!passRoleTokens.includes(domRoles[i])) { + passRoleTokens.push(domRoles[i]) + } + /**else if (allowedRoles.includes("any")) { + //if (domRoles[i] === 'generic' && failRoleTokens.indexOf(domRoles[i]) === -1) { + // failRoleTokens.push(domRoles[i]); + //} else { + if (passRoleTokens.indexOf(domRoles[i]) === -1) + passRoleTokens.push(domRoles[i]); + //} + }*/ + } // for loop } - } // for loop + } if (failRoleTokens.includes("presentation") || failRoleTokens.includes("none") && RPTUtil.isTabbable(ruleContext)) { return RuleFail("Fail_2", [failRoleTokens.join(", "), tagName]); } else if (failRoleTokens.length > 0) { diff --git a/accessibility-checker-engine/src/v4/rules/table_aria_descendants.ts b/accessibility-checker-engine/src/v4/rules/table_aria_descendants.ts index 87f36df50..912a70801 100644 --- a/accessibility-checker-engine/src/v4/rules/table_aria_descendants.ts +++ b/accessibility-checker-engine/src/v4/rules/table_aria_descendants.ts @@ -11,9 +11,10 @@ limitations under the License. *****************************************************************************/ -import { Rule, RuleResult, RuleFail, RuleContext, RulePotential, RuleManual, RulePass, RuleContextHierarchy } from "../api/IRule"; +import { Rule, RuleResult, RuleFail, RuleContext, RuleContextHierarchy } from "../api/IRule"; import { eRulePolicy, eToolkitLevel } from "../api/IRule"; import { setCache } from "../util/CacheUtil"; +import { isTableDescendant } from "../util/CommonUtil"; export let table_aria_descendants: Rule = { id: "table_aria_descendants", @@ -38,10 +39,12 @@ export let table_aria_descendants: Rule = { }], act: [], run: (context: RuleContext, options?: {}, contextHierarchies?: RuleContextHierarchy): RuleResult | RuleResult[] => { - const ruleContext = context["dom"].node as Element; - let parentRole = contextHierarchies["aria"].filter(hier => ["table", "grid", "treegrid"].includes(hier.role)); + const ruleContext = context["dom"].node as Element; + let parentRole = isTableDescendant(contextHierarchies); // cache the result - setCache(ruleContext, "table_aria_descendants", "explicit_role"); + if (parentRole === null || parentRole.length === 0) + return; + return RuleFail("explicit_role", [context["dom"].node.nodeName.toLowerCase(), parentRole[0].role]); } } \ No newline at end of file diff --git a/accessibility-checker-engine/src/v4/util/CommonUtil.ts b/accessibility-checker-engine/src/v4/util/CommonUtil.ts index 8397dab5e..0cdbf8d8b 100644 --- a/accessibility-checker-engine/src/v4/util/CommonUtil.ts +++ b/accessibility-checker-engine/src/v4/util/CommonUtil.ts @@ -11,6 +11,7 @@ limitations under the License. *****************************************************************************/ +import {RuleContextHierarchy } from "../api/IRule"; import { RPTUtil } from "../../v2/checker/accessibility/util/legacy"; import { ARIADefinitions } from "../../v2/aria/ARIADefinitions"; @@ -31,6 +32,44 @@ export function areRolesDefined(roles: string[]) { return true; } +/* + * check if any explicit role specified for the element is a valid ARIA role + * return: null if no explicit role is defined, + * true if the role(s) are defined in ARIA + * false if any role is not defined in ARIA +*/ +export function getInvalidRoles(ruleContext: Element) { + let domRoles: string[] = RPTUtil.getUserDefinedRoles(ruleContext); + + if (!domRoles || domRoles.length === 0) + return null; + + // check the 'generic' role first + if (domRoles && domRoles.includes('generic')) + return ["generic"]; + + // Failing roles + let failRoleTokens = []; + // Passing roles + let passRoleTokens = []; + + let tagProperty = RPTUtil.getElementAriaProperty(ruleContext); + let allowedRoles = RPTUtil.getAllowedAriaRoles(ruleContext, tagProperty); + if (!allowedRoles && allowedRoles.length === 0) + return domRoles; + + let invalidRoles = []; + + if (allowedRoles && allowedRoles.includes('any')) + return []; + + for (let i = 0; i < domRoles.length; i++) + if (!allowedRoles.includes(domRoles[i]) && !invalidRoles.includes(domRoles[i])) + invalidRoles.push(domRoles[i]); + + return invalidRoles; +} + /* * this method first checks explicit roles, if no explicit role, it will check the implicit role * return: null if any explicit role is invalid, @@ -67,10 +106,8 @@ export function getInvalidAriaAttributes(ruleContext: Element): string[] { } /* - * check if any explicit role specified for the element is a valid ARIA role - * return: null if no explicit role is defined, - * true if the role(s) are defined in ARIA - * false if any role is not defined in ARIA + * get conflict Aria and Html attributes + * return: a list of Aria and Html attribute pairs that are conflict */ export function getConflictAriaAndHtmlAttributes(elem: Element) { @@ -90,3 +127,13 @@ export function getConflictAriaAndHtmlAttributes(elem: Element) { } return ret; } + +/* + * get conflict Aria and Html attributes + * return: a list of Aria and Html attribute pairs that are conflict +*/ +export function isTableDescendant(contextHierarchies?: RuleContextHierarchy) { + if (!contextHierarchies) return null; + + return contextHierarchies["aria"].filter(hier => ["table", "grid", "treegrid"].includes(hier.role)); +} From f7b834e945ba570f76c85763abdb4057addcd746 Mon Sep 17 00:00:00 2001 From: Shunguo Yan Date: Wed, 22 Mar 2023 10:33:31 -0500 Subject: [PATCH 6/7] update dependencies for aria_semantics role #1330 --- .../v4/rules/Rpt_Aria_ValidPropertyValue.ts | 1 + .../src/v4/rules/aria_attribute_deprecated.ts | 4 +- .../src/v4/rules/aria_role_redundant.ts | 16 ++-- .../src/v4/rules/aria_semantics.ts | 73 +++++-------------- 4 files changed, 29 insertions(+), 65 deletions(-) diff --git a/accessibility-checker-engine/src/v4/rules/Rpt_Aria_ValidPropertyValue.ts b/accessibility-checker-engine/src/v4/rules/Rpt_Aria_ValidPropertyValue.ts index 37a035255..4bd930bc5 100644 --- a/accessibility-checker-engine/src/v4/rules/Rpt_Aria_ValidPropertyValue.ts +++ b/accessibility-checker-engine/src/v4/rules/Rpt_Aria_ValidPropertyValue.ts @@ -19,6 +19,7 @@ import { ARIADefinitions } from "../../v2/aria/ARIADefinitions"; export let Rpt_Aria_ValidPropertyValue: Rule = { id: "Rpt_Aria_ValidPropertyValue", context: "dom:*", + dependencies: ["Rpt_Aria_ValidProperty"], help: { "en-US": { "group": "Rpt_Aria_ValidPropertyValue.html", diff --git a/accessibility-checker-engine/src/v4/rules/aria_attribute_deprecated.ts b/accessibility-checker-engine/src/v4/rules/aria_attribute_deprecated.ts index a2fc50de4..17aee5e09 100644 --- a/accessibility-checker-engine/src/v4/rules/aria_attribute_deprecated.ts +++ b/accessibility-checker-engine/src/v4/rules/aria_attribute_deprecated.ts @@ -47,9 +47,7 @@ export let aria_attribute_deprecated: Rule = { act: [], run: (context: RuleContext, options?: {}, contextHierarchies?: RuleContextHierarchy): RuleResult | RuleResult[] => { const ruleContext = context["dom"].node as Element; - // dependency check: if the ARIA attribute is completely invalid, skip this check - if (getCache(ruleContext, "aria_semantics_role", "") === "Fail_1") return null; - + let domAttributes = ruleContext.attributes; let ariaAttrs = []; if (domAttributes) { diff --git a/accessibility-checker-engine/src/v4/rules/aria_role_redundant.ts b/accessibility-checker-engine/src/v4/rules/aria_role_redundant.ts index 3ccd7d3b1..710468180 100644 --- a/accessibility-checker-engine/src/v4/rules/aria_role_redundant.ts +++ b/accessibility-checker-engine/src/v4/rules/aria_role_redundant.ts @@ -15,7 +15,7 @@ import { Rule, RuleResult, RuleFail, RuleContext, RulePass, RuleContextHierarchy import { eRulePolicy, eToolkitLevel } from "../api/IRule"; import { RPTUtil } from "../../v2/checker/accessibility/util/legacy"; import { getCache } from "../util/CacheUtil"; -import { isTableDescendant } from "../util/CommonUtil"; +import { isTableDescendant, areRolesDefined } from "../util/CommonUtil"; export let aria_role_redundant: Rule = { id: "aria_role_redundant", @@ -45,9 +45,14 @@ export let aria_role_redundant: Rule = { const ruleContext = context["dom"].node as Element; let elemName = ruleContext.tagName.toLowerCase(); - // dependency check: if the ARIA attribute is completely invalid, skip this check - if (getCache(ruleContext, "aria_semantics_role", "") === "Fail_1") return null; - + let ariaRoles = RPTUtil.getRoles(ruleContext, false); + if (!ariaRoles || ariaRoles.length === 0) return; + + // the invalid role case: handled by Rpt_Aria_ValidRole. Ignore to avoid duplicated report + let role_defined = areRolesDefined(ariaRoles); + if (!role_defined) + return null; + // dependency check: if it's already failed in the parent relation, then skip this check if (["td", "th", "tr"].includes(elemName)) { let parentRole = isTableDescendant(contextHierarchies); @@ -55,9 +60,6 @@ export let aria_role_redundant: Rule = { return null; } - let ariaRoles = RPTUtil.getRoles(ruleContext, false); - if (!ariaRoles || ariaRoles.length === 0) return; - let implicitRoles = RPTUtil.getImplicitRole(ruleContext); if (!implicitRoles || implicitRoles.length === 0) return RulePass("pass"); diff --git a/accessibility-checker-engine/src/v4/rules/aria_semantics.ts b/accessibility-checker-engine/src/v4/rules/aria_semantics.ts index e91c2bd39..02bb4a27e 100644 --- a/accessibility-checker-engine/src/v4/rules/aria_semantics.ts +++ b/accessibility-checker-engine/src/v4/rules/aria_semantics.ts @@ -14,8 +14,7 @@ import { Rule, RuleResult, RuleFail, RuleContext, RulePass, RuleContextHierarchy } from "../api/IRule"; import { eRulePolicy, eToolkitLevel } from "../api/IRule"; import { RPTUtil } from "../../v2/checker/accessibility/util/legacy"; -import { getCache, setCache } from "../util/CacheUtil"; -import { getInvalidAriaAttributes, areRolesDefined, isTableDescendant } from "../util/CommonUtil"; +import { getInvalidAriaAttributes, areRolesDefined, isTableDescendant, getInvalidRoles } from "../util/CommonUtil"; export let aria_semantics_role: Rule = { id: "aria_semantics_role", @@ -59,68 +58,32 @@ export let aria_semantics_role: Rule = { } let domRoles: string[] = RPTUtil.getUserDefinedRoles(ruleContext); + if (!domRoles || domRoles.length ===0) + return null; // check the 'generic' role first - if (domRoles && domRoles.includes('generic')) { - setCache(ruleContext, "aria_semantics_role", "Fail_1"); + if (domRoles.includes('generic')) return RuleFail("Fail_1", ["generic", tagName]); - } // the invalid role case: handled by Rpt_Aria_ValidRole. Ignore to avoid duplicated report let role_defined = areRolesDefined(domRoles); - if (domRoles && domRoles.length > 0 && !role_defined) + if (!role_defined) return null; - // Roles allowed on this node - let allowedRoles = []; - - // Failing roles - let failRoleTokens = []; - // Passing roles - let passRoleTokens = []; + let invalidRoles = getInvalidRoles(ruleContext); + if (invalidRoles === null || invalidRoles.length ===0) + return RulePass("Pass_0", [domRoles.join(", "), tagName]); - let tagProperty = RPTUtil.getElementAriaProperty(ruleContext); - allowedRoles = RPTUtil.getAllowedAriaRoles(ruleContext, tagProperty); + if (invalidRoles.includes("presentation") || invalidRoles.includes("none") && RPTUtil.isTabbable(ruleContext)) + return RuleFail("Fail_2", [invalidRoles.join(", "), tagName]); - if (!allowedRoles || allowedRoles.length === 0) { - RPTUtil.concatUniqueArrayItemList(domRoles, failRoleTokens); - } else { - if (allowedRoles && allowedRoles.includes('any')) { - RPTUtil.concatUniqueArrayItemList(domRoles, passRoleTokens); - } else { - // Testing restrictions for each role and adding the corresponding attributes to the allowed attribute list - for (let i = 0; i < domRoles.length; i++) { - if (!allowedRoles.includes(domRoles[i])) { - if (!failRoleTokens.includes(domRoles[i])) { - failRoleTokens.push(domRoles[i]); - } - } else if (!passRoleTokens.includes(domRoles[i])) { - passRoleTokens.push(domRoles[i]) - } - /**else if (allowedRoles.includes("any")) { - //if (domRoles[i] === 'generic' && failRoleTokens.indexOf(domRoles[i]) === -1) { - // failRoleTokens.push(domRoles[i]); - //} else { - if (passRoleTokens.indexOf(domRoles[i]) === -1) - passRoleTokens.push(domRoles[i]); - //} - }*/ - } // for loop - } - } - if (failRoleTokens.includes("presentation") || failRoleTokens.includes("none") && RPTUtil.isTabbable(ruleContext)) { - return RuleFail("Fail_2", [failRoleTokens.join(", "), tagName]); - } else if (failRoleTokens.length > 0) { - setCache(ruleContext, "aria_semantics_role", "Fail_1"); - return RuleFail("Fail_1", [failRoleTokens.join(", "), tagName]); - } else if (passRoleTokens.length > 0) { - return RulePass("Pass_0", [passRoleTokens.join(", "), tagName]); - } else { - return null; - } - - // below for listing all allowed role and attributes. We can delete it if we are not using it next year (2018) #283 - // return new ValidationResult(passed, [ruleContext], '', '', passed == true ? [] : [roleOrAttributeTokens, tagName, allowedRoleOrAttributeTokens]); + if (invalidRoles.length > 0) + return RuleFail("Fail_1", [invalidRoles.join(", "), tagName]); + + if (domRoles.length > 0) + return RulePass("Pass_0", [domRoles.join(", "), tagName]); + + return null; } } @@ -131,7 +94,7 @@ export let aria_attribute_allowed: Rule = { id: "aria_attribute_allowed", context: "dom:*", // The the ARIA role is completely invalid, skip this check - dependencies: ["aria_semantics_role"], + dependencies: ["aria_attribute_deprecated", "aria_semantics_role"], help: { "en-US": { "group": "aria_attribute_allowed.html", From fd830d17f0689ba81e60556bff11c505110a66c8 Mon Sep 17 00:00:00 2001 From: Shunguo Yan Date: Wed, 22 Mar 2023 10:46:57 -0500 Subject: [PATCH 7/7] clean up the code #1330 --- .../src/v4/rules/aria_attribute_conflict.ts | 32 ------------------- .../src/v4/rules/aria_attribute_redundant.ts | 5 +-- 2 files changed, 1 insertion(+), 36 deletions(-) diff --git a/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts b/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts index 79f116dcc..9287cc1d3 100644 --- a/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts +++ b/accessibility-checker-engine/src/v4/rules/aria_attribute_conflict.ts @@ -69,37 +69,5 @@ export let aria_attribute_conflict: Rule = { return ret; return null; - /** - let domAttributes = ruleContext.attributes; - let ariaAttrs = []; - let htmlAttrs = []; - if (domAttributes) { - for (let i = 0; i < domAttributes.length; i++) { - let attrName = domAttributes[i].name.trim().toLowerCase(); - let attrValue = ruleContext.getAttribute(attrName); - if (attrValue === '') attrValue = null; - if (attrName.substring(0, 5) === 'aria-') - ariaAttrs.push({name: attrName, value: attrValue}); - else - htmlAttrs.push({name: attrName, value: attrValue}); - } - } - let ret = []; - for (let i = 0; i < ariaAttrs.length; i++) { - const examinedHtmlAtrNames = RPTUtil.getConflictOrOverlappingHtmlAttribute(ariaAttrs[i], htmlAttrs, 'conflict'); - if (examinedHtmlAtrNames === null) continue; - examinedHtmlAtrNames.forEach(item => { - if (item['result'] === 'Pass') { //pass - ret.push(RulePass("pass")); - } else if (item['result'] === 'Failed') { //failed - setCache(ruleContext, "aria_attribute_conflict", "fail_conflict"); - ret.push(RuleFail("fail_conflict", [ariaAttrs[i]['name'], item['attr']])); - } - }); - } - if (ret.length > 0) - return ret; - return null; - */ } } \ No newline at end of file diff --git a/accessibility-checker-engine/src/v4/rules/aria_attribute_redundant.ts b/accessibility-checker-engine/src/v4/rules/aria_attribute_redundant.ts index 582195f01..296553cfc 100644 --- a/accessibility-checker-engine/src/v4/rules/aria_attribute_redundant.ts +++ b/accessibility-checker-engine/src/v4/rules/aria_attribute_redundant.ts @@ -45,16 +45,13 @@ export let aria_attribute_redundant: Rule = { act: [], run: (context: RuleContext, options?: {}, contextHierarchies?: RuleContextHierarchy): RuleResult | RuleResult[] => { const ruleContext = context["dom"].node as Element; - // dependency check: if the ARIA attribute is completely invalid, skip this check - - //if (getCache(ruleContext, "aria_attribute_allowed", "") === "Fail") return null; + // dependency check: if the ARIA attribute is completely invalid, skip this check let invalidAttributes = getInvalidAriaAttributes(ruleContext); if (invalidAttributes && invalidAttributes.length > 0) return null; // if conflict already reported, ignore reporting overlap - //if (getCache(ruleContext, "aria_attribute_conflict", "") === "fail_conflict") return null; let conflictAttributes = getConflictAriaAndHtmlAttributes(ruleContext); if (conflictAttributes && conflictAttributes.length > 0) return null;