From cf70404f98d5ba1bb985a883026b38f717cc6a37 Mon Sep 17 00:00:00 2001 From: Cassey Lottman Date: Wed, 19 May 2021 12:50:23 -0500 Subject: [PATCH 01/13] fix typo --- doc/standards-object.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/standards-object.md b/doc/standards-object.md index 2bddd6ee7e..3c2f4157df 100644 --- a/doc/standards-object.md +++ b/doc/standards-object.md @@ -95,7 +95,7 @@ The [`htmlElms`](../lib/standards/html-elms.js) object defines valid HTML elemen - `aria-allowed-attr` - Checks if the attribute can be used on the element from the `noAriaAttrs` property. - `aria-allowed-role` - Checks if the role can be used on the HTML element from the `allowedRoles` property. -- `aria-required-attrs` - Checks if any required attrs are defied implicitly on the element from the `implicitAttrs` property. +- `aria-required-attrs` - Checks if any required attrs are defined implicitly on the element from the `implicitAttrs` property. ### Structure From 38f7b0a2e266494fcbe1d7d200aed83470c18f38 Mon Sep 17 00:00:00 2001 From: Cassey Lottman Date: Wed, 19 May 2021 15:57:07 -0500 Subject: [PATCH 02/13] add matcher so rule only applies when element has an implicit role that could conflict --- lib/commons/aria/index.js | 1 + lib/core/base/metadata-function-map.js | 2 + .../presentation-role-conflict-matches.js | 7 +++ lib/rules/presentation-role-conflict.json | 1 + .../presentation-role-conflict.html | 2 - .../presentation-role-conflict.json | 3 +- .../presentation-role-conflict-matches.js | 43 +++++++++++++++++++ 7 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 lib/rules/presentation-role-conflict-matches.js create mode 100644 test/rule-matches/presentation-role-conflict-matches.js diff --git a/lib/commons/aria/index.js b/lib/commons/aria/index.js index 14de4ebc0c..d9f7bd4805 100644 --- a/lib/commons/aria/index.js +++ b/lib/commons/aria/index.js @@ -9,6 +9,7 @@ export { default as arialabelledbyText } from './arialabelledby-text'; export { default as getAccessibleRefs } from './get-accessible-refs'; export { default as getElementUnallowedRoles } from './get-element-unallowed-roles'; export { default as getExplicitRole } from './get-explicit-role'; +export { default as getImplicitRole } from './implicit-role'; export { default as getOwnedVirtual } from './get-owned-virtual'; export { default as getRoleType } from './get-role-type'; export { default as getRole } from './get-role'; diff --git a/lib/core/base/metadata-function-map.js b/lib/core/base/metadata-function-map.js index cf1a17de4a..346467c99e 100644 --- a/lib/core/base/metadata-function-map.js +++ b/lib/core/base/metadata-function-map.js @@ -164,6 +164,7 @@ import noNamingMethodMatches from '../../rules/no-naming-method-matches'; import noRoleMatches from '../../rules/no-role-matches'; import notHtmlMatches from '../../rules/not-html-matches'; import pAsHeadingMatches from '../../rules/p-as-heading-matches'; +import presentationRoleConflictMatches from '../../rules/presentation-role-conflict-matches'; import scrollableRegionFocusableMatches from '../../rules/scrollable-region-focusable-matches'; import skipLinkMatches from '../../rules/skip-link-matches'; import svgNamespaceMatches from '../../rules/svg-namespace-matches'; @@ -339,6 +340,7 @@ const metadataFunctionMap = { 'no-role-matches': noRoleMatches, 'not-html-matches': notHtmlMatches, 'p-as-heading-matches': pAsHeadingMatches, + 'presentation-role-conflict-matches': presentationRoleConflictMatches, 'scrollable-region-focusable-matches': scrollableRegionFocusableMatches, 'skip-link-matches': skipLinkMatches, 'svg-namespace-matches': svgNamespaceMatches, diff --git a/lib/rules/presentation-role-conflict-matches.js b/lib/rules/presentation-role-conflict-matches.js new file mode 100644 index 0000000000..dde05fe454 --- /dev/null +++ b/lib/rules/presentation-role-conflict-matches.js @@ -0,0 +1,7 @@ +import { getImplicitRole } from '../commons/aria'; + +function presentationRoleConflictMatches(node) { + return getImplicitRole(node) !== null; +} + +export default presentationRoleConflictMatches; diff --git a/lib/rules/presentation-role-conflict.json b/lib/rules/presentation-role-conflict.json index 696ed07b25..2940780417 100644 --- a/lib/rules/presentation-role-conflict.json +++ b/lib/rules/presentation-role-conflict.json @@ -1,5 +1,6 @@ { "id": "presentation-role-conflict", + "matches": "presentation-role-conflict-matches", "selector": "[role=\"none\"], [role=\"presentation\"]", "tags": ["cat.aria", "best-practice"], "metadata": { diff --git a/test/integration/rules/presentation-role-conflict/presentation-role-conflict.html b/test/integration/rules/presentation-role-conflict/presentation-role-conflict.html index 9d95d87d89..c97fe93998 100644 --- a/test/integration/rules/presentation-role-conflict/presentation-role-conflict.html +++ b/test/integration/rules/presentation-role-conflict/presentation-role-conflict.html @@ -1,4 +1,2 @@ - -

Hello

diff --git a/test/integration/rules/presentation-role-conflict/presentation-role-conflict.json b/test/integration/rules/presentation-role-conflict/presentation-role-conflict.json index 0e12ace4d8..0cd8dc357f 100644 --- a/test/integration/rules/presentation-role-conflict/presentation-role-conflict.json +++ b/test/integration/rules/presentation-role-conflict/presentation-role-conflict.json @@ -1,6 +1,5 @@ { "rule": "presentation-role-conflict", "description": "presentation-role-conflict tests", - "violations": [["#violation1"], ["#violation2"]], - "passes": [["#pass1"], ["#pass2"]] + "violations": [["#violation1"], ["#violation2"]] } diff --git a/test/rule-matches/presentation-role-conflict-matches.js b/test/rule-matches/presentation-role-conflict-matches.js new file mode 100644 index 0000000000..b2e838806e --- /dev/null +++ b/test/rule-matches/presentation-role-conflict-matches.js @@ -0,0 +1,43 @@ +describe('presentation-role-conflict-matches', function() { + 'use strict'; + + var rule; + var fixture = document.getElementById('fixture'); + var flatTreeSetup = axe.testUtils.flatTreeSetup; + + beforeEach(function() { + rule = axe.utils.getRule('presentation-role-conflict'); + }); + + afterEach(function() { + fixture.innerHTML = ''; + }); + + it('is a function', function() { + assert.isFunction(rule.matches); + }); + + it('matches elements with an implicit role', function() { + fixture.innerHTML = '
'; + flatTreeSetup(fixture); + var target = fixture.querySelector('#target'); + + assert.isTrue(rule.matches(target)); + }); + + it('does not match elements with no implicit role', function() { + fixture.innerHTML = '
'; + flatTreeSetup(fixture); + var target = fixture.querySelector('#target'); + + assert.isFalse(rule.matches(target)); + }); + + it('does not match elements with no implicit role even if they are focusable and have an explicit role', function() { + fixture.innerHTML = '
'; + flatTreeSetup(fixture); + var target = fixture.querySelector('#target'); + + assert.isFalse(rule.matches(target)); + }); +}); From 8f5111c43c6ccbbd0641557ac41cd00d2a7f63c9 Mon Sep 17 00:00:00 2001 From: Cassey Lottman Date: Wed, 19 May 2021 15:57:07 -0500 Subject: [PATCH 03/13] add matcher so rule only applies when element has an implicit role that could conflict --- lib/commons/aria/index.js | 1 + lib/core/base/metadata-function-map.js | 2 + .../presentation-role-conflict-matches.js | 7 +++ lib/rules/presentation-role-conflict.json | 1 + .../presentation-role-conflict.html | 4 +- .../presentation-role-conflict.json | 3 +- .../presentation-role-conflict-matches.js | 43 +++++++++++++++++++ 7 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 lib/rules/presentation-role-conflict-matches.js create mode 100644 test/rule-matches/presentation-role-conflict-matches.js diff --git a/lib/commons/aria/index.js b/lib/commons/aria/index.js index 14de4ebc0c..d9f7bd4805 100644 --- a/lib/commons/aria/index.js +++ b/lib/commons/aria/index.js @@ -9,6 +9,7 @@ export { default as arialabelledbyText } from './arialabelledby-text'; export { default as getAccessibleRefs } from './get-accessible-refs'; export { default as getElementUnallowedRoles } from './get-element-unallowed-roles'; export { default as getExplicitRole } from './get-explicit-role'; +export { default as getImplicitRole } from './implicit-role'; export { default as getOwnedVirtual } from './get-owned-virtual'; export { default as getRoleType } from './get-role-type'; export { default as getRole } from './get-role'; diff --git a/lib/core/base/metadata-function-map.js b/lib/core/base/metadata-function-map.js index cf1a17de4a..346467c99e 100644 --- a/lib/core/base/metadata-function-map.js +++ b/lib/core/base/metadata-function-map.js @@ -164,6 +164,7 @@ import noNamingMethodMatches from '../../rules/no-naming-method-matches'; import noRoleMatches from '../../rules/no-role-matches'; import notHtmlMatches from '../../rules/not-html-matches'; import pAsHeadingMatches from '../../rules/p-as-heading-matches'; +import presentationRoleConflictMatches from '../../rules/presentation-role-conflict-matches'; import scrollableRegionFocusableMatches from '../../rules/scrollable-region-focusable-matches'; import skipLinkMatches from '../../rules/skip-link-matches'; import svgNamespaceMatches from '../../rules/svg-namespace-matches'; @@ -339,6 +340,7 @@ const metadataFunctionMap = { 'no-role-matches': noRoleMatches, 'not-html-matches': notHtmlMatches, 'p-as-heading-matches': pAsHeadingMatches, + 'presentation-role-conflict-matches': presentationRoleConflictMatches, 'scrollable-region-focusable-matches': scrollableRegionFocusableMatches, 'skip-link-matches': skipLinkMatches, 'svg-namespace-matches': svgNamespaceMatches, diff --git a/lib/rules/presentation-role-conflict-matches.js b/lib/rules/presentation-role-conflict-matches.js new file mode 100644 index 0000000000..dde05fe454 --- /dev/null +++ b/lib/rules/presentation-role-conflict-matches.js @@ -0,0 +1,7 @@ +import { getImplicitRole } from '../commons/aria'; + +function presentationRoleConflictMatches(node) { + return getImplicitRole(node) !== null; +} + +export default presentationRoleConflictMatches; diff --git a/lib/rules/presentation-role-conflict.json b/lib/rules/presentation-role-conflict.json index 696ed07b25..2940780417 100644 --- a/lib/rules/presentation-role-conflict.json +++ b/lib/rules/presentation-role-conflict.json @@ -1,5 +1,6 @@ { "id": "presentation-role-conflict", + "matches": "presentation-role-conflict-matches", "selector": "[role=\"none\"], [role=\"presentation\"]", "tags": ["cat.aria", "best-practice"], "metadata": { diff --git a/test/integration/rules/presentation-role-conflict/presentation-role-conflict.html b/test/integration/rules/presentation-role-conflict/presentation-role-conflict.html index 9d95d87d89..547d05251a 100644 --- a/test/integration/rules/presentation-role-conflict/presentation-role-conflict.html +++ b/test/integration/rules/presentation-role-conflict/presentation-role-conflict.html @@ -1,4 +1,4 @@ - -

Hello

+ + diff --git a/test/integration/rules/presentation-role-conflict/presentation-role-conflict.json b/test/integration/rules/presentation-role-conflict/presentation-role-conflict.json index 0e12ace4d8..0cd8dc357f 100644 --- a/test/integration/rules/presentation-role-conflict/presentation-role-conflict.json +++ b/test/integration/rules/presentation-role-conflict/presentation-role-conflict.json @@ -1,6 +1,5 @@ { "rule": "presentation-role-conflict", "description": "presentation-role-conflict tests", - "violations": [["#violation1"], ["#violation2"]], - "passes": [["#pass1"], ["#pass2"]] + "violations": [["#violation1"], ["#violation2"]] } diff --git a/test/rule-matches/presentation-role-conflict-matches.js b/test/rule-matches/presentation-role-conflict-matches.js new file mode 100644 index 0000000000..b2e838806e --- /dev/null +++ b/test/rule-matches/presentation-role-conflict-matches.js @@ -0,0 +1,43 @@ +describe('presentation-role-conflict-matches', function() { + 'use strict'; + + var rule; + var fixture = document.getElementById('fixture'); + var flatTreeSetup = axe.testUtils.flatTreeSetup; + + beforeEach(function() { + rule = axe.utils.getRule('presentation-role-conflict'); + }); + + afterEach(function() { + fixture.innerHTML = ''; + }); + + it('is a function', function() { + assert.isFunction(rule.matches); + }); + + it('matches elements with an implicit role', function() { + fixture.innerHTML = '
'; + flatTreeSetup(fixture); + var target = fixture.querySelector('#target'); + + assert.isTrue(rule.matches(target)); + }); + + it('does not match elements with no implicit role', function() { + fixture.innerHTML = '
'; + flatTreeSetup(fixture); + var target = fixture.querySelector('#target'); + + assert.isFalse(rule.matches(target)); + }); + + it('does not match elements with no implicit role even if they are focusable and have an explicit role', function() { + fixture.innerHTML = '
'; + flatTreeSetup(fixture); + var target = fixture.querySelector('#target'); + + assert.isFalse(rule.matches(target)); + }); +}); From ee8bc8d5db1ff848b71b63e257228abefa4c46ba Mon Sep 17 00:00:00 2001 From: Cassey Lottman Date: Mon, 24 May 2021 09:50:23 -0500 Subject: [PATCH 04/13] use virtual node --- .../presentation-role-conflict-matches.js | 4 +-- .../presentation-role-conflict-matches.js | 25 +++++++------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/lib/rules/presentation-role-conflict-matches.js b/lib/rules/presentation-role-conflict-matches.js index dde05fe454..0b0d4b842b 100644 --- a/lib/rules/presentation-role-conflict-matches.js +++ b/lib/rules/presentation-role-conflict-matches.js @@ -1,7 +1,7 @@ import { getImplicitRole } from '../commons/aria'; -function presentationRoleConflictMatches(node) { - return getImplicitRole(node) !== null; +function presentationRoleConflictMatches(node, virtualNode) { + return getImplicitRole(virtualNode) !== null; } export default presentationRoleConflictMatches; diff --git a/test/rule-matches/presentation-role-conflict-matches.js b/test/rule-matches/presentation-role-conflict-matches.js index b2e838806e..9fd5333d93 100644 --- a/test/rule-matches/presentation-role-conflict-matches.js +++ b/test/rule-matches/presentation-role-conflict-matches.js @@ -3,7 +3,7 @@ describe('presentation-role-conflict-matches', function() { var rule; var fixture = document.getElementById('fixture'); - var flatTreeSetup = axe.testUtils.flatTreeSetup; + var queryFixture = axe.testUtils.queryFixture; beforeEach(function() { rule = axe.utils.getRule('presentation-role-conflict'); @@ -18,26 +18,19 @@ describe('presentation-role-conflict-matches', function() { }); it('matches elements with an implicit role', function() { - fixture.innerHTML = '
'; - flatTreeSetup(fixture); - var target = fixture.querySelector('#target'); - - assert.isTrue(rule.matches(target)); + var vNode = queryFixture('
'); + assert.isTrue(rule.matches(null, vNode)); }); it('does not match elements with no implicit role', function() { - fixture.innerHTML = '
'; - flatTreeSetup(fixture); - var target = fixture.querySelector('#target'); - - assert.isFalse(rule.matches(target)); + var vNode = queryFixture('
'); + assert.isFalse(rule.matches(null, vNode)); }); it('does not match elements with no implicit role even if they are focusable and have an explicit role', function() { - fixture.innerHTML = '
'; - flatTreeSetup(fixture); - var target = fixture.querySelector('#target'); - - assert.isFalse(rule.matches(target)); + var vNode = queryFixture( + '
' + ); + assert.isFalse(rule.matches(null, vNode)); }); }); From 3b54401faecc78dcef4374edea44346f17933785 Mon Sep 17 00:00:00 2001 From: Cassey Lottman Date: Tue, 25 May 2021 16:35:03 -0500 Subject: [PATCH 05/13] add chromium implicit roles to lookup --- .../aria/aria-prohibited-attr-evaluate.js | 29 +++++++++++-------- lib/checks/aria/aria-prohibited-attr.json | 15 +--------- lib/commons/aria/implicit-role.js | 25 +++++++++++----- test/commons/aria/implicit-role.js | 17 ++++++++++- 4 files changed, 52 insertions(+), 34 deletions(-) diff --git a/lib/checks/aria/aria-prohibited-attr-evaluate.js b/lib/checks/aria/aria-prohibited-attr-evaluate.js index e74aec05d1..eab2496687 100644 --- a/lib/checks/aria/aria-prohibited-attr-evaluate.js +++ b/lib/checks/aria/aria-prohibited-attr-evaluate.js @@ -1,4 +1,4 @@ -import { getRole } from '../../commons/aria'; +import { getRole, getImplicitRole } from '../../commons/aria'; import { sanitize, subtreeText } from '../../commons/text'; import standards from '../../standards'; @@ -27,36 +27,41 @@ import standards from '../../standards'; * @return {Boolean} True if the element uses any prohibited ARIA attributes. False otherwise. */ function ariaProhibitedAttrEvaluate(node, options = {}, virtualNode) { - const { elementsAllowedAriaLabel = [] } = options; - const prohibitedList = listProhibitedAttrs(virtualNode, elementsAllowedAriaLabel); - + const extraElementsAllowedAriaLabel = options.elementsAllowedAriaLabel || []; + + const prohibitedList = listProhibitedAttrs( + virtualNode, + extraElementsAllowedAriaLabel + ); + const prohibited = prohibitedList.filter(attrName => { if (!virtualNode.attrNames.includes(attrName)) { return false; } - return sanitize(virtualNode.attr(attrName)) !== '' + return sanitize(virtualNode.attr(attrName)) !== ''; }); if (prohibited.length === 0) { return false; } - + this.data(prohibited); - const hasTextContent = sanitize(subtreeText(virtualNode)) !== '' + const hasTextContent = sanitize(subtreeText(virtualNode)) !== ''; // Don't fail if there is text content to announce return hasTextContent ? undefined : true; } function listProhibitedAttrs(virtualNode, elementsAllowedAriaLabel) { const role = getRole(virtualNode); - const roleSpec = standards.ariaRoles[role] + const roleSpec = standards.ariaRoles[role]; if (roleSpec) { return roleSpec.prohibitedAttrs || []; } - - const { nodeName } = virtualNode.props - if (elementsAllowedAriaLabel.includes(nodeName)) { - return [] + + const { nodeName } = virtualNode.props; + const implicitRole = getImplicitRole(virtualNode, { chromiumRoles: true }); + if (!!implicitRole || elementsAllowedAriaLabel.includes(nodeName)) { + return []; } return ['aria-label', 'aria-labelledby']; } diff --git a/lib/checks/aria/aria-prohibited-attr.json b/lib/checks/aria/aria-prohibited-attr.json index 834f163a43..b5c3f95d6f 100644 --- a/lib/checks/aria/aria-prohibited-attr.json +++ b/lib/checks/aria/aria-prohibited-attr.json @@ -2,20 +2,7 @@ "id": "aria-prohibited-attr", "evaluate": "aria-prohibited-attr-evaluate", "options": { - "elementsAllowedAriaLabel": [ - "audio", - "applet", - "canvas", - "dl", - "embed", - "iframe", - "input", - "label", - "meter", - "object", - "svg", - "video" - ] + "elementsAllowedAriaLabel": ["applet", "input"] }, "metadata": { "impact": "serious", diff --git a/lib/commons/aria/implicit-role.js b/lib/commons/aria/implicit-role.js index e806b2af6a..1b5dc43f67 100644 --- a/lib/commons/aria/implicit-role.js +++ b/lib/commons/aria/implicit-role.js @@ -2,6 +2,19 @@ import implicitHtmlRoles from '../standards/implicit-html-roles'; import { getNodeFromTree } from '../../core/utils'; import AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-node'; +const implicitChromiumRoles = { + audio: 'Audio', + canvas: 'Canvas', + dl: 'DescriptionList', + embed: 'EmbeddedObject', + iframe: 'Iframe', + label: 'Label', + meter: 'progressbar', + object: 'PluginObject', + svg: 'SVGRoot', + video: 'Video' +}; + /** * Get the implicit role for a given node * @method implicitRole @@ -10,7 +23,7 @@ import AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-no * @param {HTMLElement|VirtualNode} node The node to test * @return {Mixed} Either the role or `null` if there is none */ -function implicitRole(node) { +function implicitRole(node, { chromiumRoles } = {}) { const vNode = node instanceof AbstractVirtuaNode ? node : getNodeFromTree(node); node = vNode.actualNode; @@ -25,16 +38,14 @@ function implicitRole(node) { ); } - // until we have proper implicit role lookups for svgs we will - // avoid giving them one - if (node && node.namespaceURI === 'http://www.w3.org/2000/svg') { - return null; - } - const nodeName = vNode.props.nodeName; const role = implicitHtmlRoles[nodeName]; if (!role) { + if (chromiumRoles) { + const chromiumRole = implicitChromiumRoles[nodeName]; + return chromiumRole || null; + } return null; } diff --git a/test/commons/aria/implicit-role.js b/test/commons/aria/implicit-role.js index 074f873bde..7cb19c94d4 100644 --- a/test/commons/aria/implicit-role.js +++ b/test/commons/aria/implicit-role.js @@ -32,6 +32,20 @@ describe('aria.implicitRole', function() { assert.isNull(implicitRole(node)); }); + it('should return null if there is no implicit role when not considering chromium', function() { + fixture.innerHTML = ''; + var node = fixture.querySelector('#target'); + flatTreeSetup(fixture); + assert.isNull(implicitRole(node)); + }); + + it('should return the chromium implicit role for elements that have one', function() { + fixture.innerHTML = ''; + var node = fixture.querySelector('#target'); + flatTreeSetup(fixture); + assert.equal(implicitRole(node, { chromiumRoles: true }), 'Canvas'); + }); + it('should return link for "a[href]"', function() { fixture.innerHTML = 'link'; var node = fixture.querySelector('#target'); @@ -287,7 +301,8 @@ describe('aria.implicitRole', function() { }); it('should return textbox for "input[type=password][list]"', function() { - fixture.innerHTML = '' + + fixture.innerHTML = + '' + ''; var node = fixture.querySelector('#target'); flatTreeSetup(fixture); From 2c2033d752599d5c1f97e3910d3241909f400b70 Mon Sep 17 00:00:00 2001 From: Cassey Lottman Date: Thu, 27 May 2021 11:06:47 -0500 Subject: [PATCH 06/13] remove temp variable Co-authored-by: Wilco Fiers --- lib/commons/aria/implicit-role.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/commons/aria/implicit-role.js b/lib/commons/aria/implicit-role.js index 1b5dc43f67..7534594673 100644 --- a/lib/commons/aria/implicit-role.js +++ b/lib/commons/aria/implicit-role.js @@ -43,8 +43,7 @@ function implicitRole(node, { chromiumRoles } = {}) { if (!role) { if (chromiumRoles) { - const chromiumRole = implicitChromiumRoles[nodeName]; - return chromiumRole || null; + return implicitChromiumRoles[nodeName] || null; } return null; } From d3a80b0af5bccba42a16d9b90e4b0e0453e93967 Mon Sep 17 00:00:00 2001 From: Cassey Lottman Date: Thu, 27 May 2021 11:31:59 -0500 Subject: [PATCH 07/13] use chromiumroles for getimplicitrole in this matcher Co-authored-by: Wilco Fiers --- lib/rules/presentation-role-conflict-matches.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/presentation-role-conflict-matches.js b/lib/rules/presentation-role-conflict-matches.js index 0b0d4b842b..d89c28b192 100644 --- a/lib/rules/presentation-role-conflict-matches.js +++ b/lib/rules/presentation-role-conflict-matches.js @@ -1,7 +1,7 @@ import { getImplicitRole } from '../commons/aria'; function presentationRoleConflictMatches(node, virtualNode) { - return getImplicitRole(virtualNode) !== null; + return getImplicitRole(virtualNode, { chromiumRoles: true }) !== null; } export default presentationRoleConflictMatches; From 5061e98042531d0a20ecbad9f424122080babb07 Mon Sep 17 00:00:00 2001 From: Cassey Lottman Date: Thu, 27 May 2021 12:53:34 -0500 Subject: [PATCH 08/13] rename chromiumRoles to includeChromiumRoles to add clarity and add it to getRole --- .../aria/aria-prohibited-attr-evaluate.js | 4 +++- lib/commons/aria/get-role.js | 18 +++++++++++++----- lib/commons/aria/implicit-role.js | 13 +++++-------- test/commons/aria/implicit-role.js | 2 +- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/checks/aria/aria-prohibited-attr-evaluate.js b/lib/checks/aria/aria-prohibited-attr-evaluate.js index eab2496687..a8491d44bd 100644 --- a/lib/checks/aria/aria-prohibited-attr-evaluate.js +++ b/lib/checks/aria/aria-prohibited-attr-evaluate.js @@ -59,7 +59,9 @@ function listProhibitedAttrs(virtualNode, elementsAllowedAriaLabel) { } const { nodeName } = virtualNode.props; - const implicitRole = getImplicitRole(virtualNode, { chromiumRoles: true }); + const implicitRole = getImplicitRole(virtualNode, { + includeChromiumRoles: true + }); if (!!implicitRole || elementsAllowedAriaLabel.includes(nodeName)) { return []; } diff --git a/lib/commons/aria/get-role.js b/lib/commons/aria/get-role.js index 7578c47967..bf56495156 100644 --- a/lib/commons/aria/get-role.js +++ b/lib/commons/aria/get-role.js @@ -82,8 +82,8 @@ function getInheritedRole(vNode, explicitRoleOptions) { return getInheritedRole(vNode.parent, explicitRoleOptions); } -function resolveImplicitRole(vNode, explicitRoleOptions) { - const implicitRole = getImplicitRole(vNode); +function resolveImplicitRole(vNode, includeChromiumRoles, explicitRoleOptions) { + const implicitRole = getImplicitRole(vNode, { includeChromiumRoles }); if (!implicitRole) { return null; @@ -118,7 +118,10 @@ function hasConflictResolution(vNode) { * @returns {string|null} Role or null * @deprecated noImplicit option is deprecated. Use aria.getExplicitRole instead. */ -function resolveRole(node, { noImplicit, ...explicitRoleOptions } = {}) { +function resolveRole( + node, + { noImplicit, includeChromiumRoles, ...explicitRoleOptions } = {} +) { const vNode = node instanceof AbstractVirtuaNode ? node : getNodeFromTree(node); if (vNode.props.nodeType !== 1) { @@ -128,7 +131,9 @@ function resolveRole(node, { noImplicit, ...explicitRoleOptions } = {}) { const explicitRole = getExplicitRole(vNode, explicitRoleOptions); if (!explicitRole) { - return noImplicit ? null : resolveImplicitRole(vNode, explicitRoleOptions); + return noImplicit + ? null + : resolveImplicitRole(vNode, includeChromiumRoles, explicitRoleOptions); } if (!['presentation', 'none'].includes(explicitRole)) { @@ -138,7 +143,9 @@ function resolveRole(node, { noImplicit, ...explicitRoleOptions } = {}) { if (hasConflictResolution(vNode)) { // return null if there is a conflict resolution but no implicit // has been set as the explicit role is not the true role - return noImplicit ? null : resolveImplicitRole(vNode, explicitRoleOptions); + return noImplicit + ? null + : resolveImplicitRole(vNode, includeChromiumRoles, explicitRoleOptions); } // role presentation or none and no conflict resolution @@ -158,6 +165,7 @@ function resolveRole(node, { noImplicit, ...explicitRoleOptions } = {}) { * @param {boolean} options.abstracts Allow role to be abstract * @param {boolean} options.dpub Allow role to be any (valid) doc-* roles * @param {boolean} options.noPresentational return null if role is presentation or none + * @param {boolean} options.includeChromiumRoles Include implicit roles from chromium-based browsers in role result * @returns {string|null} Role or null * * @deprecated noImplicit option is deprecated. Use aria.getExplicitRole instead. diff --git a/lib/commons/aria/implicit-role.js b/lib/commons/aria/implicit-role.js index 1b5dc43f67..d70f142d3e 100644 --- a/lib/commons/aria/implicit-role.js +++ b/lib/commons/aria/implicit-role.js @@ -23,7 +23,7 @@ const implicitChromiumRoles = { * @param {HTMLElement|VirtualNode} node The node to test * @return {Mixed} Either the role or `null` if there is none */ -function implicitRole(node, { chromiumRoles } = {}) { +function implicitRole(node, { includeChromiumRoles } = {}) { const vNode = node instanceof AbstractVirtuaNode ? node : getNodeFromTree(node); node = vNode.actualNode; @@ -41,19 +41,16 @@ function implicitRole(node, { chromiumRoles } = {}) { const nodeName = vNode.props.nodeName; const role = implicitHtmlRoles[nodeName]; - if (!role) { - if (chromiumRoles) { - const chromiumRole = implicitChromiumRoles[nodeName]; - return chromiumRole || null; - } - return null; + if (!role && includeChromiumRoles) { + const chromiumRole = implicitChromiumRoles[nodeName]; + return chromiumRole || null; } if (typeof role === 'function') { return role(vNode); } - return role; + return role || null; } export default implicitRole; diff --git a/test/commons/aria/implicit-role.js b/test/commons/aria/implicit-role.js index 7cb19c94d4..94f7556f46 100644 --- a/test/commons/aria/implicit-role.js +++ b/test/commons/aria/implicit-role.js @@ -43,7 +43,7 @@ describe('aria.implicitRole', function() { fixture.innerHTML = ''; var node = fixture.querySelector('#target'); flatTreeSetup(fixture); - assert.equal(implicitRole(node, { chromiumRoles: true }), 'Canvas'); + assert.equal(implicitRole(node, { includeChromiumRoles: true }), 'Canvas'); }); it('should return link for "a[href]"', function() { From 81836d2cdb68bf68631ad6711f760f5e4fe409c2 Mon Sep 17 00:00:00 2001 From: Cassey Lottman Date: Thu, 27 May 2021 13:18:27 -0500 Subject: [PATCH 09/13] refactor aria-prohibited-attr and add test --- lib/checks/aria/aria-prohibited-attr-evaluate.js | 9 +++------ test/checks/aria/aria-prohibited-attr.js | 11 ++++++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/checks/aria/aria-prohibited-attr-evaluate.js b/lib/checks/aria/aria-prohibited-attr-evaluate.js index a8491d44bd..98957320a9 100644 --- a/lib/checks/aria/aria-prohibited-attr-evaluate.js +++ b/lib/checks/aria/aria-prohibited-attr-evaluate.js @@ -1,4 +1,4 @@ -import { getRole, getImplicitRole } from '../../commons/aria'; +import { getRole } from '../../commons/aria'; import { sanitize, subtreeText } from '../../commons/text'; import standards from '../../standards'; @@ -52,17 +52,14 @@ function ariaProhibitedAttrEvaluate(node, options = {}, virtualNode) { } function listProhibitedAttrs(virtualNode, elementsAllowedAriaLabel) { - const role = getRole(virtualNode); + const role = getRole(virtualNode, { includeChromiumRoles: true }); const roleSpec = standards.ariaRoles[role]; if (roleSpec) { return roleSpec.prohibitedAttrs || []; } const { nodeName } = virtualNode.props; - const implicitRole = getImplicitRole(virtualNode, { - includeChromiumRoles: true - }); - if (!!implicitRole || elementsAllowedAriaLabel.includes(nodeName)) { + if (!!role || elementsAllowedAriaLabel.includes(nodeName)) { return []; } return ['aria-label', 'aria-labelledby']; diff --git a/test/checks/aria/aria-prohibited-attr.js b/test/checks/aria/aria-prohibited-attr.js index 12523cfa7d..4900b6e3f5 100644 --- a/test/checks/aria/aria-prohibited-attr.js +++ b/test/checks/aria/aria-prohibited-attr.js @@ -65,19 +65,24 @@ describe('aria-prohibited-attr', function() { assert.isFalse(checkEvaluate.apply(checkContext, params)); }); - it('should allow `elementsAllowedAriaLabel` nodes to have aria-label', function () { + it('should allow `elementsAllowedAriaLabel` nodes to have aria-label', function() { var params = checkSetup( '
', { elementsAllowedAriaLabel: ['div'] } ); assert.isFalse(checkEvaluate.apply(checkContext, params)); }); - - it('should not allow `elementsAllowedAriaLabel` nodes with a prohibited role', function () { + + it('should not allow `elementsAllowedAriaLabel` nodes with a prohibited role', function() { var params = checkSetup( '
', { elementsAllowedAriaLabel: ['div'] } ); assert.isTrue(checkEvaluate.apply(checkContext, params)); }); + + it('should allow elements that have an implicit role in chromium', function() { + var params = checkSetup(''); + assert.isFalse(checkEvaluate.apply(checkContext, params)); + }); }); From 13a66354a27ccfcc3f54f733789930a9f54357a3 Mon Sep 17 00:00:00 2001 From: Cassey Lottman Date: Thu, 27 May 2021 13:28:50 -0500 Subject: [PATCH 10/13] rename to has-implicit-chromium-role-matches --- lib/core/base/metadata-function-map.js | 4 ++-- lib/rules/has-implicit-chromium-role-matches.js | 7 +++++++ lib/rules/presentation-role-conflict-matches.js | 7 ------- lib/rules/presentation-role-conflict.json | 2 +- ...ct-matches.js => has-implicit-chromium-role-matches.js} | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 lib/rules/has-implicit-chromium-role-matches.js delete mode 100644 lib/rules/presentation-role-conflict-matches.js rename test/rule-matches/{presentation-role-conflict-matches.js => has-implicit-chromium-role-matches.js} (94%) diff --git a/lib/core/base/metadata-function-map.js b/lib/core/base/metadata-function-map.js index 346467c99e..6d74804a50 100644 --- a/lib/core/base/metadata-function-map.js +++ b/lib/core/base/metadata-function-map.js @@ -14,6 +14,7 @@ import ariaValidAttrEvaluate from '../../checks/aria/aria-valid-attr-evaluate'; import ariaValidAttrValueEvaluate from '../../checks/aria/aria-valid-attr-value-evaluate'; import fallbackroleEvaluate from '../../checks/aria/fallbackrole-evaluate'; import hasGlobalAriaAttributeEvaluate from '../../checks/aria/has-global-aria-attribute-evaluate'; +import hasImplicitChromiumRoleMatches from '../../rules/has-implicit-chromium-role-matches'; import hasWidgetRoleEvaluate from '../../checks/aria/has-widget-role-evaluate'; import invalidroleEvaluate from '../../checks/aria/invalidrole-evaluate'; import isElementFocusableEvaluate from '../../checks/aria/is-element-focusable-evaluate'; @@ -164,7 +165,6 @@ import noNamingMethodMatches from '../../rules/no-naming-method-matches'; import noRoleMatches from '../../rules/no-role-matches'; import notHtmlMatches from '../../rules/not-html-matches'; import pAsHeadingMatches from '../../rules/p-as-heading-matches'; -import presentationRoleConflictMatches from '../../rules/presentation-role-conflict-matches'; import scrollableRegionFocusableMatches from '../../rules/scrollable-region-focusable-matches'; import skipLinkMatches from '../../rules/skip-link-matches'; import svgNamespaceMatches from '../../rules/svg-namespace-matches'; @@ -188,6 +188,7 @@ const metadataFunctionMap = { 'aria-valid-attr-value-evaluate': ariaValidAttrValueEvaluate, 'fallbackrole-evaluate': fallbackroleEvaluate, 'has-global-aria-attribute-evaluate': hasGlobalAriaAttributeEvaluate, + 'has-implicit-chromium-role-matches': hasImplicitChromiumRoleMatches, 'has-widget-role-evaluate': hasWidgetRoleEvaluate, 'invalidrole-evaluate': invalidroleEvaluate, 'is-element-focusable-evaluate': isElementFocusableEvaluate, @@ -340,7 +341,6 @@ const metadataFunctionMap = { 'no-role-matches': noRoleMatches, 'not-html-matches': notHtmlMatches, 'p-as-heading-matches': pAsHeadingMatches, - 'presentation-role-conflict-matches': presentationRoleConflictMatches, 'scrollable-region-focusable-matches': scrollableRegionFocusableMatches, 'skip-link-matches': skipLinkMatches, 'svg-namespace-matches': svgNamespaceMatches, diff --git a/lib/rules/has-implicit-chromium-role-matches.js b/lib/rules/has-implicit-chromium-role-matches.js new file mode 100644 index 0000000000..c7b4ce0ec8 --- /dev/null +++ b/lib/rules/has-implicit-chromium-role-matches.js @@ -0,0 +1,7 @@ +import { getImplicitRole } from '../commons/aria'; + +function hasImplicitChromiumRoleMatches(node, virtualNode) { + return getImplicitRole(virtualNode) !== null; +} + +export default hasImplicitChromiumRoleMatches; diff --git a/lib/rules/presentation-role-conflict-matches.js b/lib/rules/presentation-role-conflict-matches.js deleted file mode 100644 index 0b0d4b842b..0000000000 --- a/lib/rules/presentation-role-conflict-matches.js +++ /dev/null @@ -1,7 +0,0 @@ -import { getImplicitRole } from '../commons/aria'; - -function presentationRoleConflictMatches(node, virtualNode) { - return getImplicitRole(virtualNode) !== null; -} - -export default presentationRoleConflictMatches; diff --git a/lib/rules/presentation-role-conflict.json b/lib/rules/presentation-role-conflict.json index 2940780417..059d478a10 100644 --- a/lib/rules/presentation-role-conflict.json +++ b/lib/rules/presentation-role-conflict.json @@ -1,6 +1,6 @@ { "id": "presentation-role-conflict", - "matches": "presentation-role-conflict-matches", + "matches": "has-implicit-chromium-role-matches", "selector": "[role=\"none\"], [role=\"presentation\"]", "tags": ["cat.aria", "best-practice"], "metadata": { diff --git a/test/rule-matches/presentation-role-conflict-matches.js b/test/rule-matches/has-implicit-chromium-role-matches.js similarity index 94% rename from test/rule-matches/presentation-role-conflict-matches.js rename to test/rule-matches/has-implicit-chromium-role-matches.js index 9fd5333d93..04e6a492c5 100644 --- a/test/rule-matches/presentation-role-conflict-matches.js +++ b/test/rule-matches/has-implicit-chromium-role-matches.js @@ -1,4 +1,4 @@ -describe('presentation-role-conflict-matches', function() { +describe('has-implicit-chromium-role-matches', function() { 'use strict'; var rule; From b15be4f274cc65155f2513d4ed16ccba68bd813b Mon Sep 17 00:00:00 2001 From: Cassey Lottman Date: Thu, 27 May 2021 13:44:28 -0500 Subject: [PATCH 11/13] put chromium role on standards/html-elm and use it to get the implicit chromium role --- lib/commons/aria/implicit-role.js | 16 ++-------------- lib/standards/html-elms.js | 28 +++++++++++++++++++--------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/lib/commons/aria/implicit-role.js b/lib/commons/aria/implicit-role.js index d70f142d3e..f7639cc9bd 100644 --- a/lib/commons/aria/implicit-role.js +++ b/lib/commons/aria/implicit-role.js @@ -1,20 +1,8 @@ import implicitHtmlRoles from '../standards/implicit-html-roles'; import { getNodeFromTree } from '../../core/utils'; +import getElementSpec from '../standards/get-element-spec'; import AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-node'; -const implicitChromiumRoles = { - audio: 'Audio', - canvas: 'Canvas', - dl: 'DescriptionList', - embed: 'EmbeddedObject', - iframe: 'Iframe', - label: 'Label', - meter: 'progressbar', - object: 'PluginObject', - svg: 'SVGRoot', - video: 'Video' -}; - /** * Get the implicit role for a given node * @method implicitRole @@ -42,7 +30,7 @@ function implicitRole(node, { includeChromiumRoles } = {}) { const role = implicitHtmlRoles[nodeName]; if (!role && includeChromiumRoles) { - const chromiumRole = implicitChromiumRoles[nodeName]; + const { chromiumRole } = getElementSpec(vNode); return chromiumRole || null; } diff --git a/lib/standards/html-elms.js b/lib/standards/html-elms.js index 4171e3d4b3..c00d7c450c 100644 --- a/lib/standards/html-elms.js +++ b/lib/standards/html-elms.js @@ -93,7 +93,8 @@ const htmlElms = { }, // Note: if the property applies regardless of variants it is // placed at the top level instead of the default variant - allowedRoles: ['application'] + allowedRoles: ['application'], + chromiumRole: 'Audio' }, b: { contentTypes: ['phrasing', 'flow'], @@ -143,7 +144,8 @@ const htmlElms = { }, canvas: { allowedRoles: true, - contentTypes: ['embedded', 'phrasing', 'flow'] + contentTypes: ['embedded', 'phrasing', 'flow'], + chromiumRole: 'Canvas' }, caption: { allowedRoles: false @@ -205,7 +207,8 @@ const htmlElms = { }, dl: { contentTypes: ['flow'], - allowedRoles: ['group', 'list', 'presentation', 'none'] + allowedRoles: ['group', 'list', 'presentation', 'none'], + chromiumRole: 'DescriptionList' }, dt: { allowedRoles: ['listitem'] @@ -216,7 +219,8 @@ const htmlElms = { }, embed: { contentTypes: ['interactive', 'embedded', 'phrasing', 'flow'], - allowedRoles: ['application', 'document', 'img', 'presentation', 'none'] + allowedRoles: ['application', 'document', 'img', 'presentation', 'none'], + chromiumRole: 'EmbeddedObject' }, fieldset: { contentTypes: ['flow'], @@ -320,7 +324,8 @@ const htmlElms = { }, iframe: { contentTypes: ['interactive', 'embedded', 'phrasing', 'flow'], - allowedRoles: ['application', 'document', 'img', 'none', 'presentation'] + allowedRoles: ['application', 'document', 'img', 'none', 'presentation'], + chromiumRole: 'Iframe' }, img: { variant: { @@ -525,7 +530,8 @@ const htmlElms = { }, label: { contentTypes: ['interactive', 'phrasing', 'flow'], - allowedRoles: false + allowedRoles: false, + chromiumRole: 'Label' }, legend: { allowedRoles: false @@ -601,7 +607,8 @@ const htmlElms = { }, meter: { contentTypes: ['phrasing', 'flow'], - allowedRoles: false + allowedRoles: false, + chromiumRole: 'progressbar' }, nav: { contentTypes: ['sectioning', 'flow'], @@ -623,7 +630,8 @@ const htmlElms = { contentTypes: ['embedded', 'phrasing', 'flow'] } }, - allowedRoles: ['application', 'document', 'img'] + allowedRoles: ['application', 'document', 'img'], + chromiumRole: 'PluginObject' }, ol: { contentTypes: ['flow'], @@ -814,6 +822,7 @@ const htmlElms = { svg: { contentTypes: ['embedded', 'phrasing', 'flow'], allowedRoles: ['application', 'document', 'img'], + chromiumRole: 'SVGRoot', namingMethods: ['svgTitleText'] }, sub: { @@ -914,7 +923,8 @@ const htmlElms = { contentTypes: ['embedded', 'phrasing', 'flow'] } }, - allowedRoles: ['application'] + allowedRoles: ['application'], + chromiumRole: 'video' }, wbr: { contentTypes: ['phrasing', 'flow'], From 3b2cb81af8dded9119f23d22b748c030e559f0e8 Mon Sep 17 00:00:00 2001 From: Cassey Lottman Date: Tue, 1 Jun 2021 14:06:18 -0500 Subject: [PATCH 12/13] consolidate function parameters Co-authored-by: Wilco Fiers --- lib/commons/aria/get-role.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/commons/aria/get-role.js b/lib/commons/aria/get-role.js index bf56495156..137d7a8e97 100644 --- a/lib/commons/aria/get-role.js +++ b/lib/commons/aria/get-role.js @@ -82,7 +82,7 @@ function getInheritedRole(vNode, explicitRoleOptions) { return getInheritedRole(vNode.parent, explicitRoleOptions); } -function resolveImplicitRole(vNode, includeChromiumRoles, explicitRoleOptions) { +function resolveImplicitRole(vNode, { includeChromiumRoles, ...explicitRoleOptions }) { const implicitRole = getImplicitRole(vNode, { includeChromiumRoles }); if (!implicitRole) { @@ -120,7 +120,7 @@ function hasConflictResolution(vNode) { */ function resolveRole( node, - { noImplicit, includeChromiumRoles, ...explicitRoleOptions } = {} + { noImplicit, ...roleOptions } = {} ) { const vNode = node instanceof AbstractVirtuaNode ? node : getNodeFromTree(node); @@ -133,7 +133,7 @@ function resolveRole( if (!explicitRole) { return noImplicit ? null - : resolveImplicitRole(vNode, includeChromiumRoles, explicitRoleOptions); + : resolveImplicitRole(vNode, roleOptions); } if (!['presentation', 'none'].includes(explicitRole)) { @@ -145,7 +145,7 @@ function resolveRole( // has been set as the explicit role is not the true role return noImplicit ? null - : resolveImplicitRole(vNode, includeChromiumRoles, explicitRoleOptions); + : resolveImplicitRole(vNode, roleOptions); } // role presentation or none and no conflict resolution From d76b53ecd63566fb8965be29074c6dc219cb3156 Mon Sep 17 00:00:00 2001 From: Cassey Lottman Date: Tue, 1 Jun 2021 14:15:50 -0500 Subject: [PATCH 13/13] rename option to just chromium --- .../aria/aria-prohibited-attr-evaluate.js | 2 +- lib/commons/aria/get-role.js | 23 ++++++++----------- lib/commons/aria/implicit-role.js | 4 ++-- .../has-implicit-chromium-role-matches.js | 2 +- test/commons/aria/implicit-role.js | 2 +- .../has-implicit-chromium-role-matches.js | 5 ++++ 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/checks/aria/aria-prohibited-attr-evaluate.js b/lib/checks/aria/aria-prohibited-attr-evaluate.js index 98957320a9..ff843065fd 100644 --- a/lib/checks/aria/aria-prohibited-attr-evaluate.js +++ b/lib/checks/aria/aria-prohibited-attr-evaluate.js @@ -52,7 +52,7 @@ function ariaProhibitedAttrEvaluate(node, options = {}, virtualNode) { } function listProhibitedAttrs(virtualNode, elementsAllowedAriaLabel) { - const role = getRole(virtualNode, { includeChromiumRoles: true }); + const role = getRole(virtualNode, { chromium: true }); const roleSpec = standards.ariaRoles[role]; if (roleSpec) { return roleSpec.prohibitedAttrs || []; diff --git a/lib/commons/aria/get-role.js b/lib/commons/aria/get-role.js index 137d7a8e97..7f6d6556ae 100644 --- a/lib/commons/aria/get-role.js +++ b/lib/commons/aria/get-role.js @@ -82,8 +82,10 @@ function getInheritedRole(vNode, explicitRoleOptions) { return getInheritedRole(vNode.parent, explicitRoleOptions); } -function resolveImplicitRole(vNode, { includeChromiumRoles, ...explicitRoleOptions }) { - const implicitRole = getImplicitRole(vNode, { includeChromiumRoles }); +function resolveImplicitRole(vNode, { chromium, ...explicitRoleOptions }) { + const implicitRole = getImplicitRole(vNode, { + chromium + }); if (!implicitRole) { return null; @@ -118,22 +120,17 @@ function hasConflictResolution(vNode) { * @returns {string|null} Role or null * @deprecated noImplicit option is deprecated. Use aria.getExplicitRole instead. */ -function resolveRole( - node, - { noImplicit, ...roleOptions } = {} -) { +function resolveRole(node, { noImplicit, ...roleOptions } = {}) { const vNode = node instanceof AbstractVirtuaNode ? node : getNodeFromTree(node); if (vNode.props.nodeType !== 1) { return null; } - const explicitRole = getExplicitRole(vNode, explicitRoleOptions); + const explicitRole = getExplicitRole(vNode, roleOptions); if (!explicitRole) { - return noImplicit - ? null - : resolveImplicitRole(vNode, roleOptions); + return noImplicit ? null : resolveImplicitRole(vNode, roleOptions); } if (!['presentation', 'none'].includes(explicitRole)) { @@ -143,9 +140,7 @@ function resolveRole( if (hasConflictResolution(vNode)) { // return null if there is a conflict resolution but no implicit // has been set as the explicit role is not the true role - return noImplicit - ? null - : resolveImplicitRole(vNode, roleOptions); + return noImplicit ? null : resolveImplicitRole(vNode, roleOptions); } // role presentation or none and no conflict resolution @@ -165,7 +160,7 @@ function resolveRole( * @param {boolean} options.abstracts Allow role to be abstract * @param {boolean} options.dpub Allow role to be any (valid) doc-* roles * @param {boolean} options.noPresentational return null if role is presentation or none - * @param {boolean} options.includeChromiumRoles Include implicit roles from chromium-based browsers in role result + * @param {boolean} options.chromium Include implicit roles from chromium-based browsers in role result * @returns {string|null} Role or null * * @deprecated noImplicit option is deprecated. Use aria.getExplicitRole instead. diff --git a/lib/commons/aria/implicit-role.js b/lib/commons/aria/implicit-role.js index f7639cc9bd..509d772172 100644 --- a/lib/commons/aria/implicit-role.js +++ b/lib/commons/aria/implicit-role.js @@ -11,7 +11,7 @@ import AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-no * @param {HTMLElement|VirtualNode} node The node to test * @return {Mixed} Either the role or `null` if there is none */ -function implicitRole(node, { includeChromiumRoles } = {}) { +function implicitRole(node, { chromium } = {}) { const vNode = node instanceof AbstractVirtuaNode ? node : getNodeFromTree(node); node = vNode.actualNode; @@ -29,7 +29,7 @@ function implicitRole(node, { includeChromiumRoles } = {}) { const nodeName = vNode.props.nodeName; const role = implicitHtmlRoles[nodeName]; - if (!role && includeChromiumRoles) { + if (!role && chromium) { const { chromiumRole } = getElementSpec(vNode); return chromiumRole || null; } diff --git a/lib/rules/has-implicit-chromium-role-matches.js b/lib/rules/has-implicit-chromium-role-matches.js index c7b4ce0ec8..0f5e7e1bf6 100644 --- a/lib/rules/has-implicit-chromium-role-matches.js +++ b/lib/rules/has-implicit-chromium-role-matches.js @@ -1,7 +1,7 @@ import { getImplicitRole } from '../commons/aria'; function hasImplicitChromiumRoleMatches(node, virtualNode) { - return getImplicitRole(virtualNode) !== null; + return getImplicitRole(virtualNode, { chromium: true }) !== null; } export default hasImplicitChromiumRoleMatches; diff --git a/test/commons/aria/implicit-role.js b/test/commons/aria/implicit-role.js index 94f7556f46..fddb4a6848 100644 --- a/test/commons/aria/implicit-role.js +++ b/test/commons/aria/implicit-role.js @@ -43,7 +43,7 @@ describe('aria.implicitRole', function() { fixture.innerHTML = ''; var node = fixture.querySelector('#target'); flatTreeSetup(fixture); - assert.equal(implicitRole(node, { includeChromiumRoles: true }), 'Canvas'); + assert.equal(implicitRole(node, { chromium: true }), 'Canvas'); }); it('should return link for "a[href]"', function() { diff --git a/test/rule-matches/has-implicit-chromium-role-matches.js b/test/rule-matches/has-implicit-chromium-role-matches.js index 04e6a492c5..eedb8d519e 100644 --- a/test/rule-matches/has-implicit-chromium-role-matches.js +++ b/test/rule-matches/has-implicit-chromium-role-matches.js @@ -27,6 +27,11 @@ describe('has-implicit-chromium-role-matches', function() { assert.isFalse(rule.matches(null, vNode)); }); + it('matches elements with an implicit role in chromium', function() { + var vNode = queryFixture(''); + assert.isTrue(rule.matches(null, vNode)); + }); + it('does not match elements with no implicit role even if they are focusable and have an explicit role', function() { var vNode = queryFixture( '
'